Projects
home:rottame:rubygems
rubygem-activeresource-hel
Log In
Username
Password
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
Expand all
Collapse all
Changes of Revision 5
View file
rubygem-activeresource-hel.changes
Changed
@@ -1,4 +1,9 @@ ------------------------------------------------------------------- +Wed Jan 7 08:13:32 UTC 2026 - Angelo Grossini <rottame@intercom.it> + +- update to version 0.7.1 + +------------------------------------------------------------------- Thu Dec 4 16:22:19 UTC 2025 - Angelo Grossini <angelo@intercom.it> - update to version 0.7.0
View file
activeresource-hel-0.7.0.gem/checksums.yaml.gz -> activeresource-hel-0.7.1.gem/checksums.yaml.gz
Changed
@@ -1,7 +1,7 @@ --- SHA256: - metadata.gz: 6264f4d542dcfc60094c147c8433aef59b7e5c0709efca7359dc29214b997a25 - data.tar.gz: 6aa31fab665cfa469daf4c8be56a91b0b346fe11ba26cbd0066607638f15f3cc + metadata.gz: c7446f34ceab3b61a0af608e9b59775c9dc3da6ffc05b6208dbaf4d543754739 + data.tar.gz: 72e407fbfb7b349cc08dfc560d9d9af2e7f05d061f55c41aba331d9f1c69711f SHA512: - metadata.gz: 3756355aaac73e0da6c2832c83819f5c66c5cee16ed21518ddba6a1169cc9dac6e41758b4c3fc575bc07a646c57e498666f969abcfd8266fe25b8c164010cf5b - data.tar.gz: ff8550f4656ef74081ac40f289cc1d3d60be18c5d9c498a3aeb91f8b1a085ff4b543630cf7a97df186b8e93718e59106546e23ab5dc136d747f4b0a537aee024 + metadata.gz: b2d3571acd7050067abe253befe5e678417700e86dc6ab641e28c3070050ed47a42c1537c061f8f0e540f8ca17e105f49687c3f323d7865cd50bc2b22d0a279e + data.tar.gz: 2c79ceb01acc54344acbcd1832f8d274a65108797820a0bcc298e0921a096ef47dd8c60c369b92148ffc6acfe617fd402a06c5a402d6cc163052edc136871447
View file
activeresource-hel-0.7.1.gem/data/.byebug_history
Added
@@ -0,0 +1,28 @@ +q +continue +known_attributes +errors +load_remote_errors(@remote_errors, false) +errors +load_remote_errors(@remote_errors, true) +@remote_errors.response.body +continue +@remote_errors.response.body +continue +@remote_errors.response.body +continue +@remote_errors.response.body +continue +resource.method :load_remote_errors +continue +x = resource.errors +continue +x:name +x = resource.errors +resource.errors +continue +Mock::ResourceWithSchema.remote_schema +continue +attributes +(n) +n
View file
activeresource-hel-0.7.1.gem/data/.devcontainer/devcontainer.json
Added
@@ -0,0 +1,48 @@ +{ + "name": "activeresource-hel", + + // Update the 'dockerComposeFile' list if you have more compose files or use different names. + // The .devcontainer/docker-compose.yml file contains any overrides you need/want to make. + "dockerComposeFile": + "../docker-compose.yml" + , + + // The 'service' property is the name of the service for the container that VS Code should + // use. Update this value and .devcontainer/docker-compose.yml to the real service name. + "service": "activeresource-hel", + + // The optional 'workspaceFolder' property is the path VS Code should open by default when + // connected. This is typically a file mount in .devcontainer/docker-compose.yml + "workspaceFolder": "/activeresource-hel", + + // Features to add to the dev container. More info: https://containers.dev/features. + // "features": {}, + + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // "forwardPorts": , + + // Uncomment the next line if you want start specific services in your Docker Compose config. + // "runServices": , + + // Uncomment the next line if you want to keep your containers running after VS Code shuts down. + // "shutdownAction": "none", + + // Uncomment the next line to run commands after the container is created. + // "postCreateCommand": "cat /etc/os-release", + + // Configure tool-specific properties. + // "customizations": {}, + + // Uncomment to connect as an existing user other than the container default. More info: https://aka.ms/dev-containers-non-root. + "remoteUser": "user", + "customizations": { + "vscode": { + "extensions": + "Shopify.ruby-extensions-pack", + "esbenp.prettier-vscode", + "rvest.vs-code-prettier-eslint", + "KoichiSasada.vscode-rdbg" + + } + } +} \ No newline at end of file
View file
activeresource-hel-0.7.1.gem/data/Dockerfile
Added
@@ -0,0 +1,22 @@ +FROM registry-lab.intercom.it/docker-images/ruby:3.3 AS devel + +RUN zypper -n in tar gzip bzip2 xz git openssh + +RUN useradd -g users -u 1000 -d /home/user -m user +RUN echo "export PS1='\\\\03301;32m\\\\u\\\\03300m\\:\\\\03301;34m\\\\w\\\\03300m\\\\$ '" >> /etc/profile +RUN echo "export PS1='\\\\03301;32m\\\\u\\\\03300m\\:\\\\03301;34m\\\\w\\\\03300m\\\\$ '" >> /etc/bash.bashrc +RUN echo "alias rspec='bundle exec rspec'" >> /etc/profile +RUN echo "alias rspec='bundle exec rspec'" >> /etc/bash.bashrc + +RUN mkdir -p /activeresource-hel +RUN chown -R user:users /activeresource-hel + +USER user + +VOLUME /home/user +VOLUME /activeresource-hel + +ENV GEM_HOME=/home/user/.bundle +ENV PATH=/home/user/.bundle/bin/:/opt/ruby/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin + +WORKDIR /activeresource-hel
View file
activeresource-hel-0.7.0.gem/data/Gemfile -> activeresource-hel-0.7.1.gem/data/Gemfile
Changed
@@ -1,5 +1,8 @@ source 'https://rubygems.org' + gemspec -gem 'rails', '~> 6.0.0' -gem 'her', :github => 'remiprev/her' \ No newline at end of file +#gem 'rails', '~> 6.0.0' +#gem 'her', :github => 'remiprev/her' + +gem 'byebug'
View file
activeresource-hel-0.7.0.gem/data/Gemfile.lock -> activeresource-hel-0.7.1.gem/data/Gemfile.lock
Changed
@@ -1,215 +1,161 @@ -GIT - remote: git://github.com/remiprev/her.git - revision: bfcd4d34565ad556460cad3fa28f28eedaa6b39a - specs: - her (1.1.0) - activemodel (>= 4.2.1) - faraday (>= 0.8, < 1.0) - multi_json (~> 1.7) - PATH remote: . specs: - activeresource-hel (0.4.4.1) - activemodel (~> 6.0.0) - activeresource (>= 5.1.0) - awesome_print - kaminari + activeresource-hel (0.7.0) + activemodel (>= 6.0.0, < 9.0.0) + activeresource (>= 5.1.0, < 7.0.0) + awesome_print (~> 1.8, >= 1.8.0) + kaminari (~> 1.1, >= 1.1.1) GEM remote: https://rubygems.org/ specs: - actioncable (6.0.0) - actionpack (= 6.0.0) - nio4r (~> 2.0) - websocket-driver (>= 0.6.1) - actionmailbox (6.0.0) - actionpack (= 6.0.0) - activejob (= 6.0.0) - activerecord (= 6.0.0) - activestorage (= 6.0.0) - activesupport (= 6.0.0) - mail (>= 2.7.1) - actionmailer (6.0.0) - actionpack (= 6.0.0) - actionview (= 6.0.0) - activejob (= 6.0.0) - mail (~> 2.5, >= 2.5.4) - rails-dom-testing (~> 2.0) - actionpack (6.0.0) - actionview (= 6.0.0) - activesupport (= 6.0.0) - rack (~> 2.0) - rack-test (>= 0.6.3) - rails-dom-testing (~> 2.0) - rails-html-sanitizer (~> 1.0, >= 1.2.0) - actiontext (6.0.0) - actionpack (= 6.0.0) - activerecord (= 6.0.0) - activestorage (= 6.0.0) - activesupport (= 6.0.0) - nokogiri (>= 1.8.5) - actionview (6.0.0) - activesupport (= 6.0.0) + actionview (8.1.1) + activesupport (= 8.1.1) builder (~> 3.1) - erubi (~> 1.4) - rails-dom-testing (~> 2.0) - rails-html-sanitizer (~> 1.1, >= 1.2.0) - activejob (6.0.0) - activesupport (= 6.0.0) - globalid (>= 0.3.6) - activemodel (6.0.0) - activesupport (= 6.0.0) - activemodel-serializers-xml (1.0.2) - activemodel (> 5.x) - activesupport (> 5.x) + erubi (~> 1.11) + rails-dom-testing (~> 2.2) + rails-html-sanitizer (~> 1.6) + activemodel (8.1.1) + activesupport (= 8.1.1) + activemodel-serializers-xml (1.0.3) + activemodel (>= 5.0.0.a) + activesupport (>= 5.0.0.a) builder (~> 3.1) - activerecord (6.0.0) - activemodel (= 6.0.0) - activesupport (= 6.0.0) - activeresource (5.1.0) - activemodel (>= 5.0, < 7) + activerecord (8.1.1) + activemodel (= 8.1.1) + activesupport (= 8.1.1) + timeout (>= 0.4.0) + activeresource (6.2.0) + activemodel (>= 7.0) activemodel-serializers-xml (~> 1.0) - activesupport (>= 5.0, < 7) - activestorage (6.0.0) - actionpack (= 6.0.0) - activejob (= 6.0.0) - activerecord (= 6.0.0) - marcel (~> 0.3.1) - activesupport (6.0.0) - concurrent-ruby (~> 1.0, >= 1.0.2) - i18n (>= 0.7, < 2) - minitest (~> 5.1) - tzinfo (~> 1.1) - zeitwerk (~> 2.1, >= 2.1.8) - addressable (2.7.0) - public_suffix (>= 2.0.2, < 5.0) - awesome_print (1.8.0) - builder (3.2.3) - coderay (1.1.2) - concurrent-ruby (1.1.5) - crack (0.4.3) - safe_yaml (~> 1.0.0) - crass (1.0.4) - diff-lcs (1.3) - erubi (1.8.0) - faraday (0.15.4) - multipart-post (>= 1.2, < 3) - globalid (0.4.2) - activesupport (>= 4.2.0) - hashdiff (1.0.0) - i18n (1.6.0) + activesupport (>= 7.0) + activesupport (8.1.1) + base64 + bigdecimal + concurrent-ruby (~> 1.0, >= 1.3.1) + connection_pool (>= 2.2.5) + drb + i18n (>= 1.6, < 2) + json + logger (>= 1.4.2) + minitest (>= 5.1) + securerandom (>= 0.3) + tzinfo (~> 2.0, >= 2.0.5) + uri (>= 0.13.1) + addressable (2.8.8) + public_suffix (>= 2.0.2, < 8.0) + awesome_print (1.9.2) + base64 (0.3.0) + bigdecimal (4.0.1) + builder (3.3.0) + byebug (12.0.0) + coderay (1.1.3) + concurrent-ruby (1.3.6) + connection_pool (3.0.2) + crack (1.0.1) + bigdecimal + rexml + crass (1.0.6) + diff-lcs (1.6.2) + drb (2.2.3) + erubi (1.13.1) + hashdiff (1.2.1) + i18n (1.14.8) concurrent-ruby (~> 1.0) - kaminari (1.1.1) + json (2.18.0) + kaminari (1.2.2) activesupport (>= 4.1.0) - kaminari-actionview (= 1.1.1) - kaminari-activerecord (= 1.1.1) - kaminari-core (= 1.1.1) - kaminari-actionview (1.1.1) + kaminari-actionview (= 1.2.2) + kaminari-activerecord (= 1.2.2) + kaminari-core (= 1.2.2) + kaminari-actionview (1.2.2) actionview - kaminari-core (= 1.1.1) - kaminari-activerecord (1.1.1) + kaminari-core (= 1.2.2) + kaminari-activerecord (1.2.2) activerecord - kaminari-core (= 1.1.1) - kaminari-core (1.1.1) - loofah (2.2.3) + kaminari-core (= 1.2.2) + kaminari-core (1.2.2) + logger (1.7.0) + loofah (2.25.0) crass (~> 1.0.2) - nokogiri (>= 1.5.9) - mail (2.7.1) - mini_mime (>= 0.1.1) - marcel (0.3.3) - mimemagic (~> 0.3.2) - method_source (0.9.2) - mimemagic (0.3.3) - mini_mime (1.0.2) - mini_portile2 (2.4.0) - minitest (5.11.3) - multi_json (1.13.1) - multipart-post (2.1.1) - nio4r (2.5.1) - nokogiri (1.10.4) - mini_portile2 (~> 2.4.0) - pry (0.12.2) - coderay (~> 1.1.0) - method_source (~> 0.9.0) - public_suffix (4.0.1) - rack (2.0.7) - rack-test (1.1.0) - rack (>= 1.0, < 3) - rails (6.0.0) - actioncable (= 6.0.0) - actionmailbox (= 6.0.0) - actionmailer (= 6.0.0) - actionpack (= 6.0.0) - actiontext (= 6.0.0) - actionview (= 6.0.0) - activejob (= 6.0.0) - activemodel (= 6.0.0) - activerecord (= 6.0.0) - activestorage (= 6.0.0) - activesupport (= 6.0.0) - bundler (>= 1.3.0) - railties (= 6.0.0) - sprockets-rails (>= 2.0.0) - rails-dom-testing (2.0.3) - activesupport (>= 4.2.0) + nokogiri (>= 1.12.0) + method_source (1.1.0) + minitest (6.0.0) + prism (~> 1.5) + nokogiri (1.18.10-aarch64-linux-gnu) + racc (~> 1.4) + nokogiri (1.18.10-aarch64-linux-musl) + racc (~> 1.4) + nokogiri (1.18.10-arm-linux-gnu) + racc (~> 1.4) + nokogiri (1.18.10-arm-linux-musl) + racc (~> 1.4) + nokogiri (1.18.10-arm64-darwin) + racc (~> 1.4) + nokogiri (1.18.10-x86_64-darwin) + racc (~> 1.4) + nokogiri (1.18.10-x86_64-linux-gnu) + racc (~> 1.4) + nokogiri (1.18.10-x86_64-linux-musl) + racc (~> 1.4) + prism (1.7.0) + pry (0.15.2) + coderay (~> 1.1) + method_source (~> 1.0) + public_suffix (7.0.0) + racc (1.8.1) + rails-dom-testing (2.3.0) + activesupport (>= 5.0.0) + minitest nokogiri (>= 1.6) - rails-html-sanitizer (1.2.0) - loofah (~> 2.2, >= 2.2.2) - railties (6.0.0) - actionpack (= 6.0.0) - activesupport (= 6.0.0) - method_source - rake (>= 0.8.7) - thor (>= 0.20.3, < 2.0) - rake (12.3.3) - rspec (3.8.0) - rspec-core (~> 3.8.0) - rspec-expectations (~> 3.8.0) - rspec-mocks (~> 3.8.0) - rspec-core (3.8.2) - rspec-support (~> 3.8.0) - rspec-expectations (3.8.4) + rails-html-sanitizer (1.6.2) + loofah (~> 2.21) + nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0) + rake (13.3.1) + rexml (3.4.4) + rspec (3.13.2) + rspec-core (~> 3.13.0) + rspec-expectations (~> 3.13.0) + rspec-mocks (~> 3.13.0) + rspec-core (3.13.6) + rspec-support (~> 3.13.0) + rspec-expectations (3.13.5) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.8.0) - rspec-mocks (3.8.1) + rspec-support (~> 3.13.0) + rspec-mocks (3.13.7) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.8.0) - rspec-support (3.8.2) - safe_yaml (1.0.5) - sprockets (3.7.2) + rspec-support (~> 3.13.0) + rspec-support (3.13.6) + securerandom (0.4.1) + timecop (0.9.10) + timeout (0.6.0) + tzinfo (2.0.6) concurrent-ruby (~> 1.0) - rack (> 1, < 3) - sprockets-rails (3.2.1) - actionpack (>= 4.0) - activesupport (>= 4.0) - sprockets (>= 3.0.0) - thor (0.20.3) - thread_safe (0.3.6) - timecop (0.9.1) - tzinfo (1.2.5) - thread_safe (~> 0.1) - webmock (3.7.5) - addressable (>= 2.3.6) + uri (1.1.1) + webmock (3.26.1) + addressable (>= 2.8.0) crack (>= 0.3.2) hashdiff (>= 0.4.0, < 2.0.0) - websocket-driver (0.7.1) - websocket-extensions (>= 0.1.0) - websocket-extensions (0.1.4) - zeitwerk (2.1.10) PLATFORMS - ruby + aarch64-linux-gnu + aarch64-linux-musl + arm-linux-gnu + arm-linux-musl + arm64-darwin + x86_64-darwin + x86_64-linux-gnu + x86_64-linux-musl DEPENDENCIES activeresource-hel! - her! - pry - rails (~> 6.0.0) - rake - rspec - timecop - webmock + byebug + pry (~> 0.12, >= 0.12.2) + rake (~> 13.0, >= 13.3.1) + rspec (~> 3.0, >= 3.13.0) + timecop (~> 0.9, >= 0.9.1) + webmock (~> 3.7, >= 3.7.5) BUNDLED WITH - 1.17.3 + 2.5.22
View file
activeresource-hel-0.7.0.gem/data/activeresource-hel.gemspec -> activeresource-hel-0.7.1.gem/data/activeresource-hel.gemspec
Changed
@@ -16,12 +16,12 @@ s.require_paths = "lib" s.add_dependency "activeresource", ">= 5.1.0", '< 7.0.0' - s.add_dependency "activemodel", ">= 6.0.0", '< 8.0.0' + s.add_dependency "activemodel", ">= 6.0.0", '< 9.0.0' s.add_dependency "kaminari", "~> 1.1", ">= 1.1.1" s.add_dependency "awesome_print", "~>1.8", ">= 1.8.0" - s.add_development_dependency "rspec", "~> 2.10", ">= 2.10.0" - s.add_development_dependency "rake", "~> 12.3", ">= 12.3.3" + s.add_development_dependency "rspec", "~> 3.0", ">= 3.13.0" + s.add_development_dependency "rake", "~> 13.0", ">= 13.3.1" s.add_development_dependency "pry", "~> 0.12", ">= 0.12.2" s.add_development_dependency "webmock", "~> 3.7", ">= 3.7.5" s.add_development_dependency "timecop", "~> 0.9", ">= 0.9.1"
View file
activeresource-hel-0.7.1.gem/data/docker-compose.yml
Added
@@ -0,0 +1,13 @@ +services: + activeresource-hel: + image: devel/arh + build: + dockerfile: Dockerfile + context: . + volumes: + - home:/home/user + - ./:/activeresource-hel + command: sleep infinity + +volumes: + home:
View file
activeresource-hel-0.7.0.gem/data/lib/activeresource/hel.rb -> activeresource-hel-0.7.1.gem/data/lib/activeresource/hel.rb
Changed
@@ -10,7 +10,25 @@ end require 'activeresource/hel/compat/formats' +# Main module for ActiveResource::Hel extension +# +# This module provides enhanced functionality for ActiveResource including: +# - Hel-style authentication +# - Caching support +# - Dirty tracking +# - Scopes and filters +# - Remote schema loading +# - Embedded resources and collections +# +# @example Basic usage +# class User < ActiveResource::Hel::Resource +# self.site = 'https://api.example.com' +# self.hel_identity = 'my_identity' +# self.hel_secret = 'my_secret' +# end +# module ActiveResource + # Hel extension module for ActiveResource module Hel autoload :Connection, 'activeresource/hel/connection' @@ -46,20 +64,41 @@ end +# Ygg module for identity and session management +# +# Provides classes for managing user sessions and identities in the Ygg system. +# module Ygg + # Session management class autoload :Session, 'ygg/session' + # Core identity management classes module Core + # Identity resource class autoload :Identity, 'ygg/core/identity' + # Person resource class autoload :Person, 'ygg/core/person' end end require 'active_resource/validations' module ActiveResource + # Extended Errors class for ActiveResource + # + # Monkey-patches validation to look into attributes.keys instead of known_attributes + # since we are using active_model/dirty we are not sending all the attributes class Errors < ActiveModel::Errors - # monkey-patch validation looking into attributes.keys instead of known_attributes - # since we are using active_model/dirty we are not sending all the attributes + # Loads error messages from a hash + # + # This method overrides the default behavior to handle errors for attributes + # that may not be in the known_attributes list due to dirty tracking. + # + # @param Hash messages Hash of error messages keyed by attribute name + # @param Boolean save_cache Whether to preserve existing errors (default: false) + # @return void + # + # @example + # errors.from_hash({'name' => 'cannot be blank', 'base' => 'general error'}) def from_hash(messages, save_cache = false) clear unless save_cache messages.each do |(key,errors)|
View file
activeresource-hel-0.7.0.gem/data/lib/activeresource/hel/cached.rb -> activeresource-hel-0.7.1.gem/data/lib/activeresource/hel/cached.rb
Changed
@@ -1,6 +1,11 @@ require 'active_support/core_ext/module/delegation' module ActiveResource::Hel + # Module for adding caching capabilities to resources + # + # Provides HTTP caching support with ETag and Last-Modified headers, + # cache versioning, and cache invalidation on resource changes. + # module Cached extend ActiveSupport::Concern @@ -20,7 +25,7 @@ def find(*arguments) scope = arguments.slice!(0) options = arguments.slice!(0) || {} - + revalidate = options.delete(:revalidate) if revalidate case scope @@ -28,7 +33,7 @@ force_revalidate_every(options.dup) else force_revalidate_single(scope, options.dup) - end + end end super(scope, options) @@ -56,6 +61,10 @@ end module ClassMethods + # Gets whether caching is enabled for this resource class + # + # @return Boolean true if caching is enabled + # @see cached= def cached if _cached_defined? _cached @@ -64,23 +73,41 @@ end end + # Sets whether caching is enabled for this resource class + # + # @param Boolean cached Whether to enable caching + # @return void + # @see cached def cached=(cached) self._connection = nil self._cached = cached end + # Forces revalidation of all cached resources matching the options + # + # @param Hash options Find options + # @return void def force_revalidate_every(options) prefix_options, query_options = split_options(options:params) key = collection_path(prefix_options) - ActiveResource::Hel::Resource.cache.write("#{key}-cache-version", Time.new.to_i) + ActiveResource::Hel::Resource.cache.write("#{key}-cache-version", Time.new.to_i) end + # Forces revalidation of a single cached resource + # + # @param Integer, String scope Resource ID + # @param Hash options Find options + # @return void def force_revalidate_single(scope, options) prefix_options, query_options = split_options(options:params) key = element_path(scope, prefix_options) - ActiveResource::Hel::Resource.cache.write("#{key}-cache-version", Time.new.to_i) + ActiveResource::Hel::Resource.cache.write("#{key}-cache-version", Time.new.to_i) end + # Temporarily disables caching for a block + # + # @yield Block to execute without caching + # @return Object Result of block execution def without_cache(&block) backup = self.cached self.cached = nil @@ -89,16 +116,25 @@ self.cached = backup end + # Gets the cache version key for this resource class + # + # @return String Cache version key def cache_version_key - collection_path + collection_path end + # Increments the cache version to invalidate cached responses + # + # @return void def increase_request_version ver = ActiveResource::Hel::Resource.cache.read("#{self.cache_version_key}-cache-version") || 0 ActiveResource::Hel::Resource.cache.write("#{self.cache_version_key}-cache-version", ver + 1) end end + # Reloads the resource from the server, bypassing cache + # + # @return ActiveResource::Hel::Resource self def reload self.load(self.class.find(to_param, revalidate: true, params: @prefix_options).attributes, false, true) end @@ -106,6 +142,9 @@ protected + # Increments the cache version for this resource instance + # + # @return void def increase_request_version self.class.increase_request_version end
View file
activeresource-hel-0.7.0.gem/data/lib/activeresource/hel/collection.rb -> activeresource-hel-0.7.1.gem/data/lib/activeresource/hel/collection.rb
Changed
@@ -4,6 +4,17 @@ module ActiveResource::Hel + # Enhanced collection class with pagination support + # + # Extends ActiveResource::Collection with Kaminari pagination support and + # additional metadata tracking (total_count, offset, limit). + # + # @example + # users = User.find(:all) + # users.total_count # => 100 + # users.offset_value # => 0 + # users.limit_value # => 25 + # class Collection < ActiveResource::Collection include Kaminari::ConfigurationMethods::ClassMethods include Kaminari::PageScopeMethods @@ -13,6 +24,16 @@ attr_reader :parent, :attribute delegate :empty?, :to => :to_a + # Initializes a new Collection + # + # @param Array original_collection Original collection array (default: ) + # @param Hash options Initialization options + # @option options Integer :limit Number of items per page + # @option options Integer :offset Offset for pagination + # @option options Integer :total_count Total number of items + # @option options Class :model Model class for collection items + # @option options Object :parent Parent resource object + # @option options Symbol, String :attribute Attribute name on parent def initialize(original_collection = , options = {}) @_original_collection, @_limit_value, @_offset_value, @_total_count = original_collection, (options:limit || default_per_page).to_i, options:offset.to_i, options:total_count @@ -27,6 +48,7 @@ end end + # why? who knows if options:total_count super original_collection.to_a else @@ -35,51 +57,85 @@ end + # Gets the model class for this collection + # + # @return Class, nil Model class or nil if not determinable def model @model ||= resource_class || to_a.first.try { |m| m.class } end - # items at the specified "page" + # Gets items at the specified page number + # + # Dynamically defined method name based on Kaminari configuration. + # + # @param Integer num Page number (default: 1) + # @return ActiveResource::Hel::Collection Collection for the specified page class_eval <<-RUBY, __FILE__, __LINE__ + 1 def #{Kaminari.config.page_method_name}(num = 1) offset(limit_value * (num.to_i, 1.max - 1)) end RUBY + # Iterates over all items including destroyed ones + # + # @return Enumerator Enumerator for all items def each_with_destroyed to_a.each end + # Gets the human-readable entry name for this collection + # + # @return String Humanized model name or 'entry' def entry_name (model.model_name.try(:human) || 'entry').downcase end + # Maps over the collection, preserving metadata + # + # @yield Object Block to map over each item + # @return ActiveResource::Hel::Collection New collection with mapped items def map self.class.new super, :model => model, :attribute => attribute, :parent => parent, :limit => @_limit_value, :offset => @_offset_value, :total_count => @_total_count end - # returns another chunk of the original collection + # Returns a new collection with a different limit + # + # @param Integer num Number of items per page + # @return ActiveResource::Hel::Collection New collection with updated limit def limit(num) self.class.new @_original_collection, :limit => num, :offset => @_offset_value, :total_count => @_total_count, :model => model end - # total item numbers of the original collection + # Gets the total number of items in the original collection + # + # @return Integer Total count def total_count @_total_count || @_original_collection.count end - # returns another chunk of the original collection + # Returns a new collection with a different offset + # + # @param Integer num Offset value + # @return ActiveResource::Hel::Collection New collection with updated offset def offset(num) self.class.new @_original_collection, :limit => @_limit_value, :offset => num, :total_count => @_total_count, :model => model end + # Creates a paginated collection from an array or collection + # + # @param Array, ActiveResource::Collection array_or_collection Source collection + # @param Hash options Pagination options + # @return ActiveResource::Hel::Collection Paginated collection def self.paginate_collection(array_or_collection, options = {}) new array_or_collection, options end # private + # Marks the parent resource as dirty when collection changes + # + # @return void def mark_dirty unless parent.blank? || attribute.blank? parent.set_attribute(attribute, self) @@ -87,4 +143,4 @@ end end -end \ No newline at end of file +end
View file
activeresource-hel-0.7.0.gem/data/lib/activeresource/hel/compat/formats.rb -> activeresource-hel-0.7.1.gem/data/lib/activeresource/hel/compat/formats.rb
Changed
@@ -1,16 +1,27 @@ module ActiveResource + # Format compatibility modules # - # default decode() removes root when there's a single element, - # we don't want that for custom methods. + # Provides plain_decode methods that don't remove root elements, + # unlike the default decode() method. # module Formats + # XML format module with plain decode support module XmlFormat + # Decodes XML without removing root element + # + # @param String xml XML string to decode + # @return Hash Decoded hash def plain_decode(xml) Hash.from_xml(xml) end end + # JSON format module with plain decode support module JsonFormat + # Decodes JSON without removing root element + # + # @param String json JSON string to decode + # @return Hash Decoded hash def plain_decode(json) ActiveSupport::JSON.decode(json) end
View file
activeresource-hel-0.7.0.gem/data/lib/activeresource/hel/connection.rb -> activeresource-hel-0.7.1.gem/data/lib/activeresource/hel/connection.rb
Changed
@@ -1,16 +1,35 @@ module ActiveResource::Hel + # Default HTTP headers for Hel connections HEADERS = { 'User-Agent' => "ActiveResource::Hel #{ActiveResource::Hel::VERSION}", 'X-REST-Client' => "ActiveResource::Hel #{ActiveResource::Hel::VERSION}", 'Accept' => 'application/json' } + # Enhanced connection class with Hel authentication and caching + # + # Extends ActiveResource::Connection with: + # - Hel-style authentication support + # - HTTP caching capabilities + # - Optional debugging support + # class Connection < ActiveResource::Connection include ActiveResource::Hel::Extensions::Cache include ActiveResource::Hel::Extensions::Authentication + # @!attribute rw hel_identity + # @return String, nil Hel identity for authentication attr_accessor :hel_identity, :hel_secret + # @!attribute rw hel_secret + # @return String, nil Hel secret for authentication + + # Initializes a new Connection + # + # Sets up default headers and optionally includes debugging support. + # + # @param Array *args Connection arguments + # @param Hash **kwargs Connection keyword arguments def initialize(*args, **kwargs) ActiveResource::Connection.send(:include, ActiveResource::Hel::Extensions::Debugging) if ENV'ARES_HEL_DEBUG' super
View file
activeresource-hel-0.7.0.gem/data/lib/activeresource/hel/default_scopes.rb -> activeresource-hel-0.7.1.gem/data/lib/activeresource/hel/default_scopes.rb
Changed
@@ -1,4 +1,8 @@ module ActiveResource::Hel + # Module providing default scopes for resources + # + # Defines commonly used scopes like :unscoped, :conditions, :filter, :limit, etc. + # module DefaultScopes extend ActiveSupport::Concern @@ -45,4 +49,4 @@ } end end -end \ No newline at end of file +end
View file
activeresource-hel-0.7.0.gem/data/lib/activeresource/hel/dirty.rb -> activeresource-hel-0.7.1.gem/data/lib/activeresource/hel/dirty.rb
Changed
@@ -123,6 +123,20 @@ # person.name_change # => "Bill", "Bill" # person.name << 'y' # person.name_change # => "Bill", "Billy" + # Module for tracking attribute changes (Dirty tracking) + # + # Provides ActiveModel::Dirty functionality compatible with ActiveResource. + # This is a Rails 5.1-compatible implementation since ActiveResource::Base + # is not compatible with Rails 5.2+ Dirty tracking mechanism. + # + # @example + # user = User.find(1) + # user.name = 'John' + # user.changed? # => true + # user.name_changed? # => true + # user.name_was # => 'Jane' + # user.changes # => {'name' => 'Jane', 'John'} + # module Dirty extend ActiveSupport::Concern include ActiveModel::AttributeMethods @@ -136,8 +150,11 @@ attribute_method_affix prefix: "restore_", suffix: "!" end - # Returns +true+ if any of the attributes have unsaved changes, +false+ otherwise. + # Checks if any attributes have unsaved changes # + # @return Boolean true if any attributes have changed + # + # @example # person.changed? # => false # person.name = 'bob' # person.changed? # => true @@ -145,8 +162,11 @@ changed_attributes.present? end - # Returns an array with the name of the attributes with unsaved changes. + # Gets an array of attribute names that have changed + # + # @return Array<String> Array of changed attribute names # + # @example # person.changed # => # person.name = 'bob' # person.changed # => "name" @@ -154,9 +174,11 @@ changed_attributes.keys end - # Returns a hash of changed attributes indicating their original - # and new values like <tt>attr => original value, new value</tt>. + # Gets a hash of changed attributes with original and new values + # + # @return HashWithIndifferentAccess Hash of changes: attr => old_value, new_value # + # @example # person.changes # => {} # person.name = 'bob' # person.changes # => { "name" => "bill", "bob" } @@ -164,8 +186,11 @@ ActiveSupport::HashWithIndifferentAccesschanged.map { |attr| attr, attribute_change(attr) } end - # Returns a hash of attributes that were changed before the model was saved. + # Gets a hash of attributes that were changed before the last save # + # @return HashWithIndifferentAccess Hash of previous changes + # + # @example # person.name # => "bob" # person.name = 'robert' # person.save @@ -174,9 +199,11 @@ @previously_changed ||= ActiveSupport::HashWithIndifferentAccess.new end - # Returns a hash of the attributes with unsaved changes indicating their original - # values like <tt>attr => original value</tt>. + # Gets a hash of changed attributes with their original values + # + # @return HashWithIndifferentAccess Hash of original values: attr => old_value # + # @example # person.name # => "bob" # person.name = 'robert' # person.changed_attributes # => {"name" => "bob"} @@ -184,24 +211,38 @@ @changed_attributes ||= ActiveSupport::HashWithIndifferentAccess.new end - # Handles <tt>*_changed?</tt> for +method_missing+. + # Checks if an attribute has changed + # + # @param String, Symbol attr Attribute name + # @param Object from Optional original value to check + # @param Object to Optional new value to check + # @return Boolean true if attribute changed and matches optional conditions def attribute_changed?(attr, from: OPTION_NOT_GIVEN, to: OPTION_NOT_GIVEN) # :nodoc: !!changes_include?(attr) && (to == OPTION_NOT_GIVEN || to == __send__(attr)) && (from == OPTION_NOT_GIVEN || from == changed_attributesattr) end - # Handles <tt>*_was</tt> for +method_missing+. + # Gets the original value of a changed attribute + # + # @param String, Symbol attr Attribute name + # @return Object Original value if changed, current value otherwise def attribute_was(attr) # :nodoc: attribute_changed?(attr) ? changed_attributesattr : __send__(attr) end - # Handles <tt>*_previously_changed?</tt> for +method_missing+. + # Checks if an attribute was changed before the last save + # + # @param String, Symbol attr Attribute name + # @return Boolean true if attribute was previously changed def attribute_previously_changed?(attr) #:nodoc: previous_changes_include?(attr) end - # Restore all previous data of the provided attributes. + # Restores previous values for the specified attributes + # + # @param Array<String, Symbol> attributes Attribute names to restore (default: all changed) + # @return void def restore_attributes(attributes = changed) attributes.each { |attr| restore_attribute! attr } end
View file
activeresource-hel-0.7.0.gem/data/lib/activeresource/hel/embedded_collection.rb -> activeresource-hel-0.7.1.gem/data/lib/activeresource/hel/embedded_collection.rb
Changed
@@ -1,9 +1,20 @@ module ActiveResource::Hel + # Collection class for embedded resources with destroy tracking + # + # Extends Collection to support tracking of destroyed items within embedded collections. + # Used for nested attributes that can be marked for destruction. + # class EmbeddedCollection < Collection + # @!attribute r destroyed + # @return Array<Integer, String> Array of IDs of destroyed items attr_reader :destroyed + # Initializes a new EmbeddedCollection + # + # @param Array original_collection Original collection array (default: ) + # @param Hash options Initialization options (see Collection#initialize) def initialize(original_collection = , options = {}) super @destroyed = @@ -17,6 +28,11 @@ # @elements.select { |z| !z.destroyed? }.each #end + # Adds an object to the collection + # + # @param Object obj Object to add (must be instance of model class) + # @return ActiveResource::Hel::EmbeddedCollection self + # @raise ArgumentError if object is not of the correct model class def push(obj) raise ArgumentError, "Object is not of class #{model}" unless obj.is_a?(model) @@ -25,10 +41,18 @@ end alias_method :<<, :push + # Finds an item by ID + # + # @param Integer, String id Item ID + # @return Object, nil Found item or nil def find(id) @elements.find { |z| z.id.to_s == id.to_s} end + # Marks an item for destruction + # + # @param Integer, String id Item ID to destroy + # @return void def destroy(id) element = find(id) if element && !element.destroyed? @@ -38,17 +62,27 @@ end end + # Clears all embedded changes and destroyed items + # + # @return void def clear_embedded_changes @destroyed = changed_items.each {|i| i.changed_attributes.clear } end + # Gets all items that have been changed + # + # @return Array Array of changed items def changed_items @elements.select { |e| e.changed? } end private + # Marks an item ID as destroyed + # + # @param Integer, String id Item ID + # @return void def mark_destroyed(id) mark_dirty @destroyed << id
View file
activeresource-hel-0.7.0.gem/data/lib/activeresource/hel/embedded_resource.rb -> activeresource-hel-0.7.1.gem/data/lib/activeresource/hel/embedded_resource.rb
Changed
@@ -1,27 +1,50 @@ module ActiveResource::Hel + # Resource class for embedded resources + # + # Extends Resource to support embedded resources that can be marked for destruction + # and are part of nested attributes. + # class EmbeddedResource < Resource - + # Initializes a new EmbeddedResource + # + # @param Hash attributes Resource attributes (default: {}) + # @param Boolean persisted Whether the resource is persisted (default: false) def initialize(attributes = {}, persisted = false) super @_destroyed = false @_parent = nil end + # Marks the resource for destruction + # + # @return ActiveResource::Hel::EmbeddedResource self def destroy @_destroyed = true self end + # Checks if the resource is marked for destruction + # + # @return Boolean true if marked for destruction def destroyed? @_destroyed end + # Updates a single attribute + # + # @param String, Symbol name Attribute name + # @param Object value Attribute value + # @return Object The set value def update_attribute(name, value) set_attribute(name, value) end alias_method :update_attribute, :assign_attribute + # Updates multiple attributes + # + # @param Hash attributes Hash of attribute names and values + # @return ActiveResource::Hel::EmbeddedResource self def update_attributes(attributes) attributes.each do |name,value| update_attribute name, value @@ -30,4 +53,4 @@ alias_method :update_attributes, :assign_attributes end -end \ No newline at end of file +end
View file
activeresource-hel-0.7.0.gem/data/lib/activeresource/hel/extensions/authentication.rb -> activeresource-hel-0.7.1.gem/data/lib/activeresource/hel/extensions/authentication.rb
Changed
@@ -1,8 +1,9 @@ module ActiveResource::Hel::Extensions + # Module for Hel-style authentication support # - # Module that gets mixed in the Resource Connection to handle Hel style - # authentication + # Gets mixed into the Resource Connection to handle Hel-style authentication + # with session management and token-based authentication. # module Authentication extend ActiveSupport::Concern @@ -55,53 +56,74 @@ end end + # Executes a block with authentication + # + # @yield Hash Block receives authentication headers + # @return Object Result of block execution def with_authentication(&block) session.with_authentication do |headers| yield headers end end + # Authenticates the current session + # + # @return Boolean true if authentication succeeded + # @raise ActiveResource::Hel::Session::Invalid if authentication fails def authenticate! session.authenticate! end + # Checks if the current session is authenticated + # + # @return Boolean true if authenticated def check! res = get ActiveResource::Hel::Session::SESSION_CHECK_METHOD ActiveSupport::JSON.decode(res.body)'authenticated' end + # Gets the current connection authentication session # - # returns the current connection authentication context - # + # @return ActiveResource::Hel::Session Current session def session self.current_session ||= new_session(secret: hel_secret, identity: hel_identity) end + # Creates a new authentication session # - # returns a new authentication session for the given params - # - # mysession = connection.new_session(:identity => "foo", :secret => "bar") - # - # mysession = connection.new_session(:token => 'aa1e0c79df691159c06ce59b76e3972a') + # @param Hash params Session parameters + # @option params String :identity User identity/FQDA + # @option params String :secret User password/secret + # @option params String :token Existing authentication token + # @return ActiveResource::Hel::Session New session instance # + # @example + # mysession = connection.new_session(identity: "foo", secret: "bar") + # mysession = connection.new_session(token: 'aa1e0c79df691159c06ce59b76e3972a') def new_session(params = {}) params = params.merge(:site => site) params = params.merge(:ssl_options => ssl_options) ActiveResource::Hel::Session.new(params) end + # Executes a block within a specific session context # - # executes the block in the context of an explicit session + # Temporarily switches to the provided session, executes the block, then restores + # the previous session. # - # mysession = connection.new_session(:user => "admin", :pass => "s3kr337") - # connection.with_session mysession do - # User.get(23) - # end + # @param ActiveResource::Hel::Session, Hash, Ygg::Session session Session object or hash + # @yield Block to execute with the session + # @return Object Result of block execution # - # connection.with_session :token => 'aa1e0c79df691159c06ce59b76e3972a' do - # Identity.find(...) - # end + # @example + # mysession = connection.new_session(user: "admin", secret: "s3kr337") + # connection.with_session mysession do + # User.get(23) + # end # + # connection.with_session token: 'aa1e0c79df691159c06ce59b76e3972a' do + # Identity.find(...) + # end def with_session(session, &block) if session.is_a?(Hash) session = new_session(session) @@ -119,10 +141,17 @@ end + # Gets the current thread-local session + # + # @return ActiveResource::Hel::Session, nil Current session or nil def current_session Thread.current:ar_session end + # Sets the current thread-local session + # + # @param ActiveResource::Hel::Session, nil session Session to set + # @return void def current_session=(session) Thread.current:ar_session = session end
View file
activeresource-hel-0.7.0.gem/data/lib/activeresource/hel/extensions/cache.rb -> activeresource-hel-0.7.1.gem/data/lib/activeresource/hel/extensions/cache.rb
Changed
@@ -1,10 +1,14 @@ +require 'active_support/core_ext' + module ActiveResource::Hel::Extensions + # Module for HTTP caching support # - # Module that gets mixed in the Resource Connection to handle Hel style - # authentication + # Gets mixed into the Resource Connection to handle HTTP caching with + # ETag and Last-Modified headers, cache versioning, and conditional requests. # module Cache + # Exception raised when a cached response is still valid (304 Not Modified) class NotModified < Exception end @@ -55,18 +59,29 @@ else logger.info "CACHE #{path}" if logger end - response + response rescue NotModified cache_touch(path, headers, cached_data) cached_data:response end + # Reads cached response data + # + # @param String path Request path + # @param Hash headers Request headers + # @return Hash, nil Cached data or nil def cache_read(path, headers) data = ActiveResource::Hel::Resource.cache.read(cache_key(path, headers)) logger.debug "+++ Read response from cache: etag: #{data:etag} last-modified: #{data:last_modified}, ttl: #{data:ttl}" if logger && data data end + # Writes response data to cache + # + # @param String path Request path + # @param Hash req_headers Request headers + # @param Net::HTTPResponse response HTTP response + # @return void def cache_write(path, req_headers, response) etag, last_modified, can_cache, ttl, must_revalidate = parse_cache_headers(response) if can_cache @@ -83,12 +98,22 @@ end end + # Updates the cached_at timestamp for cached data + # + # @param String path Request path + # @param Hash req_headers Request headers + # @param Hash data Cached data hash + # @return void def cache_touch(path, req_headers, data) data:cached_at = Time.new logger.debug "+++ Touching response cache" if logger ActiveResource::Hel::Resource.cache.write(cache_key(path, req_headers), data) end + # Parses cache-related headers from HTTP response + # + # @param Net::HTTPResponse response HTTP response + # @return Array Array containing etag, last_modified, can_cache, ttl, must_revalidate def parse_cache_headers(response) etag = response'etag' last_mod = response'last_modified' @@ -102,6 +127,11 @@ etag, last_mod, can_cache, ttl end + # Checks if cached data is stale + # + # @param String path Request path + # @param Hash, nil data Cached data hash + # @return Boolean true if cache is stale def stale?(path, data) data ||= {} stale = false @@ -111,20 +141,33 @@ stale end + # Builds conditional request headers from cached data + # + # @param Hash data Cached data hash + # @return Hash Headers hash with If-None-Match and/or If-Modified-Since def headers_from_cache(data) data ||= {} headers = {} if data:etag || data:last_modified - headers'If-None-Match' = data:etag if data:etag.present? + headers'If-None-Match' = data:etag if data:etag.present? headers'If-Modified-Since' = data:last_modified.utc.strftime('%a, %d %b %Y %T GMT') if data:last_modified.present? end headers end + # Generates a cache key for the request + # + # @param String path Request path + # @param Hash headers Request headers + # @return String MD5 hash cache key def cache_key(path, headers) Digest::MD5.hexdigest("#{path}|#{format.mime_type}|#{headers'Authorization'}") end + # Gets the cache version for a path + # + # @param String path Request path + # @return Integer Cache version number def cache_version(path) path = path.gsub(/\?.*/, '').split('/') keys = @@ -133,7 +176,7 @@ keys << ActiveResource::Hel::Resource.cache.read("#{memo.join('/')}-cache-version") memo end - keys.compact.max || 0 + keys.compact.max || 0 end end end
View file
activeresource-hel-0.7.0.gem/data/lib/activeresource/hel/extensions/debugging.rb -> activeresource-hel-0.7.1.gem/data/lib/activeresource/hel/extensions/debugging.rb
Changed
@@ -1,7 +1,8 @@ module ActiveResource::Hel::Extensions + # Module for HTTP request debugging # - # Module that gets mixed in the Resource Connection to provide debugging - # of http requests + # Gets mixed into the Resource Connection to provide debugging output + # for HTTP requests. Enabled when ENV'ARES_HEL_DEBUG' is set. # module Debugging extend ActiveSupport::Concern @@ -12,6 +13,10 @@ end module PrependMethods + # Configures HTTP connection with debug output + # + # @param Net::HTTP http HTTP connection object + # @return Net::HTTP Configured HTTP connection def configure_http(http) super(http).tap do |h| h.set_debug_output $stderr
View file
activeresource-hel-0.7.0.gem/data/lib/activeresource/hel/filters.rb -> activeresource-hel-0.7.1.gem/data/lib/activeresource/hel/filters.rb
Changed
@@ -1,4 +1,19 @@ module ActiveResource::Hel + # Module for building complex query filters + # + # Provides a DSL for building complex filter queries with operators like + # eq, gt, lt, like, in, etc., and logical operators (AND, OR). + # + # @example + # filter = User.build_filter do + # eq(status: 'active') + # self.or { + # gt(age: 18) + # lt(age: 65) + # } + # end + # User.find(:all, params: {filter: filter}) + # module Filters extend ActiveSupport::Concern @@ -20,6 +35,20 @@ module ClassMethods + # Builds a filter query using the DSL + # + # Creates a filter builder and executes the provided block to build the query. + # + # @param Proc proc Optional proc to use as filter definition + # @yield Block to build the filter query + # @return ActiveResource::Hel::Filters::DSL Filter DSL instance + # + # @example + # filter = build_filter do + # eq(field1: 'value1') + # gt(field2: 10) + # end + # # Build yggdra query hash # example: # build_query do @@ -82,6 +111,10 @@ end end + # DSL class for building filter queries + # + # Provides methods for building complex filter queries with various operators. + # class DSL OPERATORS = { eq: '=', @@ -98,24 +131,41 @@ not_regexp: 'NOT REGEXP', } + # Initializes a new DSL instance + # + # @param Class default_join Default join class (AndQuery or OrQuery) def initialize(default_join = AndQuery) @default_join = default_join @filter = nil end + # Builds the filter by executing the block + # + # @yield Block containing filter DSL calls + # @return ActiveResource::Hel::Filters::DSL self def build(&block) self.instance_exec(&block) if block_given? self end + # Converts the filter to a JSON-compatible hash + # + # @return Hash Filter hash def as_json @filter.as_json end + # Converts the filter to JSON string + # + # @return String JSON string def to_json self.as_json.to_json end + # Creates an OR block for combining conditions + # + # @yield Block containing filter conditions to be OR'd together + # @return ActiveResource::Hel::Filters::DSL self def or(&block) q = DSL.new(OrQuery).instance_eval &block q = OrQuery.new(@filter, q) if @filter @@ -123,6 +173,10 @@ self end + # Creates an AND block for combining conditions + # + # @yield Block containing filter conditions to be AND'd together + # @return ActiveResource::Hel::Filters::DSL self def and(&block) q = DSL.new(AndQuery).instance_eval &block q = AndQuery.new(@filter, q) if @filter @@ -130,6 +184,10 @@ self end + # Adds IS NULL conditions + # + # @param Array<String, Symbol> conds Array of field names + # @return ActiveResource::Hel::Filters::DSL self def is_null(conds) conds = conds.map do |k| IsNullQuery.new(k) @@ -144,6 +202,10 @@ self end + # Adds IS NOT NULL conditions + # + # @param Array<String, Symbol> conds Array of field names + # @return ActiveResource::Hel::Filters::DSL self def is_not_null(conds) conds = conds.map do |k| IsNotNullQuery.new(k) @@ -158,6 +220,13 @@ self end + # Handles dynamic operator method calls (eq, gt, lt, etc.) + # + # @param Symbol m Method name (operator name) + # @param Array args Method arguments + # @param Proc block Optional block + # @return ActiveResource::Hel::Filters::DSL self + # @raise NoMethodError if operator is not recognized def method_missing(m, *args, &block) if OPERATORSm.present? is_op(OPERATORSm, args.first) @@ -168,6 +237,11 @@ protected + # Processes an operator with conditions + # + # @param String op Operator string (=, >, <, etc.) + # @param Hash conds Conditions hash (field => value) + # @return ActiveResource::Hel::Filters::DSL self def is_op(op, conds) conds = conds.map do |k, v| OperatorQuery.new(op, k,v) @@ -182,40 +256,60 @@ self end + # Base class for query elements class QueryElement class_attribute :operator self.operator = nil + # Initializes a new QueryElement + # + # @param Object a Left operand + # @param Object b Right operand def initialize(a, b) @a = a @b = b end + # Converts to JSON-compatible hash + # + # @return Hash Query hash def as_json {a: {field: @a}, o: operator, b: @b} end end + # Query element for AND operations class AndQuery < QueryElement self.operator = 'AND' def as_json {a: @a.try(:as_json) || @a, o: operator, b: @b.try(:as_json) || @b} end end + # Query element for OR operations class OrQuery < QueryElement self.operator = 'OR' def as_json {a: @a.try(:as_json) || @a, o: operator, b: @b.try(:as_json) || @b} end end + # Query element for operator queries (=, >, <, etc.) class OperatorQuery < QueryElement + # Initializes an operator query + # + # @param String op Operator string + # @param String, Symbol a Field name + # @param Object b Value def initialize(op, a, b) self.operator = op super(a,b) end end + # Query element for IS NULL conditions class IsNullQuery < QueryElement self.operator = 'IS NULL' + # Initializes an IS NULL query + # + # @param String, Symbol a Field name def initialize(a) @a = a end @@ -223,10 +317,11 @@ {a: {field: @a}, o: operator} end end + # Query element for IS NOT NULL conditions class IsNotNullQuery < IsNullQuery self.operator = 'IS NOT NULL' end end end -end \ No newline at end of file +end
View file
activeresource-hel-0.7.0.gem/data/lib/activeresource/hel/rails.rb -> activeresource-hel-0.7.1.gem/data/lib/activeresource/hel/rails.rb
Changed
@@ -1,4 +1,9 @@ module ActiveResource::Hel + # Module for Rails-specific configuration support + # + # Provides functionality to use different configurations for different + # environments or contexts. + # module Rails extend ActiveSupport::Concern @@ -7,6 +12,11 @@ end module ClassMethods + # Uses a specific configuration from the configurations hash + # + # @param Symbol sym Configuration key (e.g., :development, :production) + # @return Hash Configuration hash + # @raise StandardError if configuration not found def use_configuration(sym) self.configurations ||= {} raise StandardError.new("Configuration #{sym} not found") unless self.configurations.keys.include?(sym) @@ -19,4 +29,3 @@ end end end -
View file
activeresource-hel-0.7.0.gem/data/lib/activeresource/hel/railtie.rb -> activeresource-hel-0.7.1.gem/data/lib/activeresource/hel/railtie.rb
Changed
@@ -1,4 +1,9 @@ module ActiveResource::Hel + # Railtie for Rails integration + # + # Automatically configures ActiveResource::Hel::Resource with settings from + # config/activeresource_hel.yml or environment variables. + # class Railtie < ::Rails::Railtie initializer "activeresource-hel_initialization" do config_file = ::Rails.root.join('config', 'activeresource_hel.yml')
View file
activeresource-hel-0.7.0.gem/data/lib/activeresource/hel/remote_pagination.rb -> activeresource-hel-0.7.1.gem/data/lib/activeresource/hel/remote_pagination.rb
Changed
@@ -2,6 +2,14 @@ require "kaminari/models/configuration_methods" module ActiveResource::Hel + # Module for adding remote pagination support + # + # Provides Kaminari-compatible pagination for remote resources using + # start and limit parameters. + # + # @example + # User.paginate(1, 25).all # First page, 25 items per page + # module RemotePagination extend ActiveSupport::Concern include Kaminari::ConfigurationMethods @@ -22,4 +30,4 @@ module ClassMethods end end -end \ No newline at end of file +end
View file
activeresource-hel-0.7.0.gem/data/lib/activeresource/hel/remote_schema.rb -> activeresource-hel-0.7.1.gem/data/lib/activeresource/hel/remote_schema.rb
Changed
@@ -1,17 +1,23 @@ module ActiveResource::Hel + # Module for loading remote schema definitions + # + # Provides functionality to fetch and process schema definitions from the + # remote API, automatically defining attributes and associations. + # module RemoteSchema extend ActiveSupport::Concern - included do + included do class_attribute :remote_schema ActiveResource::Schema::KNOWN_ATTRIBUTE_TYPES.concat('embedded_collection', 'collection') end module ClassMethods + # Gets whether remote schema loading is enabled # - # - # + # @return Boolean true if remote schema loading is enabled + # @see load_remote_schema= def load_remote_schema if defined?(@load_remote_schema) @load_remote_schema @@ -22,14 +28,27 @@ end end + # Sets whether remote schema loading is enabled + # + # @param Boolean value Whether to enable remote schema loading + # @return void + # @see load_remote_schema def load_remote_schema=(value) @load_remote_schema = value end + # Ensures remote schema is loaded if enabled + # + # @return void def ensure_remote_schema fetch_remote_schema if load_remote_schema end + # Maps a remote schema attribute type to an ActiveResource attribute type + # + # @param String, Symbol association Association name + # @param Hash obj Schema object definition + # @return Symbol, String Mapped attribute type def map_attribute_type(association, obj) type = obj'type' @@ -63,6 +82,9 @@ end + # Processes the remote schema and defines attributes + # + # @return void def process_remote_schema klass = self @@ -75,6 +97,9 @@ private + # Fetches the remote schema from the server + # + # @return Hash Remote schema hash def fetch_remote_schema return remote_schema if remote_schema @@ -87,4 +112,4 @@ end end -end \ No newline at end of file +end
View file
activeresource-hel-0.7.0.gem/data/lib/activeresource/hel/resource.rb -> activeresource-hel-0.7.1.gem/data/lib/activeresource/hel/resource.rb
Changed
@@ -1,8 +1,333 @@ module ActiveResource::Hel + # Extends ActiveResource::Base to provide a Hel resource with enhanced functionality # - # Extends ActiveResource::Base to provide a Hel resource of the appropriate format and - # authentication + # This class provides: + # - Hel-style authentication + # - Enhanced REST method support with proper encoding/decoding + # - Embedded resource and collection support + # - Remote schema loading + # - Caching capabilities + # - Scopes and filters + # - Dirty tracking + # + # @example Basic usage + # class User < ActiveResource::Hel::Resource + # self.site = 'https://api.example.com' + # self.hel_identity = 'identity' + # self.hel_secret = 'secret' + # end + # + # user = User.find(1) + # user.name = 'John' + # user.save + # + # == Included Modules + # + # This class includes the following modules which add methods and properties: + # + # === ActiveResource::Hel::Scopes + # + # Adds scope support for building chainable queries: + # + # @!method scoped + # Gets a new scope object for building queries + # @return ActiveResource::Hel::Scope New scope instance + # @example User.scoped.where(status: 'active').limit(10).all + # + # @!method where(params = {}) + # Adds where conditions to the query (delegated to scoped) + # @param Hash params Conditions hash + # @return ActiveResource::Hel::Scope Scope with conditions + # @example User.where(status: 'active', role: 'admin') + # + # @!method set_filter(filter) + # Sets a filter for the query (delegated to scoped) + # @param Hash filter Filter hash + # @return ActiveResource::Hel::Scope Scope with filter + # + # @!method scope(name, *args, &block) + # Defines a named scope + # @param Symbol, String name Scope name + # @param Proc, Hash *args Scope definition + # @yield Block to use as scope definition + # @return void + # @example scope :active, -> { where(status: 'active') } + # + # @!method scopes + # Gets the hash of defined scopes + # @return Hash<Symbol, Proc> Hash of scope names and definitions + # + # === ActiveResource::Hel::DefaultScopes + # + # Adds default scopes: + # + # @!method unscoped + # Returns a scope without any conditions + # @return ActiveResource::Hel::Scope Unscoped scope + # + # @!method conditions(hash = {}) + # Adds conditions from a hash + # @param Hash hash Conditions hash + # @return ActiveResource::Hel::Scope Scope with conditions + # + # @!method raw_filter(f) + # Sets a raw filter hash + # @param Hash f Filter hash + # @return ActiveResource::Hel::Scope Scope with filter + # + # @!method filter(field, operator, value) + # Adds a filter condition + # @param String, Symbol field Field name + # @param String operator Operator (=, >, <, etc.) + # @param Object value Value to compare + # @return ActiveResource::Hel::Scope Scope with filter + # + # @!method my(n = nil) + # Uses the 'my' custom method for the query + # @param Object n Optional parameter + # @return ActiveResource::Hel::Scope Scope with 'my' option + # + # @!method limit(n) + # Sets the limit for pagination + # @param Integer n Number of items per page + # @return ActiveResource::Hel::Scope Scope with limit + # + # @!method start(n) + # Sets the start offset for pagination + # @param Integer n Start offset + # @return ActiveResource::Hel::Scope Scope with start offset + # + # @!method offset(n) + # Sets the offset (alias for start) + # @param Integer n Offset value + # @return ActiveResource::Hel::Scope Scope with offset + # + # @!method view(v) + # Sets the view name for the query + # @param String, Symbol v View name + # @return ActiveResource::Hel::Scope Scope with view + # + # @!method api_scopes(n) + # Sets API scopes for the query + # @param Object n Scopes value + # @return ActiveResource::Hel::Scope Scope with API scopes + # + # === ActiveResource::Hel::RemotePagination + # + # Adds pagination support: + # + # @!method paginate(page, per_page = nil) + # Paginates the query results + # @param Integer page Page number + # @param Integer per_page Items per page (default: Kaminari default) + # @return ActiveResource::Hel::Scope Paginated scope + # @example User.paginate(1, 25).all + # + # === ActiveResource::Hel::RemoteSchema + # + # Adds remote schema loading: + # + # @!method load_remote_schema + # Gets whether remote schema loading is enabled + # @return Boolean true if enabled + # + # @!method load_remote_schema=(value) + # Sets whether remote schema loading is enabled + # @param Boolean value Whether to enable + # @return void + # + # @!method ensure_remote_schema + # Ensures remote schema is loaded if enabled + # @return void + # + # @!method process_remote_schema + # Processes the remote schema and defines attributes + # @return void + # + # === ActiveResource::Hel::Validations + # + # Adds remote validation support: + # + # @!method remote_validation + # Gets whether remote validation is enabled + # @return Boolean true if enabled + # + # @!method remote_validation=(value) + # Sets whether remote validation is enabled + # @param Boolean value Whether to enable + # @return void + # + # @!method save_with_validation(options = {}) + # Saves the resource with validation + # @param Hash options Save options + # @option options Boolean :validate Whether to perform validation (default: true) + # @return Boolean true if save succeeded + # + # @!method valid?(options = {}) + # Validates the resource, optionally including remote validation + # @param Hash options Validation options + # @option options Boolean :skip_remote_validation Skip remote validation + # @return Boolean true if valid + # + # === ActiveResource::Hel::Cached + # + # Adds caching capabilities: + # + # @!method cached + # Gets whether caching is enabled for this resource class + # @return Boolean true if enabled + # + # @!method cached=(cached) + # Sets whether caching is enabled + # @param Boolean cached Whether to enable caching + # @return void + # + # @!method force_revalidate_every(options) + # Forces revalidation of all cached resources matching options + # @param Hash options Find options + # @return void + # + # @!method force_revalidate_single(scope, options) + # Forces revalidation of a single cached resource + # @param Integer, String scope Resource ID + # @param Hash options Find options + # @return void + # + # @!method without_cache(&block) + # Temporarily disables caching for a block + # @yield Block to execute without caching + # @return Object Result of block execution + # + # @!method cache_version_key + # Gets the cache version key for this resource class + # @return String Cache version key + # + # @!method increase_request_version + # Increments the cache version to invalidate cached responses + # @return void + # + # Instance method: + # + # @!method reload + # Reloads the resource from the server, bypassing cache + # @return ActiveResource::Hel::Resource self + # + # === ActiveResource::Hel::Filters + # + # Adds filter building DSL: + # + # @!method build_filter(proc = nil, &block) + # Builds a filter query using the DSL + # @param Proc proc Optional proc to use as filter definition + # @yield Block to build the filter query + # @return ActiveResource::Hel::Filters::DSL Filter DSL instance + # @example User.build_filter { eq(status: 'active') } + # + # === ActiveResource::Hel::Dirty + # + # Adds dirty tracking for attribute changes: + # + # @!method changed? + # Checks if any attributes have unsaved changes + # @return Boolean true if any attributes changed + # + # @!method changed + # Gets an array of attribute names that have changed + # @return Array<String> Array of changed attribute names + # + # @!method changes + # Gets a hash of changed attributes with original and new values + # @return HashWithIndifferentAccess Hash: attr => old_value, new_value + # + # @!method previous_changes + # Gets a hash of attributes that were changed before the last save + # @return HashWithIndifferentAccess Hash of previous changes + # + # @!method changed_attributes + # Gets a hash of changed attributes with their original values + # @return HashWithIndifferentAccess Hash: attr => old_value + # + # @!method attribute_changed?(attr, from: OPTION_NOT_GIVEN, to: OPTION_NOT_GIVEN) + # Checks if an attribute has changed + # @param String, Symbol attr Attribute name + # @param Object from Optional original value to check + # @param Object to Optional new value to check + # @return Boolean true if changed and matches conditions + # + # @!method attribute_was(attr) + # Gets the original value of a changed attribute + # @param String, Symbol attr Attribute name + # @return Object Original value if changed, current value otherwise + # + # @!method attribute_previously_changed?(attr) + # Checks if an attribute was changed before the last save + # @param String, Symbol attr Attribute name + # @return Boolean true if previously changed + # + # @!method restore_attributes(attributes = changed) + # Restores previous values for the specified attributes + # @param Array<String, Symbol> attributes Attribute names to restore + # @return void + # + # Dynamic methods for each attribute: + # + # @!method attr_changed? + # Checks if a specific attribute has changed + # @return Boolean true if changed + # @example user.name_changed? + # + # @!method attr_change + # Gets the change array for a specific attribute + # @return Array, nil old_value, new_value or nil if not changed + # @example user.name_change # => 'John', 'Jane' + # + # @!method attr_will_change! + # Marks an attribute as about to change + # @return void + # @example user.name_will_change! + # + # @!method attr_was + # Gets the original value of a changed attribute + # @return Object Original value + # @example user.name_was # => 'John' + # + # @!method attr_previously_changed? + # Checks if an attribute was previously changed + # @return Boolean true if previously changed + # + # @!method attr_previous_change + # Gets the previous change array for an attribute + # @return Array, nil old_value, new_value or nil + # + # @!method restore_attr! + # Restores the previous value of an attribute + # @return void + # @example user.restore_name! + # + # === ActiveResource::Hel::Rails (if Rails is available) + # + # Adds Rails-specific configuration: + # + # @!method use_configuration(sym) + # Uses a specific configuration from the configurations hash + # @param Symbol sym Configuration key (e.g., :development, :production) + # @return Hash Configuration hash + # @raise StandardError if configuration not found + # + # == Class Attributes + # + # @!attribute rw cache + # Cache store to use for HTTP caching + # @return ActiveSupport::Cache::Store Cache store instance + # + # @!attribute rw scopes + # Hash of defined scopes for this resource class + # @return Hash<Symbol, Proc> Scope definitions + # + # @!attribute rw remote_schema + # Remote schema hash loaded from the server + # @return Hash, nil Schema hash or nil # class Resource < ActiveResource::Base DEFAULT_CACHE = ActiveSupport::Cache::MemoryStore.new @@ -34,9 +359,13 @@ include ThreadsafeAttributes threadsafe_attribute :_hel_identity, :_hel_secret + # Determines whether to guess embedded object types instead of returning hashes # - # trues to guess the type of embedded object instead of just returning hashes + # When enabled, embedded objects will be instantiated as Resource objects + # instead of plain Hash objects. # + # @return Boolean true if embedded types should be guessed, false otherwise + # @see guess_embedded_types= def guess_embedded_types if defined?(@guess_embedded_types) @guess_embedded_types @@ -47,13 +376,23 @@ end end + # Sets whether to guess embedded object types + # + # @param Boolean guess Whether to guess embedded types + # @return void + # @see guess_embedded_types def guess_embedded_types=(guess) @guess_embedded_types = guess end + # Encodes request body and decodes response # - # encodes requests + # Handles encoding of request bodies and decoding of responses for custom REST methods. # + # @param Hash options Request options + # @param Hash, String body Request body (Hash will be converted to JSON) + # @yield String encoded_body The encoded request body + # @return Hash Decoded response body def resource_codec(options, body, &block) body ||= {} @@ -62,30 +401,65 @@ format.plain_decode(res.body) end + # Performs a GET request to a custom collection method # - # custom REST class/collection methods + # @param String, Symbol custom_method_name Name of the custom method + # @param Hash options Request options (query parameters) + # @return Hash Decoded response body # + # @example + # User.get(:search, params: {q: 'john'}) def get(custom_method_name, options = {}) format.plain_decode(connection.get(custom_method_collection_url(custom_method_name, options), headers).body) end + # Performs a POST request to a custom collection method + # + # @param String, Symbol custom_method_name Name of the custom method + # @param Hash options Request options + # @param Hash, String body Request body (Hash will be JSON encoded) + # @return Hash Decoded response body + # + # @example + # User.post(:bulk_create, {}, {users: ...}) def post(custom_method_name, options = {}, body = '') resource_codec(options, body) do |encoded_body| super(custom_method_name, options, encoded_body) end end + # Performs a PUT request to a custom collection method + # + # @param String, Symbol custom_method_name Name of the custom method + # @param Hash options Request options + # @param Hash, String body Request body (Hash will be JSON encoded) + # @return Hash Decoded response body + # + # @example + # User.put(:bulk_update, {}, {users: ...}) def put(custom_method_name, options = {}, body = '') resource_codec(options, body) do |encoded_body| super(custom_method_name, options, encoded_body) end end + # Performs a DELETE request to a custom collection method + # + # @param String, Symbol custom_method_name Name of the custom method + # @param Hash options Request options + # @return Hash Decoded response body + # + # @example + # User.delete(:bulk_delete, params: {ids: 1,2,3}) def delete(custom_method_name, options = {}) res = super(custom_method_name, options) format.plain_decode(res.body) end + # Gets the Hel identity for authentication + # + # @return String, nil The Hel identity, or nil if not set + # @see hel_identity= def hel_identity # Not using superclass_delegating_reader. See +site+ for explanation if _hel_identity_defined? @@ -95,11 +469,20 @@ end end + # Sets the Hel identity for authentication + # + # @param String id The Hel identity + # @return void + # @see hel_identity def hel_identity=(id) self._connection = nil self._hel_identity = id end + # Gets the Hel secret for authentication + # + # @return String, nil The Hel secret, or nil if not set + # @see hel_secret= def hel_secret # Not using superclass_delegating_reader. See +site+ for explanation if _hel_secret_defined? @@ -109,11 +492,22 @@ end end + # Sets the Hel secret for authentication + # + # @param String sec The Hel secret + # @return void + # @see hel_secret def hel_secret=(sec) self._connection = nil self._hel_secret = sec end + # Gets or creates the connection instance + # + # Configures the connection with authentication credentials and other settings. + # + # @param Boolean refresh Whether to force refresh of the connection (default: false) + # @return ActiveResource::Hel::Connection The connection instance def connection(refresh = false) if _connection_defined? || superclass == Object self._connection = connection_class.new( @@ -136,11 +530,15 @@ end end + # Override some specific AR::Base methods which we want to have slightly different + # behaviors + + # Generates the prefix path for resource URLs # - # Override some specific AR::Base method which we want to have slighly different - # behaviours + # Includes namespace support and ensures proper path formatting. # - + # @param Hash options Prefix options + # @return String The prefix path def prefix(options={}) default = site.path default << '/' unless default-1..-1 == '/' @@ -151,36 +549,74 @@ prefix(options) end + # Gets the namespace for this resource class + # + # Extracts the namespace from the class name by removing the class name itself. + # + # @return String The namespace path + # + # @example + # Api::V1::User.namespace # => "api/v1" def namespace ActiveSupport::Inflector.deconstantize(self.name).underscore end + # Generates the path for a specific element + # + # @param String, Integer id The element ID + # @param Hash prefix_options Prefix options + # @param Hash query_options Query string options (if nil, extracted from prefix_options) + # @return String The element path URL def element_path(id, prefix_options = {}, query_options = nil) check_prefix_options(prefix_options) prefix_options, query_options = split_options(prefix_options) if query_options.nil? "#{prefix(prefix_options)}#{collection_name}/#{URI::DEFAULT_PARSER.escape id.to_s}#{query_string(query_options)}" end + # Generates the path for creating a new element + # + # @param Hash prefix_options Prefix options + # @return String The new element path URL def new_element_path(prefix_options = {}) "#{prefix(prefix_options)}#{collection_name}/new" end + # Generates the path for the collection + # + # @param Hash prefix_options Prefix options + # @param Hash query_options Query string options (if nil, extracted from prefix_options) + # @return String The collection path URL def collection_path(prefix_options = {}, query_options = nil) check_prefix_options(prefix_options) prefix_options, query_options = split_options(prefix_options) if query_options.nil? "#{prefix(prefix_options)}#{collection_name}#{query_string(query_options)}" end + # Generates the URL for a custom collection method + # + # @param String, Symbol method_name Name of the custom method + # @param Hash options Options hash (will be split into prefix and query options) + # @return String The custom method collection URL def custom_method_collection_url(method_name, options = {}) prefix_options, query_options = split_options(options) "#{prefix(prefix_options)}#{collection_name}/#{method_name}#{query_string(query_options)}" end - + # Executes a block within a specific session context + # + # @param ActiveResource::Hel::Session, Hash session Session object or hash of session parameters + # @yield Block to execute with the session + # @return Object Result of the block execution def with_session(session, &block) connection.with_session(session, &block) end + # Finds resources with automatic remote schema loading + # + # Ensures remote schema is loaded before finding resources. + # + # @param Symbol, Integer, Hash *arguments Find arguments (scope, options, etc.) + # @return ActiveResource::Hel::Collection, ActiveResource::Hel::Resource, nil Found resource(s) def find(*arguments) ensure_remote_schema super @@ -188,7 +624,14 @@ private - # Find every resource + # Finds every resource matching the options + # + # Handles custom method calls, custom paths, and standard collection queries. + # + # @param Hash options Find options + # @option options Symbol :from Custom method name (Symbol) or custom path (String) + # @option options Hash :params Query parameters + # @return ActiveResource::Hel::Collection, nil Collection of resources or nil if not found def find_every(options) begin case from = options:from @@ -214,28 +657,44 @@ if ActiveResource::VERSION::MAJOR >= 4 def instantiate_collection(collection, response, original_params = {}, prefix_options = {}) - total_count = response.to_hash'x-total-resources-count'.try(:first).to_i + headers = response.to_hash + + total_count = headers'x-total-resources-count'&.first&.to_i || collection&.length || 0 + offset = headers'x-resources-offset'&.first&.to_i || 0 + limit = headers'x-resources-limit'&.first&.to_i || total_count + ActiveResource::Hel::Collection.new(super(collection, original_params, prefix_options), { :total_count => total_count, - :offset => (response.to_hash'x-resources-offset'.try(:first) || 0).to_i, - :limit => (response.to_hash'x-resources-limit'.try(:first) || total_count).to_i + :offset => offset, + :limit => limit }) - end else def instantiate_collection(collection, response, prefix_options = {}) - total_count = response.to_hash'x-total-resources-count'.try(:first).to_i + headers = response.to_hash + + total_count = headers'x-total-resources-count'&.first&.to_i || collection.length || 0 + offset = headers'x-resources-offset'&.first&.to_i || 0 + limit = headers'x-resources-limit'&.first&.to_i || total_count + ActiveResource::Hel::Collection.new(super(collection, prefix_options), { :total_count => total_count, - :offset => (response.to_hash'x-resources-offset'.try(:first) || 0).to_i, - :limit => (response.to_hash'x-resources-limit'.try(:first) || total_count).to_i + :offset => offset, + :limit => limit, }) - end end end + # Loads a collection attribute from the response + # + # Handles both regular collections and embedded collections based on schema definition. + # + # @param String, Symbol key Attribute name + # @param Array value Array of attribute values (hashes or objects) + # @param Boolean persisted Whether the data is persisted + # @return Array, ActiveResource::Hel::Collection, ActiveResource::Hel::EmbeddedCollection Loaded collection def load_collection(key, value, persisted) resource = nil embedded = false @@ -268,6 +727,17 @@ value end + # Loads an object attribute from the response + # + # Attempts to instantiate the object as a Resource if type information is available. + # + # @param String, Symbol key Attribute name + # @param Hash value Hash of attribute values + # @param Boolean persisted Whether the data is persisted + # @param Class resource_class Optional resource class to use + # @param Boolean collection Whether this is part of a collection + # @param Boolean embedded Whether this is an embedded resource + # @return ActiveResource::Hel::Resource, Hash Loaded resource object or hash def load_object(key, value, persisted, resource_class = nil, collection = nil, embedded=nil) if (value.has_key?(:_type) || value.has_key?('_type')) && (type = value.delete(:_type) { value.delete('_type') }) @@ -282,6 +752,15 @@ end end + # Loads attributes into the resource + # + # Processes attributes, handling root removal, collections, and embedded objects. + # + # @param Hash attributes Hash of attributes to load + # @param Boolean remove_root Whether to remove root element (default: false) + # @param Boolean persisted Whether the data is persisted (default: false) + # @return ActiveResource::Hel::Resource self + # @raise ArgumentError if attributes is not a Hash def load(attributes, remove_root = false, persisted = false) raise ArgumentError, "expected an attributes Hash, got #{attributes.inspect}" unless attributes.is_a?(Hash) @prefix_options, attributes = split_options(attributes) @@ -308,9 +787,14 @@ self end + # Performs a GET request to a custom instance method # - # custom REST instance/item methods + # @param String, Symbol method_name Name of the custom method + # @param Hash options Request options + # @return Hash Decoded response body # + # @example + # user.get(:profile) def get(method_name, options = {}) self.class.format.plain_decode(connection.get(custom_method_element_url(method_name, options), self.class.headers).body) end @@ -332,22 +816,41 @@ format.plain_decode(res.body) end + # Returns a hash containing only the specified attributes # - # returns an hash of the listed attributes + # @param Array<Symbol, String> *attrs Attribute names to include + # @return HashWithIndifferentAccess Hash containing only the specified attributes # + # @example + # user.filtered_attributes(:name, :email) def filtered_attributes(*attrs) attributes.inject_hash(HashWithIndifferentAccess.new) { |acc, (n,v)| accn = v if attrs.include?(n.to_sym) } end + # Gets an attribute value by name + # + # @param String, Symbol name Attribute name + # @return Object, nil Attribute value or nil if not set def attribute(name) attributesname end + # Sets an attribute value by name + # + # @param String, Symbol name Attribute name + # @param Object value Attribute value + # @return Object The set value def set_attribute(name, value) self.send("#{name}=".to_sym, value) end + # Updates multiple attributes with dirty tracking + # + # Marks all attributes as changed before updating. + # + # @param Hash attributes Hash of attribute names and values + # @return Boolean true if update succeeded, false otherwise def update_attributes(attributes) attributes.keys.each do |k| attribute_will_change! k @@ -355,10 +858,21 @@ super end + # Assigns a single attribute value + # + # @param String, Symbol name Attribute name + # @param Object value Attribute value + # @return Object The assigned value def assign_attribute(name, value) self.send("#{name}=".to_sym, value) end + # Assigns multiple attributes with dirty tracking + # + # Marks all attributes as changed before loading. + # + # @param Hash attributes Hash of attribute names and values + # @return ActiveResource::Hel::Resource self def assign_attributes(attributes) attributes.keys.each do |k| attribute_will_change! k @@ -366,6 +880,16 @@ load(attributes) end + # Saves the resource using a custom method + # + # Performs validation and saves using the specified custom method instead of + # the standard create/update methods. + # + # @param String, Symbol method Custom method name to use for saving + # @param Hash options Save options + # @option options Boolean :validate Whether to perform validation (default: true) + # @return Boolean true if save succeeded, false otherwise + # @raise ActiveResource::ResourceInvalid if validation fails and save! is called def custom_save(method, options = {}) perform_validation = options:validate != false options.merge!(:custom_method => method, :skip_remote_validation => true) @@ -389,15 +913,28 @@ false end + # Saves the resource using a custom method, raising an exception on failure + # + # @param String, Symbol method Custom method name to use for saving + # @param Hash options Save options + # @return Boolean true if save succeeded + # @raise ActiveResource::ResourceInvalid if save fails def custom_save!(method, options = {}) custom_save(method, options) || raise(ActiveResource::ResourceInvalid.new(self)) end + # Reads an attribute value for serialization + # + # Handles special serialization for embedded collections, including dirty tracking + # and destroyed items. + # + # @param String, Symbol n Attribute name + # @param Hash options Serialization options + # @option options Boolean :dirty Whether to include only changed attributes + # @return Object, Array Attribute value or serialized array for embedded collections def read_attribute_for_serialization(n, options = {}) - case scheman when 'embedded_collection' - collection = attributesn # changed elements @@ -411,7 +948,7 @@ end collection.destroyed.each do |e| - res.push(:id => e, :_destroy => true) + res.push('id' => e, '_destroy' => true) end res else @@ -419,24 +956,50 @@ end end + # Converts the resource to a hash for serialization + # + # Supports filtering by :only, :except, :dirty, and :include options. + # + # @param Hash, nil options Serialization options + # @option options Array :only List of attributes to include + # @option options Array :except List of attributes to exclude + # @option options Boolean :dirty Include only changed attributes + # @option options Array :include Associations to include + # @option options Array :methods Method names to include as attributes + # @return Hash Serialized hash representation def serializable_hash(options = nil) options ||= {} + options = options.dup + only_attrs = options.delete(:only) + except_attrs = options.delete(:except) + include_attrs = options.delete(:include) attribute_names = attributes.keys - if only = options:only - attribute_names &= Array(only).map(&:to_s) - elsif except = options:except - attribute_names -= Array(except).map(&:to_s) + if only_attrs + attribute_names &= Array(only_attrs).map(&:to_s) + elsif except_attrs + attribute_names -= Array(except_attrs).map(&:to_s) elsif options:dirty attribute_names &= Array(self.changed.append(:id)).map(&:to_s) end hash = {} - attribute_names.each { |n| hashn = read_attribute_for_serialization(n, options) } + attribute_names.each do |n| + hashn = read_attribute_for_serialization(n, options) + end + if options:dirty + schema.each do | name, defn | + if defn == 'embedded_collection' + val = read_attribute_for_serialization(name, options) + hashname = val if val.present? + end + end + end Array(options:methods).each { |m| hashm.to_s = send(m) if respond_to?(m) } - serializable_add_includes(options) do |association, records, opts| + serializable_add_includes({include: include_attrs}) do |association, records, opts| + opts = {:dirty => options:dirty}.merge(opts) hashassociation.to_s = if records.respond_to?(:to_ary) records.to_ary.map { |a| a.serializable_hash(opts) } else @@ -460,9 +1023,15 @@ end =end + # Updates the resource on the remote service with dirty tracking # - # adds dirty tracking to the default method + # Tracks changes and clears them after successful update. # + # @param Hash options Update options + # @option options String, Symbol :custom_method Custom method name to use + # @option options Boolean :only_validate Whether to only validate without saving + # @option options Hash :additional_headers Additional HTTP headers + # @return Net::HTTPResponse HTTP response def update(options = {}) additionals_headers = options:only_validate ? { 'X-Validate-Only' => 'true' } : {} additionals_headers.merge!(options:additional_headers) if options:additional_headers @@ -482,8 +1051,15 @@ end end - # Create (i.e., \save to the remote service) the \new resource. - # adds dirty tracking to the default method + # Creates the resource on the remote service with dirty tracking + # + # Saves a new resource to the remote service and tracks changes. + # + # @param Hash options Create options + # @option options String, Symbol :custom_method Custom method name to use + # @option options Boolean :only_validate Whether to only validate without saving + # @option options Hash :additional_headers Additional HTTP headers + # @return Net::HTTPResponse HTTP response def create(options = {}) additionals_headers = options:only_validate ? { 'X-Validate-Only' => 'true' } : {} additionals_headers.merge!(options:additional_headers) if options:additional_headers @@ -502,45 +1078,75 @@ end end + # Clears changes in all embedded collections + # + # @return void def clear_embedded_changes embedded_collections.each {|c| c.clear_embedded_changes } end + # Gets all embedded collection attributes + # + # @return Array<ActiveResource::Hel::EmbeddedCollection> Array of embedded collections def embedded_collections attributes.values.select {|k,v| v.is_a? EmbeddedCollection } end + # Handles dynamic attribute accessors and mutators # - # adds dirty tracking to the default method + # Provides support for attribute getters, setters, and boolean predicates. # + # @param Symbol method_symbol Method name + # @param Array arguments Method arguments + # @return Object Attribute value or result of method call + # @raise NoMethodError if method is not found def method_missing(method_symbol, *arguments) #:nodoc: - method_name = method_symbol.to_s - - if method_name =~ /(=|\?)$/ - case $1 - when "=" - attribute_will_change! $` - attributes$` = arguments.first - when "?" - attributes$` - end - else - return attributesmethod_name if attributes.include?(method_name) - # not set right now but we know about it - return nil if known_attributes.include?(method_name) - super - end + method_name = method_symbol.to_s + + if method_name =~ /(=|\?)$/ + case $1 + when "=" + attribute_will_change! $` + attributes$` = arguments.first + when "?" + if known_attributes.include?($`) + attributes$` + else + super + end + end + else + return attributesmethod_name if attributes.include?(method_name) + # not set right now but we know about it + return nil if known_attributes.include?(method_name) + super + end end + # Generates the URL for a custom instance method + # + # @param String, Symbol method_name Name of the custom method + # @param Hash options Query string options + # @return String The custom method element URL def custom_method_element_url(method_name, options = {}) "#{self.class.prefix(prefix_options)}#{self.class.collection_name}/#{id}/#{method_name}#{self.class.__send__(:query_string, options)}" end + # Generates the URL for a custom new element method + # + # @param String, Symbol method_name Name of the custom method + # @param Hash options Query string options + # @return String The custom method new element URL def custom_method_new_element_url(method_name, options = {}) "#{self.class.prefix(prefix_options)}#{self.class.collection_name}/#{method_name}#{self.class.__send__(:query_string, options)}" end class << self + # Finds or creates a resource class by name + # + # @param String name Fully qualified class name + # @param Boolean embedded Whether this is an embedded resource + # @return Class Resource class def find_or_create_resource_by_name(name, embedded = false) if klass = class_exists?(name) klass @@ -549,6 +1155,10 @@ end end + # Checks if a class exists by name + # + # @param String class_name Fully qualified class name + # @return Boolean, Class false if class doesn't exist, otherwise the class def class_exists?(class_name) klass = class_from_string(class_name) klass.is_a?(Class) ? klass : false @@ -558,14 +1168,23 @@ false end + # Converts a string to a class constant + # + # @param String str Fully qualified class name + # @return Class, Module The class or module constant + # @raise NameError if the constant doesn't exist def class_from_string(str) str.split('::').reject {|x| x.blank?}.inject(Object) do |mod, class_name| mod.const_get(class_name) end end + # Creates a resource class in the appropriate namespace + # + # @param String resource_name Fully qualified resource name + # @param Boolean embedded Whether this is an embedded resource + # @return Class Newly created resource class def create_absolute_resource_for(resource_name, embedded = false) - parent_class = (embedded ? ActiveResource::Hel::EmbeddedResource : ActiveResource::Hel::Resource) ns = find_or_create_namespace ActiveSupport::Inflector.deconstantize(resource_name) @@ -576,6 +1195,10 @@ resource end + # Finds or creates a namespace module + # + # @param String, Array ns Namespace name(s) + # @return Module The namespace module def find_or_create_namespace(ns) return Object if ns.blank? @@ -591,7 +1214,10 @@ parent.const_get(name) end - # Create and return a class definition for a resource inside the current resource + # Creates a resource class inside the current resource class + # + # @param String resource_name Resource name (not fully qualified) + # @return Class Newly created resource class def create_resource_for(resource_name) resource = const_set(resource_name, Class.new(ActiveResource::Hel::Resource)) resource.prefix = prefix @@ -600,9 +1226,11 @@ end end + # Delegates class methods to the class delegate :find_or_create_resource_by_name, :create_absolute_resource_for, :create_resource_for, :to => 'self.class' end + # Resource class with Dirty tracking included class Resource include ActiveResource::Hel::Dirty
View file
activeresource-hel-0.7.0.gem/data/lib/activeresource/hel/scopes.rb -> activeresource-hel-0.7.1.gem/data/lib/activeresource/hel/scopes.rb
Changed
@@ -1,6 +1,19 @@ require 'active_support/core_ext/module/delegation' module ActiveResource::Hel + # Module for adding scope support to resources + # + # Provides ActiveRecord-like scoping capabilities for ActiveResource, + # including where clauses, custom scopes, and chainable query methods. + # + # @example + # class User < ActiveResource::Hel::Resource + # scope :active, -> { where(status: 'active') } + # scope :admins, -> { where(role: 'admin') } + # end + # + # User.active.admins.limit(10).all + # module Scopes extend ActiveSupport::Concern @@ -22,6 +35,9 @@ end module ClassMethods + # Gets a new scope object for this resource class + # + # @return ActiveResource::Hel::Scope New scope instance def scoped ActiveResource::Hel::Scope.new(self) end @@ -29,11 +45,23 @@ delegate :where, :to => :scoped delegate :set_filter, :to => :scoped - + # Gets the hash of defined scopes + # + # @return Hash<Symbol, Proc> Hash of scope names and their definitions def scopes self.scopes ||= {} end + # Defines a named scope + # + # @param Symbol, String name Scope name + # @param Proc, Hash *args Scope definition (Proc, Hash, or lambda) + # @yield Block to use as scope definition + # @return void + # + # @example + # scope :recent, -> { where(created_at: Time.now - 1.day) } + # scope :by_status, ->(status) { where(status: status) } def scope(name, *args, &block) scopesname = name.to_sym = (args.first || block || lambda { |*args| where name => args }) self.class.delegate name, :to => :scoped @@ -41,51 +69,113 @@ end end + # Scope class for building chainable queries + # + # Provides methods for building and executing queries with filters, limits, offsets, etc. + # class Scope include Enumerable + # @!attribute r resource_class + # @return Class Resource class for this scope attr_reader :resource_class + # @!attribute rw filters + # @return Hash, nil Filter hash for the query attr_accessor :filters + # Initializes a new Scope + # + # @param Class resource_class Resource class def initialize(resource_class) @resource_class = resource_class end + # Gets the query parameters hash + # + # @return Hash Query parameters def params @params ||= {} end + # Gets the custom options hash + # + # @return Hash Custom options def custom_options @custom_options ||= {} end + # Converts scope to array by executing the query + # + # @return Array Array of resources def to_a all end delegate :each, :to => :to_a + # Gets the first resource matching the scope + # + # @return ActiveResource::Hel::Resource, nil First resource or nil def first limit(1).all.first end + # Executes the scope query and returns results + # + # @param Hash *args Optional find options + # @return ActiveResource::Hel::Collection, nil Collection of resources or nil def all(*args) args = (args.last if args.last.is_a?(Hash)) || {} params.merge!(:filter => filters.to_json) if filters resource_class.find :all, custom_options.merge(:params => params).merge(args) end + # Uses the 'my' custom method for the query + # + # @return ActiveResource::Hel::Scope New scope with 'my' option def my params.merge!(:filter => filters.to_json) if filters dup.tap { |scope| scope.custom_options.merge!(:from => :my) } end + # Adds where conditions to the scope + # + # @param Hash params Conditions hash + # @return ActiveResource::Hel::Scope New scope with conditions def where(params = {}) dup.tap { |scope| scope.params.update params } end + # Sets the limit for pagination + # + # @param Integer limit Number of items per page (default: Kaminari default) + # @return ActiveResource::Hel::Scope New scope with limit + def limit(limit = Kaminari.config.default_per_page) + dup.tap { |scope| scope.params.update limit: limit } + end + + # Sets the start offset for pagination + # + # @param Integer start Start offset (default: 0) + # @return ActiveResource::Hel::Scope New scope with start offset + def start(start = 0) + dup.tap { |scope| scope.params.update start: start } + end + + # Sets the view name for the query + # + # @param String, Symbol name View name + # @return ActiveResource::Hel::Scope New scope with view + def view(name) + dup.tap { |scope| scope.params.update view: name } + end + + # Sets a filter for the query + # + # @param Hash filter Filter hash + # @return ActiveResource::Hel::Scope New scope with filter def set_filter(filter) - dup.tap do |scope| + dup.tap do |scope| if scope.filters.blank? scope.filters = filter else @@ -96,10 +186,16 @@ delegate :scopes, :to => :resource_class + # Handles dynamic scope method calls + # + # @param Symbol name Scope name + # @param Array args Scope arguments + # @return ActiveResource::Hel::Scope New scope with applied scope + # @raise NoMethodError if scope doesn't exist def method_missing(name, *args) return super unless scopes.key? name scope = scopesname - scope = scope.call(*args) if scope.is_a? Proc + scope = @resource_class.instance_exec(*args, &scope) if scope.is_a? Proc if scope.respond_to?(:filters) && scope.filters self.filters = set_filter(scope.filters).filters end @@ -107,4 +203,4 @@ where scope.params end end -end \ No newline at end of file +end
View file
activeresource-hel-0.7.0.gem/data/lib/activeresource/hel/session.rb -> activeresource-hel-0.7.1.gem/data/lib/activeresource/hel/session.rb
Changed
@@ -1,37 +1,81 @@ module ActiveResource::Hel + # Exception raised when authentication fails class FailedAuthentication < RuntimeError; end - # # Session context for the Hel Connection # + # Manages authentication sessions with the Hel API, including token-based + # authentication and session validation. + # + # @example + # session = ActiveResource::Hel::Session.new( + # site: 'https://api.example.com', + # identity: 'user@example.com', + # secret: 'password' + # ) + # session.authenticate! + # headers = session.authentication_headers + # class Session + # Exception raised when session is invalid class Invalid < RuntimeError; end + # Authentication endpoint path AUTH_METHOD = '/ygg/session/authenticate_by_fqda_and_password' + # Try authentication endpoint path TRY_AUTH_METHOD = '/ygg/session/would_authenticate_by_fqda_and_password' + # Session check endpoint path SESSION_CHECK_METHOD = '/ygg/session/check' + # Default timeout in seconds DEFAULT_TIMEOUT = 30 + # @!attribute r site + # @return String API site URL attr_reader :site + # @!attribute r data + # @return Hash Session data from authentication response attr_reader :data + # @!attribute r token + # @return String, nil Authentication token attr_reader :token + # @!attribute r identity + # @return String User identity (FQDA) attr_reader :identity + # @!attribute r status + # @return String, nil Session status attr_reader :status + # @!attribute r authenticated + # @return Boolean Whether session is authenticated attr_reader :authenticated + # @!attribute r message + # @return String Authentication message attr_reader :message + # @!attribute r reason + # @return String Authentication reason code attr_reader :reason - #debug + # @!attribute w token + # @return void Sets the authentication token (for debugging) attr_writer :token + # Initializes a new Session + # + # @param Hash options Session options + # @option options String :site API site URL (required) + # @option options String :identity User identity/FQDA (required) + # @option options String :secret User password/secret (required for authentication) + # @option options String :token Existing authentication token + # @option options Hash :ssl_options SSL options for connection + # @option options Integer :timeout Connection timeout in seconds (default: 30) + # @raise Invalid if identity is blank def initialize(options = {}) @site = options:site @identity = options:identity @secret = options:secret - @ssl_options = options:ssl_options + @ssl_options = options:ssl_options || {} if options:token @token = options:token @@ -46,6 +90,10 @@ end + # Initializes session from authentication response data + # + # @param Hash auth_data Authentication response data + # @return ActiveResource::Hel::Session self def initialize_from_data(auth_data) @data = auth_data @token, @status = @data'id', @data'status' @@ -61,10 +109,16 @@ self end + # Checks if the session is authenticated + # + # @return Boolean true if authenticated and token is present def authenticated? authenticated && !token.blank? end + # Gets the active identity resource + # + # @return Ygg::Core::Identity, nil Identity resource or nil if not authenticated def active_identity if authenticated? @active_identity ||= Ygg::Core::Identity.find_by_qualified_for_authentication(@identity) @@ -73,6 +127,14 @@ @active_identity = nil end + # Executes a block with authentication, retrying if unauthorized + # + # Authenticates if not already authenticated, and retries the request + # if authentication fails. + # + # @yield Hash Block receives authentication headers + # @return Object Result of block execution + # @raise ActiveResource::UnauthorizedAccess if authentication fails def with_authentication(&block) authenticate! unless authenticated? do_request &block @@ -85,6 +147,11 @@ do_request &block end + # Authenticates the session with the server + # + # @return Boolean true if authentication succeeded + # @raise Invalid if credentials are invalid + # @raise FailedAuthentication if authentication fails def authenticate! raise Invalid, 'Invalid session credentials' if identity.blank? || @secret.blank? login_data = {:fqda => identity, :password => @secret} @@ -92,10 +159,18 @@ authenticated? || raise(ActiveResource::Hel::FailedAuthentication, "#{message} #{reason}") end + # Attempts authentication without actually authenticating + # + # @param Hash data Authentication data (fqda, password) + # @return Hash Authentication response def try_authenticate(data) post(TRY_AUTH_METHOD, data) end + # Checks if given credentials would authenticate successfully + # + # @param Hash data Authentication data (fqda, password) + # @return Hash, false Authentication response if successful, false otherwise def would_authenticate?(data) res = try_authenticate(data) @@ -108,16 +183,29 @@ false end + # Gets HTTP headers for authenticated requests + # + # @return Hash Headers hash with Authorization header def authentication_headers {'Authorization' => "Bearer #{token}"} end + # Performs a POST request + # + # @param String path Request path + # @param Hash object Request body object + # @param Hash headers Additional HTTP headers + # @return Hash Decoded JSON response def post(path, object, headers = {}) request_body = object.to_json res = @connection.post(path, request_body, headers.merge('Accept' => 'application/json')) ActiveSupport::JSON.decode(res.body) end + # Executes a request block with authentication headers + # + # @yield Hash Block receives authentication headers + # @return Object Result of block execution def do_request(&block) block.call authentication_headers if block_given? end
View file
activeresource-hel-0.7.0.gem/data/lib/activeresource/hel/validations.rb -> activeresource-hel-0.7.1.gem/data/lib/activeresource/hel/validations.rb
Changed
@@ -1,8 +1,18 @@ module ActiveResource::Hel + # Module for adding remote validation support + # + # Provides integration between local ActiveModel validations and remote + # server-side validations, preventing infinite loops and properly handling + # validation errors from both sources. + # module Validations extend ActiveSupport::Concern module ClassMethods + # Gets whether remote validation is enabled + # + # @return Boolean true if remote validation is enabled + # @see remote_validation= def remote_validation if defined?(@remote_validation) @remote_validation @@ -13,11 +23,23 @@ end end + # Sets whether remote validation is enabled + # + # @param Boolean value Whether to enable remote validation + # @return void + # @see remote_validation def remote_validation=(value) @remote_validation = value end end + # Saves the resource with validation + # + # Performs both local and remote validation before saving. + # + # @param Hash options Save options + # @option options Boolean :validate Whether to perform validation (default: true) + # @return Boolean true if save succeeded, false otherwise def save_with_validation(options={}) perform_validation = options:validate != false @@ -40,6 +62,11 @@ false end + # Validates the resource, optionally including remote validation + # + # @param Hash options Validation options + # @option options Boolean :skip_remote_validation Skip remote validation + # @return Boolean true if valid, false otherwise def valid?(options = {}) opt = options.merge(:only_validate => true) @remote_errors = nil @@ -60,4 +87,4 @@ end end -end \ No newline at end of file +end
View file
activeresource-hel-0.7.0.gem/data/lib/activeresource/hel/version.rb -> activeresource-hel-0.7.1.gem/data/lib/activeresource/hel/version.rb
Changed
@@ -1,5 +1,6 @@ module ActiveResource module Hel - VERSION = '0.7.0' + # Current version of ActiveResource::Hel + VERSION = '0.7.1' end end
View file
activeresource-hel-0.7.0.gem/data/lib/ygg/core/identity.rb -> activeresource-hel-0.7.1.gem/data/lib/ygg/core/identity.rb
Changed
@@ -1,8 +1,18 @@ module Ygg module Core + # Identity resource class + # + # Represents a user identity in the Ygg system with authentication capabilities. + # class Identity < ActiveResource::Hel::Resource self.cached = false + # Finds an identity by qualified name for authentication purposes + # + # Loads the identity with authentication view which includes capabilities. + # + # @param String qualified Fully qualified domain address (FQDA) + # @return Ygg::Core::Identity Identity instance with capabilities loaded def self.find_by_qualified_for_authentication(qualified) find(qualified, :params => {:view => :authentication}).tap do |ident| ident.instance_variable_set :@caps, ident.capabilities.map {|cap| cap.name.to_sym} @@ -10,25 +20,40 @@ end + # Finds an identity by qualified name + # + # @param String qualified Fully qualified domain address (FQDA) + # @return Ygg::Core::Identity Identity instance def self.find_by_qualified(qualified) find(qualified) end + # Gets the person associated with this identity + # + # @return Ygg::Core::Person, nil Person instance or nil def person @person ||= Person.find_by_identity self end + # Checks if the given secret authenticates this identity # - # fixme uh? - # + # @param String secret Password/secret to check + # @return Boolean true if authentication succeeds def authenticated?(secret) post(:check_authentication, {}, {:password => secret}) end + # Checks if this identity has a specific capability + # + # @param Symbol, String cap Capability name + # @return Boolean true if identity has the capability def has_capability?(cap) caps.include?(cap) end + # Gets the list of capabilities for this identity + # + # @return Array<Symbol> Array of capability symbols def caps @caps ||= self.class.find(self.id, :params => {:view => :authentication}).capabilities.map {|cap| cap.name.to_sym}
View file
activeresource-hel-0.7.0.gem/data/lib/ygg/core/person.rb -> activeresource-hel-0.7.1.gem/data/lib/ygg/core/person.rb
Changed
@@ -1,16 +1,30 @@ module Ygg module Core + # Person resource class + # + # Represents a person in the Ygg system with associated identity and contact information. + # class Person < ActiveResource::Hel::Resource + # @!attribute rw active_identity + # @return Ygg::Core::Identity, nil Currently active identity for this person attr_accessor :active_identity class << self + # Finds a person by fully qualified domain address + # + # @param String qualified Fully qualified domain address (FQDA) + # @return Ygg::Core::Person, nil Person instance or nil def find_by_fqda(qualified) identity = Identity.find_by_qualified(qualified) person = find_by_identity(identity) end alias_method :find_by_qualified, :find_by_fqda + # Finds a person by identity + # + # @param Ygg::Core::Identity identity Identity instance + # @return Ygg::Core::Person, nil Person instance or nil def find_by_identity(identity) return nil unless (identity && identity.person_id) person = find(identity.person_id) @@ -18,47 +32,80 @@ person end + # Finds a person by identity ID + # + # @param Integer, String id Identity ID + # @return Ygg::Core::Person, nil Person instance or nil def find_by_identity_id(id) identity = Identity.find(id) find_by_identity(identity) end end + # Gets the qualified name (FQDA) of the active identity + # + # @return String, nil Qualified name or nil def qualified active_identity.qualified if active_identity end + # Gets the full name of the person + # + # @return String, nil Full name or nil if name not available def full_name return nil unless name name.first, name.middle, name.last.reject{ |a| a.nil? }.join(' ') end + # Checks if the person can authenticate with the given secret + # + # @param String secret Password/secret + # @return Boolean, nil true if authenticated, nil if no active identity def authenticated?(secret) return nil unless active_identity active_identity.authenticated?(secret) end + # Gets the capabilities of the active identity + # + # @return Array<Symbol> Array of capability symbols def capabilities active_identity.caps end + # Checks if the person has a specific capability + # + # @param Symbol, String cap Capability name + # @return Boolean true if has capability def has_capability?(cap) active_identity.has_capability?(cap) end + # Checks if the person is an admin + # + # @return Boolean true if has :admin capability def is_admin? has_capability?(:admin) end + # Gets all email addresses for this person + # + # @return Array<String> Array of email addresses sorted by preference def emails emails = channels.find_all { |c| c.channel_type == 'email' }.sort_by { |c| c.preference }. map {|c| c.value } end + # Gets the primary email address + # + # @return String, nil Primary email or nil def email emails.first end + # Gets attributes formatted for OAuth + # + # @return Hash Hash of OAuth-compatible attributes def attrs_for_oauth data = filtered_attributes( :uuid,
View file
activeresource-hel-0.7.0.gem/data/lib/ygg/session.rb -> activeresource-hel-0.7.1.gem/data/lib/ygg/session.rb
Changed
@@ -1,14 +1,23 @@ module Ygg + # Ygg session resource class + # + # Represents a session in the Ygg system, extending ActiveResource::Hel::Resource + # with Ygg-specific session management methods. + # class Session < ActiveResource::Hel::Resource self.cached = false + # Exception class for Ygg session errors class Exception < StandardError; end class << self + # Creates a new session for the given identity # - # Gets you a session on behalf of the given identity - # + # @param String, Ygg::Core::Identity identity Identity FQDA or Identity object + # @param Hash attributes Additional attributes for authentication + # @return Ygg::Session New session instance + # @raise Ygg::Session::Exception if session creation fails def new_for_identity(identity, attributes = {}) identity = identity.qualified if identity.is_a? Ygg::Core::Identity session = instantiate_record(proxy_authenticate_by_fqda(attributes.merge(:fqda => identity))) @@ -17,10 +26,10 @@ session end + # Checks if a session is valid # - # checks the given session for validity - # takes session || token - # + # @param Ygg::Session, String given_session Session object or token string + # @return Hash Session check response def check_validity(given_session) given_session = given_session.token if given_session.respond_to?(:token) @@ -31,80 +40,57 @@ # API methods # -------------------------------------- - # Responds with an authentication token if the user would authenticate using a fqda/password pair - # - # If the session-id is not given in a X-Ygg-Session-Id header, a new session is created at the same time. - # The specified or newly created session's id is included in the reply's header X-Ygg-Session-Id. - # - # The accepted parameters are: - # :fqda FQDA (fully qualified domain address). - # :username FQDA (fully qualified domain address), accepted when :fqda is not specified as a way to - # support browsers' password managers. - # :password The cleartext password. + # Checks if authentication would succeed without actually authenticating # - # The reply contains the authentication token + # Responds with an authentication token if the user would authenticate using + # a fqda/password pair. If the session-id is not given in a X-Ygg-Session-Id + # header, a new session is created at the same time. # + # @param Hash params Authentication parameters + # @option params String :fqda FQDA (fully qualified domain address) + # @option params String :username FQDA (alternative to :fqda for browser compatibility) + # @option params String :password Cleartext password + # @return Hash Response containing authentication token if successful def would_authenticate_by_fqda_and_password(params) post 'would_authenticate_by_fqda_and_password', {}, params end - # Attempts to authenticate a session by using a fqda/password pair for a third party. + # Authenticates a session using fqda/password for a third party # # Session ID is accepted in the request body only, along with HTTP environment data. # - # The accepted parameters are: - # :fqda FQDA (fully qualified domain address). - # :password The cleartext password. - # - # :remote_addr HTTP parameters - # :remote_port - # :x_forwarded_for - # :via - # :server_addr - # :server_port - # :server_name - # :referer - # :user_agent - # :request_uri - # - # The reply contains the session object. - # In addition to the session's usual attributes two attributes are included to describe the authentication result: - # - # :msg Human-readable explanation of the result - # :reason Can be either :ok or :wrong_credentials - # + # @param Hash params Authentication and HTTP parameters + # @option params String :fqda FQDA (fully qualified domain address) + # @option params String :password Cleartext password + # @option params String :remote_addr HTTP remote address + # @option params Integer :remote_port HTTP remote port + # @option params String :x_forwarded_for X-Forwarded-For header value + # @option params String :via Via header value + # @option params String :server_addr Server address + # @option params Integer :server_port Server port + # @option params String :server_name Server name + # @option params String :referer Referer header value + # @option params String :user_agent User-Agent header value + # @option params String :request_uri Request URI + # @return Hash Session object with :msg and :reason attributes def proxy_authenticate_by_fqda(params) post 'proxy_authenticate_by_fqda', {}, params end - # Authenticate a session as third party + # Authenticates a session as third party using fqda/password # # Session ID is accepted in the request body only, along with HTTP environment data. # - # The accepted parameters are: - # :fqda FQDA (fully qualified domain address). - # :remote_addr HTTP parameters - # :remote_port - # :x_forwarded_for - # :via - # :server_addr - # :server_port - # :server_name - # :referer - # :user_agent - # :request_uri - # - # The reply contains the session object. - # In addition to the session's usual attributes two attributes are included to describe the authentication result: - # - # :msg Human-readable explanation of the result - # :reason Can be either :ok or :wrong_credentials - # + # @param Hash params Authentication and HTTP parameters (see proxy_authenticate_by_fqda) + # @return Hash Session object with :msg and :reason attributes def proxy_authenticate_by_fqda_and_password(params) post 'proxy_authenticate_by_fqda_and_password', {}, params end + # Checks session validity # + # @param Hash, nil params Optional parameters + # @return Hash Session check response def check(params = nil) # if params post 'check', {}, params @@ -116,13 +102,17 @@ ####################################################### - - # collection name, since hel session_s_ are on the 'session' controller + # Gets the collection name for this resource + # + # @return String Collection name ("session") def collection_name "session" end - #?? + # Finds a session by ID + # + # @param Array *arguments Find arguments (session ID) + # @return Ygg::Session Session instance def find(*arguments) session = check(:session_id => arguments.first) instantiate_record(session) @@ -137,15 +127,23 @@ ######################### - + # Gets the identity associated with this session + # + # @return Ygg::Core::Identity, nil Identity or nil if not available def identity Ygg::Core::Identity.find_by_qualified(auth_identity) if auth_identity end + # Gets the session token (same as ID) + # + # @return String Session token/ID def token id end + # Checks if this session is valid + # + # @return Hash Session check response def check self.class.check_validity(self) end
View file
activeresource-hel-0.7.0.gem/data/spec/integration/resource/cache_spec.rb -> activeresource-hel-0.7.1.gem/data/spec/integration/resource/cache_spec.rb
Changed
@@ -1,8 +1,8 @@ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') require 'webmock/rspec' module Mock -class Resource < ActiveResource::Hel::Resource -end + class ResourceWithCache < ActiveResource::Hel::Resource + end end describe "A HEL Resource with HTTP cache" do @@ -19,9 +19,9 @@ end def stub_auth - stub_request(:post, req_url(Mock::Resource.site, "/ygg/session/authenticate_by_fqda_and_password")). + stub_request(:post, req_url(Mock::ResourceWithCache.site, "/ygg/session/authenticate_by_fqda_and_password")). with( - body: {fqda: Mock::Resource.hel_identity, password: Mock::Resource.hel_secret} + body: {fqda: Mock::ResourceWithCache.hel_identity, password: Mock::ResourceWithCache.hel_secret} ).to_return( status: 200, body: { @@ -43,21 +43,21 @@ it 'should request a resource not cached' do stub_auth - stub_request(:get, req_url(Mock::Resource.site, "/mock/resources/1")). + stub_request(:get, req_url(Mock::ResourceWithCache.site, "/mock/resource_with_caches/1")). to_return(status: 200, body: { id: 1, field1: 'field-1', field2: 'field-2', - field3: 'field-3' + field3: 'field-3' }.to_json) - res = Mock::Resource.find(1) - expect(res).to be_a Mock::Resource + res = Mock::ResourceWithCache.find(1) + expect(res).to be_a Mock::ResourceWithCache end it 'should not request a cached resource' do stub_auth - stub_request(:get, req_url(Mock::Resource.site, "/mock/resources/1")). + stub_request(:get, req_url(Mock::ResourceWithCache.site, "/mock/resource_with_caches/1")). to_return(status: 200, body: { id: 1, field1: 'field-1', @@ -70,20 +70,20 @@ } ) - res = Mock::Resource.find(1) - expect(res).to be_a Mock::Resource + res = Mock::ResourceWithCache.find(1) + expect(res).to be_a Mock::ResourceWithCache - expect(WebMock).to have_requested(:get, req_url(Mock::Resource.site, "/mock/resources/1")).once + expect(WebMock).to have_requested(:get, req_url(Mock::ResourceWithCache.site, "/mock/resource_with_caches/1")).once - res = Mock::Resource.find(1) - expect(res).to be_a Mock::Resource + res = Mock::ResourceWithCache.find(1) + expect(res).to be_a Mock::ResourceWithCache - expect(WebMock).to have_requested(:get, req_url(Mock::Resource.site, "/mock/resources/1")).once + expect(WebMock).to have_requested(:get, req_url(Mock::ResourceWithCache.site, "/mock/resource_with_caches/1")).once end it 'should revalidate a stale cached resource' do stub_auth - req = stub_request(:get, req_url(Mock::Resource.site, "/mock/resources/1")). + req = stub_request(:get, req_url(Mock::ResourceWithCache.site, "/mock/resource_with_caches/1")). to_return(status: 200, body: { id: 1, field1: 'field-1', @@ -96,12 +96,12 @@ } ) - res = Mock::Resource.find(1) - expect(res).to be_a Mock::Resource + res = Mock::ResourceWithCache.find(1) + expect(res).to be_a Mock::ResourceWithCache remove_request_stub(req) - expect(WebMock).to have_requested(:get, req_url(Mock::Resource.site, "/mock/resources/1")).once + expect(WebMock).to have_requested(:get, req_url(Mock::ResourceWithCache.site, "/mock/resource_with_caches/1")).once - stub_request(:get, req_url(Mock::Resource.site, "/mock/resources/1")). + stub_request(:get, req_url(Mock::ResourceWithCache.site, "/mock/resource_with_caches/1")). with(headers: {'If-None-Match' => 'abc123'}). to_return(status: 304, body: "{}", headers: { @@ -110,16 +110,16 @@ } ) Timecop.travel(610.seconds.from_now) do - res = Mock::Resource.find(1) - expect(res).to be_a Mock::Resource + res = Mock::ResourceWithCache.find(1) + expect(res).to be_a Mock::ResourceWithCache end - expect(WebMock).to have_requested(:get, req_url(Mock::Resource.site, "/mock/resources/1")).twice + expect(WebMock).to have_requested(:get, req_url(Mock::ResourceWithCache.site, "/mock/resource_with_caches/1")).twice end it 'should refresh a stale cached resoource' do stub_auth - req = stub_request(:get, req_url(Mock::Resource.site, "/mock/resources/1")). + req = stub_request(:get, req_url(Mock::ResourceWithCache.site, "/mock/resource_with_caches/1")). to_return(status: 200, body: { id: 1, field1: 'field-1', @@ -132,13 +132,13 @@ } ) - res = Mock::Resource.find(1) - expect(res).to be_a Mock::Resource + res = Mock::ResourceWithCache.find(1) + expect(res).to be_a Mock::ResourceWithCache remove_request_stub(req) - expect(WebMock).to have_requested(:get, req_url(Mock::Resource.site, "/mock/resources/1")).once + expect(WebMock).to have_requested(:get, req_url(Mock::ResourceWithCache.site, "/mock/resource_with_caches/1")).once - stub_request(:get, req_url(Mock::Resource.site, "/mock/resources/1")). + stub_request(:get, req_url(Mock::ResourceWithCache.site, "/mock/resource_with_caches/1")). with(headers: {'If-None-Match' => 'abc123'}). to_return(status: 200, body: { id: 1, @@ -152,19 +152,19 @@ } ) Timecop.travel(610.seconds.from_now) do - newres = Mock::Resource.find(1) - expect(newres).to be_a Mock::Resource + newres = Mock::ResourceWithCache.find(1) + expect(newres).to be_a Mock::ResourceWithCache expect(newres.field1).to eq 'test-1' expect(newres.field2).to eq 'test-2' expect(newres.field3).to eq 'test-3' end - expect(WebMock).to have_requested(:get, req_url(Mock::Resource.site, "/mock/resources/1")).twice + expect(WebMock).to have_requested(:get, req_url(Mock::ResourceWithCache.site, "/mock/resource_with_caches/1")).twice end it 'should refresh after max-age seconds' do stub_auth - req = stub_request(:get, req_url(Mock::Resource.site, "/mock/resources/1")). + req = stub_request(:get, req_url(Mock::ResourceWithCache.site, "/mock/resource_with_caches/1")). to_return(status: 200, body: { id: 1, field1: 'field-1', @@ -177,13 +177,13 @@ } ) - res = Mock::Resource.find(1) - expect(res).to be_a Mock::Resource + res = Mock::ResourceWithCache.find(1) + expect(res).to be_a Mock::ResourceWithCache remove_request_stub(req) - expect(WebMock).to have_requested(:get, req_url(Mock::Resource.site, "/mock/resources/1")).once + expect(WebMock).to have_requested(:get, req_url(Mock::ResourceWithCache.site, "/mock/resource_with_caches/1")).once - stub_request(:get, req_url(Mock::Resource.site, "/mock/resources/1")). + stub_request(:get, req_url(Mock::ResourceWithCache.site, "/mock/resource_with_caches/1")). with(headers: {'If-None-Match' => 'abc123'}). to_return(status: 304, body: "{}", headers: { @@ -191,27 +191,27 @@ 'Cache-Control' => 'max-age=600, must-revalidate' } ) - + # no request Timecop.travel(590.seconds.from_now) do - res = Mock::Resource.find(1) - expect(res).to be_a Mock::Resource + res = Mock::ResourceWithCache.find(1) + expect(res).to be_a Mock::ResourceWithCache end - expect(WebMock).to have_requested(:get, req_url(Mock::Resource.site, "/mock/resources/1")).once + expect(WebMock).to have_requested(:get, req_url(Mock::ResourceWithCache.site, "/mock/resource_with_caches/1")).once # do request Timecop.travel(610.seconds.from_now) do - res = Mock::Resource.find(1) - expect(res).to be_a Mock::Resource + res = Mock::ResourceWithCache.find(1) + expect(res).to be_a Mock::ResourceWithCache end - expect(WebMock).to have_requested(:get, req_url(Mock::Resource.site, "/mock/resources/1")).twice + expect(WebMock).to have_requested(:get, req_url(Mock::ResourceWithCache.site, "/mock/resource_with_caches/1")).twice end it 'should refresh every request no-cache resources' do stub_auth - req = stub_request(:get, req_url(Mock::Resource.site, "/mock/resources/1")). + req = stub_request(:get, req_url(Mock::ResourceWithCache.site, "/mock/resource_with_caches/1")). to_return(status: 200, body: { id: 1, field1: 'field-1', @@ -224,13 +224,13 @@ } ) - res = Mock::Resource.find(1) - expect(res).to be_a Mock::Resource + res = Mock::ResourceWithCache.find(1) + expect(res).to be_a Mock::ResourceWithCache remove_request_stub(req) - expect(WebMock).to have_requested(:get, req_url(Mock::Resource.site, "/mock/resources/1")).once + expect(WebMock).to have_requested(:get, req_url(Mock::ResourceWithCache.site, "/mock/resource_with_caches/1")).once - stub_request(:get, req_url(Mock::Resource.site, "/mock/resources/1")). + stub_request(:get, req_url(Mock::ResourceWithCache.site, "/mock/resource_with_caches/1")). with(headers: {'If-None-Match' => 'abc123'}). to_return(status: 304, body: "{}", headers: { @@ -238,27 +238,27 @@ 'Cache-Control' => 'max-age=600, must-revalidate' } ) - + # no request Timecop.travel(10.seconds.from_now) do - res = Mock::Resource.find(1) - expect(res).to be_a Mock::Resource + res = Mock::ResourceWithCache.find(1) + expect(res).to be_a Mock::ResourceWithCache end - expect(WebMock).to have_requested(:get, req_url(Mock::Resource.site, "/mock/resources/1")).twice + expect(WebMock).to have_requested(:get, req_url(Mock::ResourceWithCache.site, "/mock/resource_with_caches/1")).twice # do request Timecop.travel(60.seconds.from_now) do - res = Mock::Resource.find(1) - expect(res).to be_a Mock::Resource + res = Mock::ResourceWithCache.find(1) + expect(res).to be_a Mock::ResourceWithCache end - expect(WebMock).to have_requested(:get, req_url(Mock::Resource.site, "/mock/resources/1")).at_least_times(3) + expect(WebMock).to have_requested(:get, req_url(Mock::ResourceWithCache.site, "/mock/resource_with_caches/1")).at_least_times(3) end it 'does not cache no-store resources' do stub_auth - req = stub_request(:get, req_url(Mock::Resource.site, "/mock/resources/1")). + req = stub_request(:get, req_url(Mock::ResourceWithCache.site, "/mock/resource_with_caches/1")). to_return(status: 200, body: { id: 1, field1: 'field-1', @@ -271,13 +271,13 @@ } ) - res = Mock::Resource.find(1) - expect(res).to be_a Mock::Resource + res = Mock::ResourceWithCache.find(1) + expect(res).to be_a Mock::ResourceWithCache remove_request_stub(req) - expect(WebMock).to have_requested(:get, req_url(Mock::Resource.site, "/mock/resources/1")).once + expect(WebMock).to have_requested(:get, req_url(Mock::ResourceWithCache.site, "/mock/resource_with_caches/1")).once - stub_request(:get, req_url(Mock::Resource.site, "/mock/resources/1")). + stub_request(:get, req_url(Mock::ResourceWithCache.site, "/mock/resource_with_caches/1")). with { |request| hash_excluding("If-None-Match" => "abc123") === request.headers }. to_return(status: 200, body: { id: 1, @@ -290,27 +290,27 @@ 'Cache-Control' => 'max-age=600, no-store' } ) - + # no request Timecop.travel(10.seconds.from_now) do - res = Mock::Resource.find(1) - expect(res).to be_a Mock::Resource + res = Mock::ResourceWithCache.find(1) + expect(res).to be_a Mock::ResourceWithCache end - expect(WebMock).to have_requested(:get, req_url(Mock::Resource.site, "/mock/resources/1")).twice + expect(WebMock).to have_requested(:get, req_url(Mock::ResourceWithCache.site, "/mock/resource_with_caches/1")).twice # do request Timecop.travel(60.seconds.from_now) do - res = Mock::Resource.find(1) - expect(res).to be_a Mock::Resource + res = Mock::ResourceWithCache.find(1) + expect(res).to be_a Mock::ResourceWithCache end - expect(WebMock).to have_requested(:get, req_url(Mock::Resource.site, "/mock/resources/1")).at_least_times(3) + expect(WebMock).to have_requested(:get, req_url(Mock::ResourceWithCache.site, "/mock/resource_with_caches/1")).at_least_times(3) end - + it 'force revalidate resource' do stub_auth - req = stub_request(:get, req_url(Mock::Resource.site, "/mock/resources/1")). + req = stub_request(:get, req_url(Mock::ResourceWithCache.site, "/mock/resource_with_caches/1")). to_return(status: 200, body: { id: 1, field1: 'field-1', @@ -323,13 +323,13 @@ } ) - res = Mock::Resource.find(1) - expect(res).to be_a Mock::Resource + res = Mock::ResourceWithCache.find(1) + expect(res).to be_a Mock::ResourceWithCache remove_request_stub(req) - expect(WebMock).to have_requested(:get, req_url(Mock::Resource.site, "/mock/resources/1")).once + expect(WebMock).to have_requested(:get, req_url(Mock::ResourceWithCache.site, "/mock/resource_with_caches/1")).once - stub_request(:get, req_url(Mock::Resource.site, "/mock/resources/1")). + stub_request(:get, req_url(Mock::ResourceWithCache.site, "/mock/resource_with_caches/1")). with(headers: {'If-None-Match' => 'abc123'}). to_return(status: 304, body: "{}", headers: { @@ -337,16 +337,16 @@ 'Cache-Control' => 'max-age=600, must-revalidate' } ) - - res = Mock::Resource.find(1, revalidate: true) - expect(res).to be_a Mock::Resource - expect(WebMock).to have_requested(:get, req_url(Mock::Resource.site, "/mock/resources/1")).twice + res = Mock::ResourceWithCache.find(1, revalidate: true) + expect(res).to be_a Mock::ResourceWithCache + + expect(WebMock).to have_requested(:get, req_url(Mock::ResourceWithCache.site, "/mock/resource_with_caches/1")).twice end it '#reload() forces revalidation of resource' do stub_auth - req = stub_request(:get, req_url(Mock::Resource.site, "/mock/resources/1")). + req = stub_request(:get, req_url(Mock::ResourceWithCache.site, "/mock/resource_with_caches/1")). to_return(status: 200, body: { id: 1, field1: 'field-1', @@ -359,13 +359,13 @@ } ) - res = Mock::Resource.find(1) - expect(res).to be_a Mock::Resource + res = Mock::ResourceWithCache.find(1) + expect(res).to be_a Mock::ResourceWithCache remove_request_stub(req) - expect(WebMock).to have_requested(:get, req_url(Mock::Resource.site, "/mock/resources/1")).once + expect(WebMock).to have_requested(:get, req_url(Mock::ResourceWithCache.site, "/mock/resource_with_caches/1")).once - stub_request(:get, req_url(Mock::Resource.site, "/mock/resources/1")). + stub_request(:get, req_url(Mock::ResourceWithCache.site, "/mock/resource_with_caches/1")). with(headers: {'If-None-Match' => 'abc123'}). to_return(status: 304, body: "{}", headers: { @@ -373,16 +373,16 @@ 'Cache-Control' => 'max-age=600, must-revalidate' } ) - + res = res.reload - expect(res).to be_a Mock::Resource + expect(res).to be_a Mock::ResourceWithCache - expect(WebMock).to have_requested(:get, req_url(Mock::Resource.site, "/mock/resources/1")).twice + expect(WebMock).to have_requested(:get, req_url(Mock::ResourceWithCache.site, "/mock/resource_with_caches/1")).twice end it '::without_cache { }' do stub_auth - req = stub_request(:get, req_url(Mock::Resource.site, "/mock/resources/1")). + req = stub_request(:get, req_url(Mock::ResourceWithCache.site, "/mock/resource_with_caches/1")). to_return(status: 200, body: { id: 1, field1: 'field-1', @@ -395,22 +395,22 @@ } ) - res = Mock::Resource.without_cache do - Mock::Resource.find(1) + res = Mock::ResourceWithCache.without_cache do + Mock::ResourceWithCache.find(1) end - expect(res).to be_a Mock::Resource + expect(res).to be_a Mock::ResourceWithCache - expect(WebMock).to have_requested(:get, req_url(Mock::Resource.site, "/mock/resources/1")).once + expect(WebMock).to have_requested(:get, req_url(Mock::ResourceWithCache.site, "/mock/resource_with_caches/1")).once - res = Mock::Resource.find(1) - expect(res).to be_a Mock::Resource + res = Mock::ResourceWithCache.find(1) + expect(res).to be_a Mock::ResourceWithCache - expect(WebMock).to have_requested(:get, req_url(Mock::Resource.site, "/mock/resources/1")).twice + expect(WebMock).to have_requested(:get, req_url(Mock::ResourceWithCache.site, "/mock/resource_with_caches/1")).twice end it '#save() forces revalidation of resource' do stub_auth - req = stub_request(:get, req_url(Mock::Resource.site, "/mock/resources")). + req = stub_request(:get, req_url(Mock::ResourceWithCache.site, "/mock/resource_with_caches")). to_return(status: 200, body: { id: 1, field1: 'field-1', @@ -423,13 +423,13 @@ } ) - res = Mock::Resource.first - expect(res).to be_a Mock::Resource + res = Mock::ResourceWithCache.first + expect(res).to be_a Mock::ResourceWithCache remove_request_stub(req) - expect(WebMock).to have_requested(:get, req_url(Mock::Resource.site, "/mock/resources")).once + expect(WebMock).to have_requested(:get, req_url(Mock::ResourceWithCache.site, "/mock/resource_with_caches")).once - stub_request(:put, req_url(Mock::Resource.site, "/mock/resources/1")). + stub_request(:put, req_url(Mock::ResourceWithCache.site, "/mock/resource_with_caches/1")). to_return(status: 200, body: { id: 1, field1: 'field-1', @@ -442,7 +442,7 @@ } ) - stub_request(:get, req_url(Mock::Resource.site, "/mock/resources")). + stub_request(:get, req_url(Mock::ResourceWithCache.site, "/mock/resource_with_caches")). with(headers: {'If-None-Match' => 'abc123'}). to_return(status: 304, body: "{}", headers: { @@ -450,17 +450,17 @@ 'Cache-Control' => 'max-age=600, must-revalidate' } ) - + res.save() - res = Mock::Resource.first - expect(res).to be_a Mock::Resource + res = Mock::ResourceWithCache.first + expect(res).to be_a Mock::ResourceWithCache - expect(WebMock).to have_requested(:get, req_url(Mock::Resource.site, "/mock/resources")).twice + expect(WebMock).to have_requested(:get, req_url(Mock::ResourceWithCache.site, "/mock/resource_with_caches")).twice end it '#destroy() forces revalidation of resource' do stub_auth - req = stub_request(:get, req_url(Mock::Resource.site, "/mock/resources")). + req = stub_request(:get, req_url(Mock::ResourceWithCache.site, "/mock/resource_with_caches")). to_return(status: 200, body: { id: 1, field1: 'field-1', @@ -473,17 +473,17 @@ } ) - res = Mock::Resource.first - expect(res).to be_a Mock::Resource + res = Mock::ResourceWithCache.first + expect(res).to be_a Mock::ResourceWithCache remove_request_stub(req) - expect(WebMock).to have_requested(:get, req_url(Mock::Resource.site, "/mock/resources")).once + expect(WebMock).to have_requested(:get, req_url(Mock::ResourceWithCache.site, "/mock/resource_with_caches")).once - stub_request(:delete, req_url(Mock::Resource.site, "/mock/resources/1")). + stub_request(:delete, req_url(Mock::ResourceWithCache.site, "/mock/resource_with_caches/1")). to_return(status: 200, body: { }.to_json ) - stub_request(:get, req_url(Mock::Resource.site, "/mock/resources")). + stub_request(:get, req_url(Mock::ResourceWithCache.site, "/mock/resource_with_caches")). with(headers: {'If-None-Match' => 'abc123'}). to_return(status: 304, body: "{}", headers: { @@ -491,17 +491,17 @@ 'Cache-Control' => 'max-age=600, must-revalidate' } ) - + res.destroy() - res = Mock::Resource.first - expect(res).to be_a Mock::Resource + res = Mock::ResourceWithCache.first + expect(res).to be_a Mock::ResourceWithCache - expect(WebMock).to have_requested(:get, req_url(Mock::Resource.site, "/mock/resources")).twice + expect(WebMock).to have_requested(:get, req_url(Mock::ResourceWithCache.site, "/mock/resource_with_caches")).twice end it '::post() forces revalidation of resource' do stub_auth - req = stub_request(:get, req_url(Mock::Resource.site, "/mock/resources")). + req = stub_request(:get, req_url(Mock::ResourceWithCache.site, "/mock/resource_with_caches")). to_return(status: 200, body: { id: 1, field1: 'field-1', @@ -514,18 +514,18 @@ } ) - res = Mock::Resource.first - expect(res).to be_a Mock::Resource + res = Mock::ResourceWithCache.first + expect(res).to be_a Mock::ResourceWithCache remove_request_stub(req) - expect(WebMock).to have_requested(:get, req_url(Mock::Resource.site, "/mock/resources")).once + expect(WebMock).to have_requested(:get, req_url(Mock::ResourceWithCache.site, "/mock/resource_with_caches")).once - stub_request(:post, req_url(Mock::Resource.site, "/mock/resources/test")). + stub_request(:post, req_url(Mock::ResourceWithCache.site, "/mock/resource_with_caches/test")). to_return(status: 200, body: { }.to_json ) - Mock::Resource.post('test', {}) + Mock::ResourceWithCache.post('test', {}) - stub_request(:get, req_url(Mock::Resource.site, "/mock/resources")). + stub_request(:get, req_url(Mock::ResourceWithCache.site, "/mock/resource_with_caches")). with(headers: {'If-None-Match' => 'abc123'}). to_return(status: 304, body: "{}", headers: { @@ -533,16 +533,16 @@ 'Cache-Control' => 'max-age=600, must-revalidate' } ) - - res = Mock::Resource.first - expect(res).to be_a Mock::Resource - expect(WebMock).to have_requested(:get, req_url(Mock::Resource.site, "/mock/resources")).twice + res = Mock::ResourceWithCache.first + expect(res).to be_a Mock::ResourceWithCache + + expect(WebMock).to have_requested(:get, req_url(Mock::ResourceWithCache.site, "/mock/resource_with_caches")).twice end it '::put() forces revalidation of resource' do stub_auth - req = stub_request(:get, req_url(Mock::Resource.site, "/mock/resources")). + req = stub_request(:get, req_url(Mock::ResourceWithCache.site, "/mock/resource_with_caches")). to_return(status: 200, body: { id: 1, field1: 'field-1', @@ -555,18 +555,18 @@ } ) - res = Mock::Resource.first - expect(res).to be_a Mock::Resource + res = Mock::ResourceWithCache.first + expect(res).to be_a Mock::ResourceWithCache remove_request_stub(req) - expect(WebMock).to have_requested(:get, req_url(Mock::Resource.site, "/mock/resources")).once + expect(WebMock).to have_requested(:get, req_url(Mock::ResourceWithCache.site, "/mock/resource_with_caches")).once - stub_request(:put, req_url(Mock::Resource.site, "/mock/resources/test")). + stub_request(:put, req_url(Mock::ResourceWithCache.site, "/mock/resource_with_caches/test")). to_return(status: 200, body: { }.to_json ) - Mock::Resource.put('test', {}) + Mock::ResourceWithCache.put('test', {}) - stub_request(:get, req_url(Mock::Resource.site, "/mock/resources")). + stub_request(:get, req_url(Mock::ResourceWithCache.site, "/mock/resource_with_caches")). with(headers: {'If-None-Match' => 'abc123'}). to_return(status: 304, body: "{}", headers: { @@ -574,16 +574,16 @@ 'Cache-Control' => 'max-age=600, must-revalidate' } ) - - res = Mock::Resource.first - expect(res).to be_a Mock::Resource - expect(WebMock).to have_requested(:get, req_url(Mock::Resource.site, "/mock/resources")).twice + res = Mock::ResourceWithCache.first + expect(res).to be_a Mock::ResourceWithCache + + expect(WebMock).to have_requested(:get, req_url(Mock::ResourceWithCache.site, "/mock/resource_with_caches")).twice end it '::patch() forces revalidation of resource' do stub_auth - req = stub_request(:get, req_url(Mock::Resource.site, "/mock/resources")). + req = stub_request(:get, req_url(Mock::ResourceWithCache.site, "/mock/resource_with_caches")). to_return(status: 200, body: { id: 1, field1: 'field-1', @@ -596,18 +596,18 @@ } ) - res = Mock::Resource.first - expect(res).to be_a Mock::Resource + res = Mock::ResourceWithCache.first + expect(res).to be_a Mock::ResourceWithCache remove_request_stub(req) - expect(WebMock).to have_requested(:get, req_url(Mock::Resource.site, "/mock/resources")).once + expect(WebMock).to have_requested(:get, req_url(Mock::ResourceWithCache.site, "/mock/resource_with_caches")).once - stub_request(:patch, req_url(Mock::Resource.site, "/mock/resources/test")). + stub_request(:patch, req_url(Mock::ResourceWithCache.site, "/mock/resource_with_caches/test")). to_return(status: 200, body: { }.to_json ) - Mock::Resource.patch('test', {}) + Mock::ResourceWithCache.patch('test', {}) - stub_request(:get, req_url(Mock::Resource.site, "/mock/resources")). + stub_request(:get, req_url(Mock::ResourceWithCache.site, "/mock/resource_with_caches")). with(headers: {'If-None-Match' => 'abc123'}). to_return(status: 304, body: "{}", headers: { @@ -615,16 +615,16 @@ 'Cache-Control' => 'max-age=600, must-revalidate' } ) - - res = Mock::Resource.first - expect(res).to be_a Mock::Resource - expect(WebMock).to have_requested(:get, req_url(Mock::Resource.site, "/mock/resources")).twice + res = Mock::ResourceWithCache.first + expect(res).to be_a Mock::ResourceWithCache + + expect(WebMock).to have_requested(:get, req_url(Mock::ResourceWithCache.site, "/mock/resource_with_caches")).twice end it '::delete() forces revalidation of resource' do stub_auth - req = stub_request(:get, req_url(Mock::Resource.site, "/mock/resources")). + req = stub_request(:get, req_url(Mock::ResourceWithCache.site, "/mock/resource_with_caches")). to_return(status: 200, body: { id: 1, field1: 'field-1', @@ -637,18 +637,18 @@ } ) - res = Mock::Resource.first - expect(res).to be_a Mock::Resource + res = Mock::ResourceWithCache.first + expect(res).to be_a Mock::ResourceWithCache remove_request_stub(req) - expect(WebMock).to have_requested(:get, req_url(Mock::Resource.site, "/mock/resources")).once + expect(WebMock).to have_requested(:get, req_url(Mock::ResourceWithCache.site, "/mock/resource_with_caches")).once - stub_request(:delete, req_url(Mock::Resource.site, "/mock/resources/test")). + stub_request(:delete, req_url(Mock::ResourceWithCache.site, "/mock/resource_with_caches/test")). to_return(status: 200, body: { }.to_json ) - Mock::Resource.delete('test', {}) + Mock::ResourceWithCache.delete('test', {}) - stub_request(:get, req_url(Mock::Resource.site, "/mock/resources")). + stub_request(:get, req_url(Mock::ResourceWithCache.site, "/mock/resource_with_caches")). with(headers: {'If-None-Match' => 'abc123'}). to_return(status: 304, body: "{}", headers: { @@ -656,11 +656,10 @@ 'Cache-Control' => 'max-age=600, must-revalidate' } ) - - res = Mock::Resource.first - expect(res).to be_a Mock::Resource - expect(WebMock).to have_requested(:get, req_url(Mock::Resource.site, "/mock/resources")).twice + res = Mock::ResourceWithCache.first + expect(res).to be_a Mock::ResourceWithCache + + expect(WebMock).to have_requested(:get, req_url(Mock::ResourceWithCache.site, "/mock/resource_with_caches")).twice end end -
View file
activeresource-hel-0.7.1.gem/data/spec/integration/resource/collection_spec.rb
Added
@@ -0,0 +1,183 @@ +require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +require 'webmock/rspec' + +module Mock + class Resource < ActiveResource::Hel::Resource + self.cached = false + end +end + +describe "A HEL Resource Collection" do + def req_url(site, path) + uri = URI(site.to_s) + uri.path = path + uri.to_s + end + + def stub_auth + stub_request(:post, req_url(Mock::Resource.site, "/ygg/session/authenticate_by_fqda_and_password")). + with( + body: {fqda: Mock::Resource.hel_identity, password: Mock::Resource.hel_secret} + ).to_return( + status: 200, + body: { + "id":"089af4a6-d558-40f1-86c4-c4ca3bf03815", + "authenticated":true, + "status":"authenticated", + "auth_identity": Mock::Resource.hel_identity, + "msg":"Authentication successful!", + "reason":"ok" + }.to_json + ) + end + + describe "collection metadata" do + it 'should extract pagination metadata from headers' do + stub_auth + stub_request(:get, req_url(Mock::Resource.site, "/mock/resources")). + to_return( + status: 200, + body: + {id: 1, name: 'Resource 1'}, + {id: 2, name: 'Resource 2'} + .to_json, + headers: { + 'X-Total-Resources-Count' => '100', + 'X-Resources-Offset' => '0', + 'X-Resources-Limit' => '20' + } + ) + + collection = Mock::Resource.find(:all) + + expect(collection).to be_a ActiveResource::Hel::Collection + expect(collection.total_count).to eq 100 + expect(collection.offset_value).to eq 0 + expect(collection.limit_value).to eq 20 + end + + it 'should handle missing pagination headers' do + stub_auth + stub_request(:get, req_url(Mock::Resource.site, "/mock/resources")). + to_return( + status: 200, + body: + {id: 1, name: 'Resource 1'} + .to_json, + headers: {} + ) + + collection = Mock::Resource.find(:all) + + expect(collection).to be_a ActiveResource::Hel::Collection + expect(collection.total_count).to eq collection.count + end + + # doesn't work + # collection.rb:30-34 + # why? dunno, its is undocumented so... who knows? + xit 'should support limit method' do + stub_auth + stub_request(:get, req_url(Mock::Resource.site, "/mock/resources")). + to_return( + status: 200, + body: (1..50).map { |i| {id: i, name: "Resource #{i}"} }.to_json, + headers: { + 'X-Total-Resources-Count' => '50', + 'X-Resources-Offset' => '0', + 'X-Resources-Limit' => '50' + } + ) + + collection = Mock::Resource.find(:all) + limited = collection.limit(10) + + expect(limited.limit_value).to eq 10 + expect(limited.count).to eq 10 + end + + it 'should support offset method' do + stub_auth + stub_request(:get, req_url(Mock::Resource.site, "/mock/resources")). + to_return( + status: 200, + body: (1..50).map { |i| {id: i, name: "Resource #{i}"} }.to_json, + headers: { + 'X-Total-Resources-Count' => '50', + 'X-Resources-Offset' => '0', + 'X-Resources-Limit' => '50' + } + ) + + collection = Mock::Resource.find(:all) + offset = collection.offset(10) + + expect(offset.offset_value).to eq 10 + end + + it 'should be enumerable' do + stub_auth + stub_request(:get, req_url(Mock::Resource.site, "/mock/resources")). + to_return( + status: 200, + body: + {id: 1, name: 'Resource 1'}, + {id: 2, name: 'Resource 2'}, + {id: 3, name: 'Resource 3'} + .to_json, + headers: { + 'X-Total-Resources-Count' => '3', + 'X-Resources-Offset' => '0', + 'X-Resources-Limit' => '3' + } + ) + + collection = Mock::Resource.find(:all) + + expect(collection).to respond_to(:each) + expect(collection.map(&:id)).to eq 1, 2, 3 + end + + it 'should support first method' do + stub_auth + stub_request(:get, req_url(Mock::Resource.site, "/mock/resources")). + to_return( + status: 200, + body: + {id: 1, name: 'Resource 1'}, + {id: 2, name: 'Resource 2'} + .to_json, + headers: { + 'X-Total-Resources-Count' => '2', + 'X-Resources-Offset' => '0', + 'X-Resources-Limit' => '2' + } + ) + + collection = Mock::Resource.find(:all) + + expect(collection.first).to be_a Mock::Resource + expect(collection.first.id).to eq 1 + end + + it 'should preserve model class information' do + stub_auth + stub_request(:get, req_url(Mock::Resource.site, "/mock/resources")). + to_return( + status: 200, + body: + {id: 1, name: 'Resource 1'} + .to_json, + headers: { + 'X-Total-Resources-Count' => '1', + 'X-Resources-Offset' => '0', + 'X-Resources-Limit' => '1' + } + ) + + collection = Mock::Resource.find(:all) + + expect(collection.model).to eq Mock::Resource + end + end +end
View file
activeresource-hel-0.7.1.gem/data/spec/integration/resource/dirty_spec.rb
Added
@@ -0,0 +1,155 @@ +require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +require 'webmock/rspec' + +module Mock + class Resource < ActiveResource::Hel::Resource + self.cached = false + end +end + +describe "A HEL Resource with Dirty Tracking" do + def req_url(site, path) + uri = URI(site.to_s) + uri.path = path + uri.to_s + end + + def stub_auth + stub_request(:post, req_url(Mock::Resource.site, "/ygg/session/authenticate_by_fqda_and_password")). + with( + body: {fqda: Mock::Resource.hel_identity, password: Mock::Resource.hel_secret} + ).to_return( + status: 200, + body: { + "id":"089af4a6-d558-40f1-86c4-c4ca3bf03815", + "authenticated":true, + "status":"authenticated", + "auth_identity": Mock::Resource.hel_identity, + "msg":"Authentication successful!", + "reason":"ok" + }.to_json + ) + end + + describe "tracking changes" do + it 'should track attribute changes' do + stub_auth + stub_request(:get, req_url(Mock::Resource.site, "/mock/resources/1")). + to_return( + status: 200, + body: {id: 1, name: 'Original', email: 'original@example.com'}.to_json, + headers: {} + ) + + resource = Mock::Resource.find(1) + expect(resource.changed?).to be_falsey + + resource.name = 'Changed' + expect(resource.changed?).to be_truthy + expect(resource.name_changed?).to be_truthy + expect(resource.name_was).to eq 'Original' + expect(resource.changes).to include('name') + end + + it 'should track multiple attribute changes' do + stub_auth + stub_request(:get, req_url(Mock::Resource.site, "/mock/resources/1")). + to_return( + status: 200, + body: {id: 1, name: 'Original', email: 'original@example.com'}.to_json, + headers: {} + ) + + resource = Mock::Resource.find(1) + resource.name = 'Changed' + resource.email = 'changed@example.com' + + expect(resource.changed).to include('name', 'email') + expect(resource.changes.keys).to include('name', 'email') + end + + it 'should clear changes after save' do + stub_auth + stub_request(:get, req_url(Mock::Resource.site, "/mock/resources/1")). + to_return( + status: 200, + body: {id: 1, name: 'Original', email: 'original@example.com'}.to_json, + headers: {} + ) + + resource = Mock::Resource.find(1) + resource.name = 'Changed' + + stub_request(:put, req_url(Mock::Resource.site, "/mock/resources/1")). + to_return( + status: 200, + body: {id: 1, name: 'Changed', email: 'original@example.com'}.to_json, + headers: {} + ) + + resource.save + + expect(resource.changed?).to be_falsey + expect(resource.previous_changes).to include('name') + end + + it 'should restore attributes' do + stub_auth + stub_request(:get, req_url(Mock::Resource.site, "/mock/resources/1")). + to_return( + status: 200, + body: {id: 1, name: 'Original', email: 'original@example.com'}.to_json, + headers: {} + ) + + resource = Mock::Resource.find(1) + original_name = resource.name + resource.name = 'Changed' + + resource.restore_attributes + + expect(resource.name).to eq original_name + expect(resource.changed?).to be_falsey + end + + it 'should restore specific attributes' do + stub_auth + stub_request(:get, req_url(Mock::Resource.site, "/mock/resources/1")). + to_return( + status: 200, + body: {id: 1, name: 'Original', email: 'original@example.com'}.to_json, + headers: {} + ) + + resource = Mock::Resource.find(1) + original_name = resource.name + resource.name = 'Changed' + resource.email = 'changed@example.com' + + resource.restore_attributes('name') + + expect(resource.name).to eq original_name + expect(resource.email).to eq 'changed@example.com' + end + end + + describe "dirty serialization" do + it 'should serialize only changed attributes' do + stub_auth + stub_request(:get, req_url(Mock::Resource.site, "/mock/resources/1")). + to_return( + status: 200, + body: {id: 1, name: 'Original', email: 'original@example.com', status: 'active'}.to_json, + headers: {} + ) + + resource = Mock::Resource.find(1) + resource.name = 'Changed' + + serialized = resource.serializable_hash(dirty: true) + + expect(serialized.keys).to include('id', 'name') + expect(serialized.keys).not_to include('email', 'status') + end + end +end
View file
activeresource-hel-0.7.1.gem/data/spec/integration/resource/embedded_resources_spec.rb
Added
@@ -0,0 +1,231 @@ +require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +require 'webmock/rspec' + +module Mock + class ResourceWithEmbedded < ActiveResource::Hel::Resource + self.cached = false + self.load_remote_schema = true + + end +end + +describe "A HEL Resource with Embedded Resources" do + def req_url(site, path) + uri = URI(site.to_s) + uri.path = path + uri.to_s + end + + def stub_auth + stub_request(:post, req_url(Mock::ResourceWithEmbedded.site, "/ygg/session/authenticate_by_fqda_and_password")). + with( + body: {fqda: Mock::ResourceWithEmbedded.hel_identity, password: Mock::ResourceWithEmbedded.hel_secret} + ).to_return( + status: 200, + body: { + "id":"089af4a6-d558-40f1-86c4-c4ca3bf03815", + "authenticated":true, + "status":"authenticated", + "auth_identity": Mock::ResourceWithEmbedded.hel_identity, + "msg":"Authentication successful!", + "reason":"ok" + }.to_json + ) + end + + before :each do + stub_auth + stub_request(:get, req_url(Mock::ResourceWithEmbedded.site, "/mock/resource_with_embeddeds/schema")). + to_return( + status: 200, + body: { + type: 'Mock::ResourceWithEmbedded', + attrs: { + id: { + type: "integer", + human_name: "", + notnull: true, + writable: false, + readable: true, + }, + name: { + type: "string", + human_name: "", + notnull: true, + writable: true, + readable: true, + }, + item: { + type: "embedded_model", + human_name: "", + writable: true, + readable: true, + class_name: "Mock::EmbeddedItem", + schema: { + type:"Mock::EmbeddedItem", + attrs: { + id:{ + type: "integer", + human_name: "", + notnull: true, + writable: false, + readable: true + }, + name: { + type: "string", + human_name: "", + notnull: true, + writable: true, + readable: true, + }, + } + } + }, + items: { + type: "uniform_models_collection", + human_name: "", + writable: true, + readable: true, + class_name: "Mock::EmbeddedItem", + schema: { + type:"Mock::EmbeddedItem", + attrs: { + id:{ + type: "integer", + human_name: "", + notnull: true, + writable: false, + readable: true + }, + name: { + type: "string", + human_name: "", + notnull: true, + writable: true, + readable: true, + }, + } + } + } + } + }.to_json, + headers: {} + ) + end + + describe "embedded resources" do + before :each do + stub_request(:get, req_url(Mock::ResourceWithEmbedded.site, "/mock/resource_with_embeddeds/1")). + to_return( + status: 200, + body: { + id: 1, + name: 'Parent', + item: { + _type: 'Mock::EmbeddedItem', + id: 10, + name: 'Embedded Item' + } + }.to_json, + headers: {} + ) + end + + it 'should load embedded resources with _type' do + resource = Mock::ResourceWithEmbedded.find(1) + expect(resource.item).to be_a Mock::EmbeddedItem + expect(resource.item.id).to eq 10 + expect(resource.item.name).to eq 'Embedded Item' + end + + # this should not call DELETE /path/to/resource/id !!! + xit 'should mark embedded resources as destroyed' do + resource = Mock::ResourceWithEmbedded.find(1) + expect(resource.item.destroyed?).to be_falsey + resource.item.destroy + expect(resource.item.destroyed?).to be_truthy + end + + # this should not call PUT /path/to/resource/id !!! + xit 'should update embedded resource attributes' do + resource = Mock::ResourceWithEmbedded.find(1) + resource.item.update_attribute(:name, 'Updated') + expect(resource.item.name).to eq 'Updated' + end + + # this should not call PUT /path/to/resource/id !!! + xit 'should update multiple embedded resource attributes' do + resource = Mock::ResourceWithEmbedded.find(1) + resource.item.update_attributes(name: 'Updated', status: 'inactive') + expect(resource.item.name).to eq 'Updated' + expect(resource.item.status).to eq 'inactive' + end + end + + describe "embedded collections" do + + before :each do + stub_request(:get, req_url(Mock::ResourceWithEmbedded.site, "/mock/resource_with_embeddeds/1")). + to_return( + status: 200, + body: { + id: 1, + name: 'Parent', + items: + {_type: 'Mock::EmbeddedItem', id: 1, name: 'Item 1'}, + {_type: 'Mock::EmbeddedItem', id: 2, name: 'Item 2'} + + }.to_json, + headers: {} + ) + end + + it 'should load embedded collections' do + resource = Mock::ResourceWithEmbedded.find(1) + expect(resource.items).to be_a ActiveResource::Hel::EmbeddedCollection + expect(resource.items.count).to eq 2 + expect(resource.items.first).to be_a Mock::EmbeddedItem + end + + it 'should add items to embedded collection' do + resource = Mock::ResourceWithEmbedded.find(1) + new_item = Mock::EmbeddedItem.new(name: 'New Item') + + resource.items << new_item + + expect(resource.items.count).to eq 3 + expect(resource.items.last).to eq new_item + end + + it 'should remove items from embedded collection' do + resource = Mock::ResourceWithEmbedded.find(1) + + resource.items.destroy(1) + + expect(resource.items.count).to eq 1 + expect(resource.items.destroyed).to include(1) + end + + it 'should serialize only changed embedded items' do + resource = Mock::ResourceWithEmbedded.find(1) + resource.items.first.name = 'Changed' + + serialized = resource.serializable_hash(dirty: true) + + expect(serialized'items'.length).to eq 1 + expect(serialized'items'.first'name').to eq 'Changed' + end + + it 'should serialize destroyed items with _destroy flag' do + resource = Mock::ResourceWithEmbedded.find(1) + resource.items.destroy(1) + + serialized = resource.serializable_hash + + destroyed_item = serialized'items'.find { |i| i'_destroy' } + expect(destroyed_item).to be_present + expect(destroyed_item'id').to eq 1 + expect(destroyed_item'_destroy').to be_truthy + end + end +end
View file
activeresource-hel-0.7.1.gem/data/spec/integration/resource/filters_spec.rb
Added
@@ -0,0 +1,171 @@ +require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +require 'webmock/rspec' + +module Mock + class Resource < ActiveResource::Hel::Resource + self.cached = false + end +end + +describe "A HEL Resource with Filters" do + def req_url(site, path, query = {}) + uri = URI(site.to_s) + + # Parse path to extract any existing query string + path_uri = URI.parse(path) + uri.path = path_uri.path + + # Merge existing query params from path with query hash + existing_query = {} + if path_uri.query + existing_query = URI.decode_www_form(path_uri.query).to_h + end + + merged_query = existing_query.merge(query.stringify_keys) + + if merged_query.any? + uri.query = URI.encode_www_form(merged_query) + end + + uri.to_s + end + + def stub_auth + stub_request(:post, req_url(Mock::Resource.site, "/ygg/session/authenticate_by_fqda_and_password")). + with( + body: {fqda: Mock::Resource.hel_identity, password: Mock::Resource.hel_secret} + ).to_return( + status: 200, + body: { + "id":"089af4a6-d558-40f1-86c4-c4ca3bf03815", + "authenticated":true, + "status":"authenticated", + "auth_identity": Mock::Resource.hel_identity, + "msg":"Authentication successful!", + "reason":"ok" + }.to_json + ) + end + + describe "filter DSL" do + it 'should build simple equality filter' do + stub_auth + filter = Mock::Resource.build_filter do + eq(status: 'active') + end + + expect(filter.to_json).to include('active') + expect(filter.to_json).to include('=') + end + + it 'should build comparison filters' do + stub_auth + filter = Mock::Resource.build_filter do + gt(age: 18) + lt(age: 65) + end + + json = filter.to_json + expect(json).to include('18') + expect(json).to include('65') + end + + it 'should build IN filter' do + stub_auth + filter = Mock::Resource.build_filter do + self.send(:in, status: 'active', 'pending', 'review') + end + + json = filter.to_json + expect(json).to include('active') + expect(json).to include('pending') + end + + it 'should build LIKE filter' do + stub_auth + filter = Mock::Resource.build_filter do + like(name: '%test%') + end + + json = filter.to_json + expect(json).to include('test') + expect(json).to include('ILIKE') + end + + it 'should build NULL checks' do + stub_auth + filter = Mock::Resource.build_filter do + is_null('deleted_at', 'archived_at') + end + + json = filter.to_json + expect(json).to include('deleted_at') + expect(json).to include('IS NULL') + end + + it 'should build AND conditions' do + stub_auth + filter = Mock::Resource.build_filter do + eq(status: 'active') + self.and do + gt(age: 18) + lt(age: 65) + end + end + + json = filter.to_json + expect(json).to include('AND') + end + + it 'should build OR conditions' do + stub_auth + filter = Mock::Resource.build_filter do + self.or do + eq(status: 'active') + eq(status: 'pending') + end + end + + json = filter.to_json + expect(json).to include('OR') + end + + it 'should use filter in find' do + stub_auth + filter = Mock::Resource.build_filter do + eq(status: 'active') + end + + stub_request(:get, req_url(Mock::Resource.site, "/mock/resources", {filter: filter.to_json})). + to_return(status: 200, body: .to_json, headers: {}) + + Mock::Resource.find(:all, params: {filter: filter}) + + expect(WebMock).to have_requested(:get, /filter=/) + end + + it 'should use filter proc in find' do + stub_auth + filter_proc = -> { + eq(status: 'active') + } + + stub_request(:get, /\/mock\/resources/). + to_return(status: 200, body: .to_json, headers: {}) + + Mock::Resource.find(:all, params: {filter: filter_proc}) + + expect(WebMock).to have_requested(:get, /filter=/) + end + + it 'should chain filters with set_filter' do + stub_auth + filter1 = Mock::Resource.build_filter { eq(status: 'active') } + filter2 = Mock::Resource.build_filter { gt(age: 18) } + + combined = Mock::Resource.scoped.set_filter(filter1).set_filter(filter2) + + expect(combined.filters).to be_present + end + end +end
View file
activeresource-hel-0.7.1.gem/data/spec/integration/resource/remote_pagination_spec.rb
Added
@@ -0,0 +1,161 @@ +require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +require 'webmock/rspec' + +module Mock + class Resource < ActiveResource::Hel::Resource + self.cached = false + end +end + +describe "A HEL Resource with Remote Pagination" do + def req_url(site, path, query = {}) + uri = URI(site.to_s) + + # Parse path to extract any existing query string + path_uri = URI.parse(path) + uri.path = path_uri.path + + # Merge existing query params from path with query hash + existing_query = {} + if path_uri.query + existing_query = URI.decode_www_form(path_uri.query).to_h + end + + merged_query = existing_query.merge(query.stringify_keys) + + if merged_query.any? + uri.query = URI.encode_www_form(merged_query) + end + + uri.to_s + end + + def stub_auth + stub_request(:post, req_url(Mock::Resource.site, "/ygg/session/authenticate_by_fqda_and_password")). + with( + body: {fqda: Mock::Resource.hel_identity, password: Mock::Resource.hel_secret} + ).to_return( + status: 200, + body: { + "id":"089af4a6-d558-40f1-86c4-c4ca3bf03815", + "authenticated":true, + "status":"authenticated", + "auth_identity": Mock::Resource.hel_identity, + "msg":"Authentication successful!", + "reason":"ok" + }.to_json + ) + end + + describe "pagination" do + it 'should paginate resources' do + stub_auth + stub_request(:get, req_url(Mock::Resource.site, "/mock/resources", {start: 0, limit: 20})). + to_return( + status: 200, + body: + {id: 1, name: 'Resource 1'}, + {id: 2, name: 'Resource 2'} + .to_json, + headers: { + 'X-Total-Resources-Count' => '50', + 'X-Resources-Offset' => '0', + 'X-Resources-Limit' => '20' + } + ) + + collection = Mock::Resource.paginate(1, 20).all + + expect(collection).to be_a ActiveResource::Hel::Collection + expect(collection.total_count).to eq 50 + expect(collection.offset_value).to eq 0 + expect(collection.limit_value).to eq 20 + expect(collection.count).to eq 2 + end + + it 'should paginate to second page' do + stub_auth + stub_request(:get, req_url(Mock::Resource.site, "/mock/resources", {start: 20, limit: 20})). + to_return( + status: 200, + body: + {id: 21, name: 'Resource 21'}, + {id: 22, name: 'Resource 22'} + .to_json, + headers: { + 'X-Total-Resources-Count' => '50', + 'X-Resources-Offset' => '20', + 'X-Resources-Limit' => '20' + } + ) + + collection = Mock::Resource.paginate(2, 20).all + + expect(collection.offset_value).to eq 20 + expect(collection.limit_value).to eq 20 + expect(WebMock).to have_requested(:get, req_url(Mock::Resource.site, "/mock/resources", {start: 20, limit: 20})) + end + + it 'should use default per_page from Kaminari' do + stub_auth + default_per_page = Kaminari.config.default_per_page + stub_request(:get, req_url(Mock::Resource.site, "/mock/resources", {start: 0, limit: default_per_page})). + to_return( + status: 200, + body: .to_json, + headers: { + 'X-Total-Resources-Count' => '0', + 'X-Resources-Offset' => '0', + 'X-Resources-Limit' => default_per_page.to_s + } + ) + + Mock::Resource.paginate(1).all + + expect(WebMock).to have_requested(:get, req_url(Mock::Resource.site, "/mock/resources", {start: 0, limit: default_per_page})) + end + + it 'should handle invalid page numbers' do + stub_auth + stub_request(:get, req_url(Mock::Resource.site, "/mock/resources", {start: 0, limit: 20})). + to_return( + status: 200, + body: .to_json, + headers: { + 'X-Total-Resources-Count' => '0', + 'X-Resources-Offset' => '0', + 'X-Resources-Limit' => '20' + } + ) + + Mock::Resource.paginate(0, 20).all + Mock::Resource.paginate(-1, 20).all + + expect(WebMock).to have_requested(:get, req_url(Mock::Resource.site, "/mock/resources", {start: 0, limit: 20})).twice + end + end + + describe "collection pagination methods" do + it 'should support Kaminari page method' do + stub_auth + stub_request(:get, req_url(Mock::Resource.site, "/mock/resources", {start: 0, limit: 20})). + to_return( + status: 200, + body: + {id: 1, name: 'Resource 1'} + .to_json, + headers: { + 'X-Total-Resources-Count' => '50', + 'X-Resources-Offset' => '0', + 'X-Resources-Limit' => '20' + } + ) + + collection = Mock::Resource.paginate(1, 20).all + page2 = collection.page(2) + + expect(page2).to be_a ActiveResource::Hel::Collection + expect(page2.offset_value).to eq 20 + end + end +end
View file
activeresource-hel-0.7.1.gem/data/spec/integration/resource/remote_schema_spec.rb
Added
@@ -0,0 +1,148 @@ +require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +require 'webmock/rspec' + +module Mock + class ResourceWithSchema < ActiveResource::Hel::Resource + self.cached = false + self.load_remote_schema = true + end +end + +describe "A HEL Resource with Remote Schema" do + def req_url(site, path) + uri = URI(site.to_s) + uri.path = path + uri.to_s + end + + def stub_auth + stub_request(:post, req_url(Mock::ResourceWithSchema.site, "/ygg/session/authenticate_by_fqda_and_password")). + with( + body: {fqda: Mock::ResourceWithSchema.hel_identity, password: Mock::ResourceWithSchema.hel_secret} + ).to_return( + status: 200, + body: { + "id":"089af4a6-d558-40f1-86c4-c4ca3bf03815", + "authenticated":true, + "status":"authenticated", + "auth_identity": Mock::ResourceWithSchema.hel_identity, + "msg":"Authentication successful!", + "reason":"ok" + }.to_json + ) + end + + before :each do + Mock::ResourceWithSchema.remote_schema = nil + end + + describe "schema loading" do + it 'should fetch remote schema' do + stub_auth + schema_response = { + "attrs" => { + "id" => {"type" => "integer"}, + "name" => {"type" => "string"}, + "email" => {"type" => "string"} + } + } + + stub_request(:get, req_url(Mock::ResourceWithSchema.site, "/mock/resource_with_schemas/schema")). + to_return(status: 200, body: schema_response.to_json, headers: {}) + + stub_request(:get, req_url(Mock::ResourceWithSchema.site, "/mock/resource_with_schemas")). + to_return(status: 200, body: .to_json, headers: {}) + + Mock::ResourceWithSchema.find(:first) + + expect(WebMock).to have_requested(:get, req_url(Mock::ResourceWithSchema.site, "/mock/resource_with_schemas/schema")) + expect(Mock::ResourceWithSchema.remote_schema).to eq schema_response + end + + it 'should process schema and define attributes' do + stub_auth + schema_response = { + "attrs" => { + "id" => {"type" => "integer"}, + "name" => {"type" => "string"}, + "email" => {"type" => "string"} + } + } + + stub_request(:get, req_url(Mock::ResourceWithSchema.site, "/mock/resource_with_schemas/schema")). + to_return(status: 200, body: schema_response.to_json, headers: {}) + + stub_request(:get, req_url(Mock::ResourceWithSchema.site, "/mock/resource_with_schemas")). + to_return(status: 200, body: .to_json, headers: {}) + + Mock::ResourceWithSchema.find(:all) + + expect(Mock::ResourceWithSchema.remote_schema).to eq schema_response + end + + it 'should handle schema with embedded collections' do + stub_auth + schema_response = { + "attrs" => { + "id" => {"type" => "integer"}, + "items" => { + "type" => "uniform_models_collection", + "schema" => { + "type" => "Mock::Item", + "attrs" => { + "id" => {"type" => "integer"}, + "name" => {"type" => "string"} + } + } + } + } + } + + stub_request(:get, req_url(Mock::ResourceWithSchema.site, "/mock/resource_with_schemas/schema")). + to_return(status: 200, body: schema_response.to_json, headers: {}) + + stub_request(:get, req_url(Mock::ResourceWithSchema.site, "/mock/resource_with_schemas")). + to_return(status: 200, body: .to_json, headers: {}) + + Mock::ResourceWithSchema.find(:all) + + expect(Mock::ResourceWithSchema.remote_schema).to eq schema_response + end + + it 'should handle schema with referenced collections' do + stub_auth + schema_response = { + "attrs" => { + "id" => {"type" => "integer"}, + "users" => { + "type" => "uniform_references_collection", + "referenced_class" => "Mock::User" + } + } + } + + stub_request(:get, req_url(Mock::ResourceWithSchema.site, "/mock/resource_with_schemas/schema")). + to_return(status: 200, body: schema_response.to_json, headers: {}) + + stub_request(:get, req_url(Mock::ResourceWithSchema.site, "/mock/resource_with_schemas")). + to_return(status: 200, body: .to_json, headers: {}) + + Mock::ResourceWithSchema.find(:all) + + expect(Mock::ResourceWithSchema.remote_schema).to eq schema_response + end + + it 'should handle missing schema gracefully' do + stub_auth + stub_request(:get, req_url(Mock::ResourceWithSchema.site, "/mock/resource_with_schemas/schema")). + to_return(status: 404, body: {}.to_json, headers: {}) + + stub_request(:get, req_url(Mock::ResourceWithSchema.site, "/mock/resource_with_schemas")). + to_return(status: 200, body: .to_json, headers: {}) + + Mock::ResourceWithSchema.find(:all) + + expect(Mock::ResourceWithSchema.remote_schema).to eq({}) + end + end +end
View file
activeresource-hel-0.7.1.gem/data/spec/integration/resource/resource_spec.rb
Added
@@ -0,0 +1,207 @@ +require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +require 'webmock/rspec' + +module Mock + class Resource < ActiveResource::Hel::Resource + self.cached = false + end +end + +describe "A HEL Resource" do + def req_url(site, path, query = {}) + uri = URI(site.to_s) + + # Parse path to extract any existing query string + path_uri = URI.parse(path) + uri.path = path_uri.path + + # Merge existing query params from path with query hash + existing_query = {} + if path_uri.query + existing_query = URI.decode_www_form(path_uri.query).to_h + end + + merged_query = existing_query.merge(query.stringify_keys) + + if merged_query.any? + uri.query = URI.encode_www_form(merged_query) + end + + uri.to_s + end + + def stub_auth + stub_request(:post, req_url(Mock::Resource.site, "/ygg/session/authenticate_by_fqda_and_password")). + with( + body: {fqda: Mock::Resource.hel_identity, password: Mock::Resource.hel_secret} + ).to_return( + status: 200, + body: { + "id":"089af4a6-d558-40f1-86c4-c4ca3bf03815", + "authenticated":true, + "status":"authenticated", + "auth_identity": Mock::Resource.hel_identity, + "msg":"Authentication successful!", + "reason":"ok" + }.to_json + ) + end + + describe "custom REST methods" do + it 'should support custom GET methods' do + stub_auth + stub_request(:get, req_url(Mock::Resource.site, "/mock/resources/custom_action")). + to_return(status: 200, body: {result: 'success'}.to_json, headers: {}) + + result = Mock::Resource.get(:custom_action) + + expect(result'result').to eq 'success' + expect(WebMock).to have_requested(:get, req_url(Mock::Resource.site, "/mock/resources/custom_action")) + end + + it 'should support custom POST methods' do + stub_auth + stub_request(:post, req_url(Mock::Resource.site, "/mock/resources/custom_action")). + with(body: {data: 'test'}.to_json). + to_return(status: 200, body: {result: 'success'}.to_json, headers: {}) + + result = Mock::Resource.post(:custom_action, {}, {data: 'test'}) + + expect(result'result').to eq 'success' + end + + it 'should support custom PUT methods' do + stub_auth + stub_request(:put, req_url(Mock::Resource.site, "/mock/resources/custom_action")). + with(body: {data: 'test'}.to_json). + to_return(status: 200, body: {result: 'success'}.to_json, headers: {}) + + result = Mock::Resource.put(:custom_action, {}, {data: 'test'}) + + expect(result'result').to eq 'success' + end + + it 'should support custom DELETE methods' do + stub_auth + stub_request(:delete, req_url(Mock::Resource.site, "/mock/resources/custom_action")). + to_return(status: 200, body: {result: 'success'}.to_json, headers: {}) + + result = Mock::Resource.delete(:custom_action) + + expect(result'result').to eq 'success' + end + + it 'should support instance custom GET methods' do + stub_auth + stub_request(:get, req_url(Mock::Resource.site, "/mock/resources/1/custom_action")). + to_return(status: 200, body: {result: 'success'}.to_json, headers: {}) + + stub_request(:get, req_url(Mock::Resource.site, "/mock/resources/1")). + to_return(status: 200, body: {id: 1, name: 'Test'}.to_json, headers: {}) + + resource = Mock::Resource.find(1) + result = resource.get(:custom_action) + + expect(result'result').to eq 'success' + end + + it 'should support instance custom POST methods' do + stub_auth + stub_request(:get, req_url(Mock::Resource.site, "/mock/resources/1")). + to_return(status: 200, body: {id: 1, name: 'Test'}.to_json, headers: {}) + + stub_request(:post, req_url(Mock::Resource.site, "/mock/resources/1/custom_action")). + with(body: {data: 'test'}.to_json). + to_return(status: 200, body: {result: 'success'}.to_json, headers: {}) + + resource = Mock::Resource.find(1) + result = resource.post(:custom_action, {}, {data: 'test'}) + + expect(result'result').to eq 'success' + end + end + + describe "serialization" do + it 'should serialize with only option' do + stub_auth + stub_request(:get, req_url(Mock::Resource.site, "/mock/resources/1")). + to_return(status: 200, body: {id: 1, name: 'Test', email: 'test@example.com'}.to_json, headers: {}) + + resource = Mock::Resource.find(1) + serialized = resource.serializable_hash(only: :id, :name) + + expect(serialized.keys).to eq 'id', 'name' + expect(serialized.keys).not_to include('email') + end + + it 'should serialize with except option' do + stub_auth + stub_request(:get, req_url(Mock::Resource.site, "/mock/resources/1")). + to_return(status: 200, body: {id: 1, name: 'Test', email: 'test@example.com'}.to_json, headers: {}) + + resource = Mock::Resource.find(1) + serialized = resource.serializable_hash(except: :email) + + expect(serialized.keys).to include('id', 'name') + expect(serialized.keys).not_to include('email') + end + + it 'should serialize dirty attributes only' do + stub_auth + stub_request(:get, req_url(Mock::Resource.site, "/mock/resources/1")). + to_return(status: 200, body: {id: 1, name: 'Original', email: 'original@example.com'}.to_json, headers: {}) + + resource = Mock::Resource.find(1) + resource.name = 'Changed' + + serialized = resource.serializable_hash(dirty: true) + + expect(serialized.keys).to include('id', 'name') + expect(serialized.keys).not_to include('email') + end + end + + describe "namespace" do + it 'should generate namespace from class name' do + expect(Mock::Resource.namespace).to eq 'mock' + end + + it 'should use namespace in prefix' do + stub_auth + expect(Mock::Resource.prefix).to include('/mock/') + end + end + + describe "custom save methods" do + it 'should support custom_save with method' do + stub_auth + stub_request(:get, req_url(Mock::Resource.site, "/mock/resources/1")). + to_return(status: 200, body: {id: 1, name: 'Test'}.to_json, headers: {}) + + resource = Mock::Resource.find(1) + resource.name = 'Updated' + + stub_request(:put, req_url(Mock::Resource.site, "/mock/resources/1/custom_method")). + to_return(status: 200, body: {id: 1, name: 'Updated'}.to_json, headers: {}) + + result = resource.custom_save(:custom_method) + + expect(result).to be_truthy + expect(WebMock).to have_requested(:put, req_url(Mock::Resource.site, "/mock/resources/1/custom_method")) + end + + it 'should support custom_save! with method' do + stub_auth + stub_request(:get, req_url(Mock::Resource.site, "/mock/resources/1")). + to_return(status: 200, body: {id: 1, name: 'Test'}.to_json, headers: {}) + + resource = Mock::Resource.find(1) + resource.name = 'Updated' + + stub_request(:put, req_url(Mock::Resource.site, "/mock/resources/1/custom_method")). + to_return(status: 200, body: {id: 1, name: 'Updated'}.to_json, headers: {}) + + expect { resource.custom_save!(:custom_method) }.not_to raise_error + end + end +end
View file
activeresource-hel-0.7.1.gem/data/spec/integration/resource/scopes_spec.rb
Added
@@ -0,0 +1,120 @@ +require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +require 'webmock/rspec' + +module Mock + class Resource < ActiveResource::Hel::Resource + self.cached = false + end +end + +describe "A HEL Resource with Scopes" do + def req_url(site, path, query = {}) + uri = URI(site.to_s) + uri.path = path + + if query.any? + uri.query = URI.encode_www_form(query) + end + + uri.to_s + end + + def stub_auth + stub_request(:post, req_url(Mock::Resource.site, "/ygg/session/authenticate_by_fqda_and_password")). + with( + body: {fqda: Mock::Resource.hel_identity, password: Mock::Resource.hel_secret} + ).to_return( + status: 200, + body: { + "id":"089af4a6-d558-40f1-86c4-c4ca3bf03815", + "authenticated":true, + "status":"authenticated", + "auth_identity": Mock::Resource.hel_identity, + "msg":"Authentication successful!", + "reason":"ok" + }.to_json + ) + end + + before :each do + Mock::Resource.scopes = {} + end + + describe "basic scopes" do + it 'should support where scope' do + stub_auth + stub_request(:get, req_url(Mock::Resource.site, "/mock/resources", {status: 'active'})). + to_return(status: 200, body: .to_json, headers: {}) + + Mock::Resource.where(status: 'active').all + + expect(WebMock).to have_requested(:get, req_url(Mock::Resource.site, "/mock/resources", {status: 'active'})) + end + + it 'should chain where scopes' do + stub_auth + stub_request(:get, req_url(Mock::Resource.site, "/mock/resources", {status: 'active', type: 'user'})). + to_return(status: 200, body: .to_json, headers: {}) + + Mock::Resource.where(status: 'active').where(type: 'user').all + + expect(WebMock).to have_requested(:get, req_url(Mock::Resource.site, "/mock/resources", {status: 'active', type: 'user'})) + end + + it 'should support custom scope definitions' do + stub_auth + Mock::Resource.scope :active, -> { where(status: 'active') } + + stub_request(:get, req_url(Mock::Resource.site, "/mock/resources", {status: 'active'})). + to_return(status: 200, body: .to_json, headers: {}) + + Mock::Resource.active.all + + expect(WebMock).to have_requested(:get, req_url(Mock::Resource.site, "/mock/resources", {status: 'active'})) + end + + it 'should support scope with parameters' do + stub_auth + Mock::Resource.scope :by_type, ->(type) { where(type: type) } + + stub_request(:get, req_url(Mock::Resource.site, "/mock/resources", {type: 'admin'})). + to_return(status: 200, body: .to_json, headers: {}) + + Mock::Resource.by_type('admin').all + + expect(WebMock).to have_requested(:get, req_url(Mock::Resource.site, "/mock/resources", {type: 'admin'})) + end + end + + describe "default scopes" do + it 'should support limit scope' do + stub_auth + stub_request(:get, req_url(Mock::Resource.site, "/mock/resources", {limit: 10})). + to_return(status: 200, body: .to_json, headers: {}) + + Mock::Resource.limit(10).all + + expect(WebMock).to have_requested(:get, req_url(Mock::Resource.site, "/mock/resources", {limit: 10})) + end + + it 'should support start/offset scope' do + stub_auth + stub_request(:get, req_url(Mock::Resource.site, "/mock/resources", {start: 20})). + to_return(status: 200, body: .to_json, headers: {}) + + Mock::Resource.start(20).all + + expect(WebMock).to have_requested(:get, req_url(Mock::Resource.site, "/mock/resources", {start: 20})) + end + + it 'should support view scope' do + stub_auth + stub_request(:get, req_url(Mock::Resource.site, "/mock/resources", {view: 'detailed'})). + to_return(status: 200, body: .to_json, headers: {}) + + Mock::Resource.view(:detailed).all + + expect(WebMock).to have_requested(:get, req_url(Mock::Resource.site, "/mock/resources", {view: 'detailed'})) + end + end +end
View file
activeresource-hel-0.7.1.gem/data/spec/integration/resource/validations_spec.rb
Added
@@ -0,0 +1,184 @@ +require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') +require 'webmock/rspec' + +module Mock + class ResourceWithValidation < ActiveResource::Hel::Resource + self.cached = false + self.remote_validation = true + + schema do + attribute :name, :string + attribute :email, :string + end + end +end + +module Mock + class ResourceWithLocalValidation < ActiveResource::Hel::Resource + self.cached = false + self.remote_validation = true + validates :name, presence: true + + schema do + attribute :name, :string + attribute :email, :string + end + end +end + +describe "A HEL Resource with Validations" do + def req_url(site, path) + uri = URI(site.to_s) + uri.path = path + uri.to_s + end + + def stub_auth + stub_request(:post, req_url(Mock::ResourceWithValidation.site, "/ygg/session/authenticate_by_fqda_and_password")). + with( + body: {fqda: Mock::ResourceWithValidation.hel_identity, password: Mock::ResourceWithValidation.hel_secret} + ).to_return( + status: 200, + body: { + "id":"089af4a6-d558-40f1-86c4-c4ca3bf03815", + "authenticated":true, + "status":"authenticated", + "auth_identity": Mock::ResourceWithValidation.hel_identity, + "msg":"Authentication successful!", + "reason":"ok" + }.to_json + ) + end + + describe "remote validation" do + it 'should validate before creating' do + stub_auth + resource = Mock::ResourceWithValidation.new(name: 'Test', email: 'test@example.com') + + stub_request(:post, req_url(Mock::ResourceWithValidation.site, "/mock/resource_with_validations")). + with(headers: {'X-Validate-Only' => 'true'}). + to_return( + status: 422, + body: { + errors: { + email: 'is invalid' + } + }.to_json, + headers: {} + ) + + expect(resource.valid?).to be_falsey + expect(resource.errors:email).to be_present + end + + it 'should validate before updating' do + stub_auth + stub_request(:get, req_url(Mock::ResourceWithValidation.site, "/mock/resource_with_validations/1")). + to_return( + status: 200, + body: {id: 1, name: 'Test', email: 'old@example.com'}.to_json, + headers: {} + ) + + resource = Mock::ResourceWithValidation.find(1) + resource.email = 'invalid-email' + + stub_request(:put, req_url(Mock::ResourceWithValidation.site, "/mock/resource_with_validations/1")). + with(headers: {'X-Validate-Only' => 'true'}). + to_return( + status: 422, + body: { + errors: { + email: 'is invalid' + } + }.to_json, + headers: {} + ) + + expect(resource.valid?).to be_falsey + expect(resource.errors:email).to be_present + end + + it 'should skip remote validation when requested' do + stub_auth + resource = Mock::ResourceWithValidation.new(name: 'Test') + + expect(resource.valid?(skip_remote_validation: true)).to be_truthy + end + + it 'should save only if valid' do + stub_auth + resource = Mock::ResourceWithValidation.new(name: 'Test', email: 'invalid') + + stub_request(:post, req_url(Mock::ResourceWithValidation.site, "/mock/resource_with_validations")). + with(headers: {'X-Validate-Only' => 'true'}). + to_return( + status: 422, + body: { + errors: { + email: 'is invalid' + } + }.to_json, + headers: {} + ) + + expect(resource.save).to be_falsey + expect(resource.errors:email).to be_present + end + + it 'should handle validation errors correctly' do + stub_auth + resource = Mock::ResourceWithValidation.new(name: 'Test') + + stub_request(:post, req_url(Mock::ResourceWithValidation.site, "/mock/resource_with_validations")). + with(headers: {'X-Validate-Only' => 'true'}). + to_return( + status: 422, + body: { + errors: { + name: 'cannot be blank', + email: 'is required' + } + }.to_json, + headers: {} + ) + + expect(resource.valid?).to be_falsey + expect(resource.errors:name).to be_present + expect(resource.errors:email).to be_present + end + end + + describe "local validation" do + it 'should support ActiveModel validations' do + stub_auth + + resource = Mock::ResourceWithLocalValidation.new + expect(resource.valid?(skip_remote_validation: true)).to be_falsey + expect(resource.errors:name).to be_present + end + + it 'should combine local and remote validations' do + stub_auth + + resource = Mock::ResourceWithLocalValidation.new(email: 'test@example.com') + + stub_request(:post, req_url(Mock::ResourceWithLocalValidation.site, "/mock/resource_with_local_validations")). + with(headers: {'X-Validate-Only' => 'true'}). + to_return( + status: 422, + body: { + errors: { + email: 'is invalid' + } + }.to_json, + headers: {} + ) + + resource.valid? + + expect(resource.errors:name).to be_present + expect(resource.errors:email).to be_present + end + end +end
View file
activeresource-hel-0.7.0.gem/data/spec/spec_helper.rb -> activeresource-hel-0.7.1.gem/data/spec/spec_helper.rb
Changed
@@ -6,9 +6,10 @@ require 'rspec' require 'yaml' require 'timecop' +require 'byebug' RSpec.configure do |config| - ActiveResource::Hel::Resource.site = 'http://test.me' + ActiveResource::Hel::Resource.site = 'https://test.me' ActiveResource::Hel::Resource.hel_identity = 'identity@test.me' ActiveResource::Hel::Resource.hel_secret = 'secret' end
View file
activeresource-hel-0.7.0.gem/metadata.gz -> activeresource-hel-0.7.1.gem/metadata.gz
Changed
@@ -1,14 +1,14 @@ --- !ruby/object:Gem::Specification name: activeresource-hel version: !ruby/object:Gem::Version - version: 0.7.0 + version: 0.7.1 platform: ruby authors: - Lele Forzani autorequire: bindir: bin cert_chain: -date: 2024-11-28 00:00:00.000000000 Z +date: 2025-12-23 00:00:00.000000000 Z dependencies: - !ruby/object:Gem::Dependency name: activeresource @@ -39,7 +39,7 @@ version: 6.0.0 - - "<" - !ruby/object:Gem::Version - version: 8.0.0 + version: 9.0.0 type: :runtime prerelease: false version_requirements: !ruby/object:Gem::Requirement @@ -49,7 +49,7 @@ version: 6.0.0 - - "<" - !ruby/object:Gem::Version - version: 8.0.0 + version: 9.0.0 - !ruby/object:Gem::Dependency name: kaminari requirement: !ruby/object:Gem::Requirement @@ -94,42 +94,42 @@ name: rspec requirement: !ruby/object:Gem::Requirement requirements: - - - ">=" - - !ruby/object:Gem::Version - version: 2.10.0 - - "~>" - !ruby/object:Gem::Version - version: '2.10' + version: '3.0' + - - ">=" + - !ruby/object:Gem::Version + version: 3.13.0 type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - - ">=" - - !ruby/object:Gem::Version - version: 2.10.0 - - "~>" - !ruby/object:Gem::Version - version: '2.10' + version: '3.0' + - - ">=" + - !ruby/object:Gem::Version + version: 3.13.0 - !ruby/object:Gem::Dependency name: rake requirement: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version - version: '12.3' + version: '13.0' - - ">=" - !ruby/object:Gem::Version - version: 12.3.3 + version: 13.3.1 type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version - version: '12.3' + version: '13.0' - - ">=" - !ruby/object:Gem::Version - version: 12.3.3 + version: 13.3.1 - !ruby/object:Gem::Dependency name: pry requirement: !ruby/object:Gem::Requirement @@ -197,12 +197,16 @@ extensions: extra_rdoc_files: files: +- ".byebug_history" +- ".devcontainer/devcontainer.json" - ".gitignore" - ".gitlab-ci.yml" +- Dockerfile - Gemfile - Gemfile.lock - Rakefile - activeresource-hel.gemspec +- docker-compose.yml - lib/activeresource/hel.rb - lib/activeresource/hel/cached.rb - lib/activeresource/hel/collection.rb @@ -231,6 +235,15 @@ - package.json - spec/integration/resource/authentication_spec.rb - spec/integration/resource/cache_spec.rb +- spec/integration/resource/collection_spec.rb +- spec/integration/resource/dirty_spec.rb +- spec/integration/resource/embedded_resources_spec.rb +- spec/integration/resource/filters_spec.rb +- spec/integration/resource/remote_pagination_spec.rb +- spec/integration/resource/remote_schema_spec.rb +- spec/integration/resource/resource_spec.rb +- spec/integration/resource/scopes_spec.rb +- spec/integration/resource/validations_spec.rb - spec/integration/session/proxy_spec.rb - spec/spec.opts - spec/spec_helper.rb
Locations
Projects
Search
Status Monitor
Help
Open Build Service
OBS Manuals
API Documentation
OBS Portal
Reporting a Bug
Contact
Mailing List
Forums
Chat (IRC)
Twitter
Open Build Service (OBS)
is an
openSUSE project
.