diff --git a/.github/probots.yml b/.github/probots.yml new file mode 100644 index 00000000..5d6307a0 --- /dev/null +++ b/.github/probots.yml @@ -0,0 +1,2 @@ +enabled: + - cla \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..af4f0e60 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,103 @@ +name: CI + +on: + push: + branches: [ master ] + pull_request: {} + +concurrency: + group: ci-${{ github.head_ref || github.ref }} + cancel-in-progress: true + +jobs: + lint: + name: Lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: "3.0" + bundler-cache: true + - name: Run standardrb + run: bundle exec standardrb --no-fix --format progress + test: + name: "Tests - ${{ matrix.ruby }} | ${{ matrix.gemfile }} | Appraisal: ${{ matrix.appraisal }}" + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + ruby: + - "2.7" + - "3.0" + - "3.1" + - "3.2" + - "3.3" + gemfile: + - Gemfile + - gemfiles/rails_5_2.gemfile + - gemfiles/rails_6.gemfile + - gemfiles/rails_7.gemfile + - gemfiles/rails_5_2_graphiti_rails.gemfile + - gemfiles/rails_6_graphiti_rails.gemfile + - gemfiles/rails_7_graphiti_rails.gemfile + - gemfiles/rails_7_1_graphiti_rails.gemfile + appraisal: + - true + - false + include: + - ruby: ruby-head + gemfile: gemfiles/rails_7_1.gemfile + appraisal: true + - ruby: ruby-head + gemfile: Gemfile + appraisal: false + exclude: + # Skip some extra variants + - gemfile: Gemfile + appraisal: true + - gemfile: gemfiles/rails_5_2.gemfile + appraisal: false + - gemfile: gemfiles/rails_6.gemfile + appraisal: false + - gemfile: gemfiles/rails_7.gemfile + appraisal: false + - gemfile: gemfiles/rails_5_2_graphiti_rails.gemfile + appraisal: false + - gemfile: gemfiles/rails_6_graphiti_rails.gemfile + appraisal: false + - gemfile: gemfiles/rails_7_graphiti_rails.gemfile + appraisal: false + - gemfile: gemfiles/rails_7_1_graphiti_rails.gemfile + appraisal: false + # Rails 5 can't run on Ruby 3 + - gemfile: gemfiles/rails_5_2.gemfile + ruby: 3.0 + - gemfile: gemfiles/rails_5_2_graphiti_rails.gemfile + ruby: 3.0 + - gemfile: gemfiles/rails_5_2.gemfile + ruby: 3.1 + - gemfile: gemfiles/rails_5_2_graphiti_rails.gemfile + ruby: 3.1 + - gemfile: gemfiles/rails_5_2.gemfile + ruby: 3.2 + - gemfile: gemfiles/rails_5_2_graphiti_rails.gemfile + ruby: 3.2 + - gemfile: gemfiles/rails_5_2.gemfile + ruby: 3.3 + - gemfile: gemfiles/rails_5_2_graphiti_rails.gemfile + ruby: 3.3 + continue-on-error: ${{ matrix.ruby == 'ruby-head' }} + env: # $BUNDLE_GEMFILE must be set at the job level, so it is set for all steps + BUNDLE_GEMFILE: ${{ github.workspace }}/${{ matrix.gemfile }} + steps: + - name: Set up Appraisal + if: matrix.appraisal + run: echo "APPRAISAL_INITIALIZED=true" >> $GITHUB_ENV + - uses: actions/checkout@v2 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + bundler-cache: true + - name: Run tests + run: bundle exec rspec diff --git a/.standard.yml b/.standard.yml index 29252f2a..8f2de2ad 100644 --- a/.standard.yml +++ b/.standard.yml @@ -8,7 +8,7 @@ ignore: # There are some false hits when doing repeated # Class.new blocks in different test cases - Lint/DuplicateMethods - # This was causing many tests to be harder to read. - # Ongoing discussion with StandardRB team in - # https://github.com/testdouble/standard/issues/94 - - Standard/SemanticBlocks \ No newline at end of file + # We define a lot of test-specific classes within our test blocks + # There's probably a better way to do this, but it's not worth the + # effort right now, so let's just disable for specs + - Lint/ConstantDefinitionInBlock diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index e7afa9dd..00000000 --- a/.travis.yml +++ /dev/null @@ -1,94 +0,0 @@ -sudo: false -language: ruby -rvm: -- 2.5 -- 2.6 -- 2.7 -gemfile: - - Gemfile - - gemfiles/rails_4.gemfile - - gemfiles/rails_5_0.gemfile - - gemfiles/rails_5_1.gemfile - - gemfiles/rails_5_2.gemfile - - gemfiles/rails_6.gemfile - - gemfiles/rails_5_0_graphiti_rails.gemfile - - gemfiles/rails_5_1_graphiti_rails.gemfile - - gemfiles/rails_5_2_graphiti_rails.gemfile - - gemfiles/rails_6_graphiti_rails.gemfile -env: - - COMMAND="standardrb --no-fix --format progress" - - COMMAND=rspec - - COMMAND=rspec APPRAISAL_INITIALIZED=true -jobs: - allow_failures: - - rvm: ruby-head - include: - - env: COMMAND=rspec - gemfile: Gemfile - rvm: ruby-head - exclude: - # Don't run the appraisal version of the specs for the base gemfile - - env: COMMAND=rspec APPRAISAL_INITIALIZED=true - gemfile: Gemfile - # Don't run the standardrb check on any gemfile except the base gemfile - # (but run it for each ruby version) - - env: COMMAND="standardrb --no-fix --format progress" - gemfile: gemfiles/rails_4.gemfile - - env: COMMAND="standardrb --no-fix --format progress" - gemfile: gemfiles/rails_5_0.gemfile - - env: COMMAND="standardrb --no-fix --format progress" - gemfile: gemfiles/rails_5_1.gemfile - - env: COMMAND="standardrb --no-fix --format progress" - gemfile: gemfiles/rails_5_2.gemfile - - env: COMMAND="standardrb --no-fix --format progress" - gemfile: gemfiles/rails_6.gemfile - - env: COMMAND="standardrb --no-fix --format progress" - gemfile: gemfiles/rails_5_0_graphiti_rails.gemfile - - env: COMMAND="standardrb --no-fix --format progress" - gemfile: gemfiles/rails_5_1_graphiti_rails.gemfile - - env: COMMAND="standardrb --no-fix --format progress" - gemfile: gemfiles/rails_5_2_graphiti_rails.gemfile - - env: COMMAND="standardrb --no-fix --format progress" - gemfile: gemfiles/rails_6_graphiti_rails.gemfile - # Don't run the basic versions of the specs for any of the specific gemfiles - - env: COMMAND=rspec - gemfile: gemfiles/rails_4.gemfile - - env: COMMAND=rspec - gemfile: gemfiles/rails_5_0.gemfile - - env: COMMAND=rspec - gemfile: gemfiles/rails_5_1.gemfile - - env: COMMAND=rspec - gemfile: gemfiles/rails_5_2.gemfile - - env: COMMAND=rspec - gemfile: gemfiles/rails_6.gemfile - - env: COMMAND=rspec - gemfile: gemfiles/rails_5_0_graphiti_rails.gemfile - - env: COMMAND=rspec - gemfile: gemfiles/rails_5_1_graphiti_rails.gemfile - - env: COMMAND=rspec - gemfile: gemfiles/rails_5_2_graphiti_rails.gemfile - - env: COMMAND=rspec - gemfile: gemfiles/rails_6_graphiti_rails.gemfile - - # Don't run rails 4 egmfile against any rubies (for some reason?) - - gemfile: gemfiles/rails_4.gemfile - rvm: 2.5 - - gemfile: gemfiles/rails_4.gemfile - rvm: 2.6 - - gemfile: gemfiles/rails_4.gemfile - rvm: 2.7 - -script: - - bundle _1.17.3_ exec $COMMAND - -install: bundle _1.17.3_ install --retry=3 --jobs=3 -before_install: -- gem install bundler -v '1.17.3' -deploy: - provider: rubygems - api_key: - secure: aON6EPWgHZXVQs+n/glMjPJ5kMpeR25khDpYlFIM0Ggf2+xuT/yRMtGbNTYnB01v/v2s9tNWK6QlMGARCbKz/W6WS8VNwFxs4DO1IPY3XoyF8QaPlXVKfBLldjUoVx61zL5xtcv8uZqWT1ATY+DjHW3nQ875ss/GW8tw+UASG1uUEuPCE9z+rNa3xYnm2qSZOwhFWqs5bHRQcc3gF3O8Hy8r4aosYTAlYOSeGDToqTUq1Ws21dV4xnIo97Nhmh9SYBQcKpZk1qF9bUz4zqdKsDKAxHMJRShtILmhPxNpIqE5ZV0+Xt+cnwi11AZDhSgfoUqYO9t2uG562luAqTnEZEQl+Lmc+nx4nJTH6tcR3ri301k0O+qDZ3PgSojJvrUuNC8DDLbbsVJNHYycGOLclqQq1aNbgz9HayEE5s+AyklPcprJhngxA5WP80AtONAxxLt4OjrY0X80TvOi2b5nfIN7DowGmcLbRahnms98Yk6/i1vCNV4R9bO0AaqpGLV+L8sVp98Qquk2mpvB3Le4W89qm5int1y2FNJbgInBpY1UOsI3wLENt0npC1X/KVMJKkpHvk80rGccI5FIVRUDZ3YD77hi+s1busPSVQ/+l4QdTFHmWp13Pghf6hNaIwfNwFMHwM2Fkv8YXa4c1mWc1cY5WP1ELS8cEnjtmp/9Iuc= - gem: graphiti - on: - tags: true - repo: graphiti-api/graphiti diff --git a/Appraisals b/Appraisals index 2db3e8e1..72b86b0d 100644 --- a/Appraisals +++ b/Appraisals @@ -1,70 +1,59 @@ -appraise "rails-4" do - gem "rails", "~> 4.2" - gem "rspec-rails" - gem "sqlite3", "~> 1.3.6" - gem "database_cleaner" -end - -appraise "rails-5_0" do - gem "rails", "~> 5.0" +appraise "rails-5_2" do + gem "rails", "~> 5.2.0" gem "rspec-rails" - gem "sqlite3", "~> 1.3.6" + gem "sqlite3", "~> 1.4.0" gem "database_cleaner" end -appraise "rails-5_1" do - gem "rails", "~> 5.1" +appraise "rails-5_2-graphiti-rails" do + gem "rails", "~> 5.2.0" gem "rspec-rails" - gem "sqlite3", "~> 1.3.6" + gem "sqlite3", "~> 1.4.0" gem "database_cleaner" + gem "graphiti-rails", "~> 0.4.0" end -appraise "rails-5_2" do - gem "rails", "~> 5.2" +appraise "rails-6" do + gem "rails", "~> 6.0" gem "rspec-rails" - gem "sqlite3", "~> 1.3.6" + gem "sqlite3", "~> 1.4.0" gem "database_cleaner" end -appraise "rails-5_0-graphiti-rails" do - gem "rails", "~> 5.0" +appraise "rails-6-graphiti-rails" do + gem "rails", "~> 6.0" gem "rspec-rails" - gem "sqlite3", "~> 1.3.6" + gem "sqlite3", "~> 1.4.0" gem "database_cleaner" - gem "rescue_registry", git: "https://github.com/wagenet/rescue_registry.git", branch: "master" - gem "graphiti-rails", git: "https://github.com/wagenet/graphiti-rails.git", branch: "master" + gem "graphiti-rails", "~> 0.4.0" end -appraise "rails-5_1-graphiti-rails" do - gem "rails", "~> 5.1" +appraise "rails-7" do + gem "rails", "~> 7.0" gem "rspec-rails" - gem "sqlite3", "~> 1.3.6" + gem "sqlite3", "~> 1.4.0" gem "database_cleaner" - gem "rescue_registry", git: "https://github.com/wagenet/rescue_registry.git", branch: "master" - gem "graphiti-rails", git: "https://github.com/wagenet/graphiti-rails.git", branch: "master" end -appraise "rails-5_2-graphiti-rails" do - gem "rails", "~> 5.2" +appraise "rails-7-graphiti-rails" do + gem "rails", "~> 7.0" gem "rspec-rails" - gem "sqlite3", "~> 1.3.6" + gem "sqlite3", "~> 1.4.0" gem "database_cleaner" - gem "rescue_registry", git: "https://github.com/wagenet/rescue_registry.git", branch: "master" - gem "graphiti-rails", git: "https://github.com/wagenet/graphiti-rails.git", branch: "master" + gem "graphiti-rails", "~> 0.4.0" end -appraise "rails-6" do - gem "rails", "~> 6.0" +appraise "rails-7-1" do + gem "rails", "~> 7.1" gem "rspec-rails" gem "sqlite3", "~> 1.4.0" gem "database_cleaner" end -appraise "rails-6-graphiti-rails" do - gem "rails", "~> 6.0" +appraise "rails-7-1-graphiti-rails" do + gem "rails", "~> 7.1" gem "rspec-rails" gem "sqlite3", "~> 1.4.0" gem "database_cleaner" - gem "rescue_registry", git: "https://github.com/wagenet/rescue_registry.git", branch: "master" - gem "graphiti-rails", git: "https://github.com/wagenet/graphiti-rails.git", branch: "master" + gem "graphiti-rails", "~> 0.4.0" end diff --git a/Gemfile b/Gemfile index 3317b886..6d80a9aa 100644 --- a/Gemfile +++ b/Gemfile @@ -4,6 +4,7 @@ source "https://rubygems.org" gemspec group :test do + gem "database_cleaner" gem "pry" gem "pry-byebug", platform: [:mri] gem "appraisal" diff --git a/gemfiles/rails_5_1_graphiti_rails.gemfile b/gemfiles/rails_5_1_graphiti_rails.gemfile deleted file mode 100644 index 6d9e19f2..00000000 --- a/gemfiles/rails_5_1_graphiti_rails.gemfile +++ /dev/null @@ -1,20 +0,0 @@ -# This file was generated by Appraisal - -source "https://rubygems.org" - -gem "rails", "~> 5.1" -gem "rspec-rails" -gem "sqlite3", "~> 1.3.6" -gem "database_cleaner" -gem "rescue_registry", git: "https://github.com/wagenet/rescue_registry.git", branch: "master" -gem "graphiti-rails", git: "https://github.com/wagenet/graphiti-rails.git", branch: "master" - -group :test do - gem "pry" - gem "pry-byebug", platform: [:mri] - gem "appraisal" - gem "guard" - gem "guard-rspec" -end - -gemspec path: "../" diff --git a/gemfiles/rails_5_2.gemfile b/gemfiles/rails_5_2.gemfile index d3fdb42f..38872d68 100644 --- a/gemfiles/rails_5_2.gemfile +++ b/gemfiles/rails_5_2.gemfile @@ -2,9 +2,9 @@ source "https://rubygems.org" -gem "rails", "~> 5.2" +gem "rails", "~> 5.2.0" gem "rspec-rails" -gem "sqlite3", "~> 1.3.6" +gem "sqlite3", "~> 1.4.0" gem "database_cleaner" group :test do diff --git a/gemfiles/rails_5_2_graphiti_rails.gemfile b/gemfiles/rails_5_2_graphiti_rails.gemfile index 3b5aeb82..0f483569 100644 --- a/gemfiles/rails_5_2_graphiti_rails.gemfile +++ b/gemfiles/rails_5_2_graphiti_rails.gemfile @@ -2,12 +2,11 @@ source "https://rubygems.org" -gem "rails", "~> 5.2" +gem "rails", "~> 5.2.0" gem "rspec-rails" -gem "sqlite3", "~> 1.3.6" +gem "sqlite3", "~> 1.4.0" gem "database_cleaner" -gem "rescue_registry", git: "https://github.com/wagenet/rescue_registry.git", branch: "master" -gem "graphiti-rails", git: "https://github.com/wagenet/graphiti-rails.git", branch: "master" +gem "graphiti-rails", "~> 0.4.0" group :test do gem "pry" diff --git a/gemfiles/rails_6_graphiti_rails.gemfile b/gemfiles/rails_6_graphiti_rails.gemfile index 07dd5281..ea204ff2 100644 --- a/gemfiles/rails_6_graphiti_rails.gemfile +++ b/gemfiles/rails_6_graphiti_rails.gemfile @@ -6,8 +6,7 @@ gem "rails", "~> 6.0" gem "rspec-rails" gem "sqlite3", "~> 1.4.0" gem "database_cleaner" -gem "rescue_registry", git: "https://github.com/wagenet/rescue_registry.git", branch: "master" -gem "graphiti-rails", git: "https://github.com/wagenet/graphiti-rails.git", branch: "master" +gem "graphiti-rails", "~> 0.4.0" group :test do gem "pry" diff --git a/gemfiles/rails_5_1.gemfile b/gemfiles/rails_7.gemfile similarity index 84% rename from gemfiles/rails_5_1.gemfile rename to gemfiles/rails_7.gemfile index edb5326c..054b43bb 100644 --- a/gemfiles/rails_5_1.gemfile +++ b/gemfiles/rails_7.gemfile @@ -2,9 +2,9 @@ source "https://rubygems.org" -gem "rails", "~> 5.1" +gem "rails", "~> 7.0" gem "rspec-rails" -gem "sqlite3", "~> 1.3.6" +gem "sqlite3", "~> 1.4.0" gem "database_cleaner" group :test do diff --git a/gemfiles/rails_4.gemfile b/gemfiles/rails_7_1.gemfile similarity index 84% rename from gemfiles/rails_4.gemfile rename to gemfiles/rails_7_1.gemfile index 2b5cb009..7f61a90e 100644 --- a/gemfiles/rails_4.gemfile +++ b/gemfiles/rails_7_1.gemfile @@ -2,9 +2,9 @@ source "https://rubygems.org" -gem "rails", "~> 4.2" +gem "rails", "~> 7.1" gem "rspec-rails" -gem "sqlite3", "~> 1.3.6" +gem "sqlite3", "~> 1.4.0" gem "database_cleaner" group :test do diff --git a/gemfiles/rails_5_0.gemfile b/gemfiles/rails_7_1_graphiti_rails.gemfile similarity index 75% rename from gemfiles/rails_5_0.gemfile rename to gemfiles/rails_7_1_graphiti_rails.gemfile index 3fb1979a..97c0a5e5 100644 --- a/gemfiles/rails_5_0.gemfile +++ b/gemfiles/rails_7_1_graphiti_rails.gemfile @@ -2,10 +2,11 @@ source "https://rubygems.org" -gem "rails", "~> 5.0" +gem "rails", "~> 7.1" gem "rspec-rails" -gem "sqlite3", "~> 1.3.6" +gem "sqlite3", "~> 1.4.0" gem "database_cleaner" +gem "graphiti-rails", "~> 0.4.0" group :test do gem "pry" diff --git a/gemfiles/rails_5_0_graphiti_rails.gemfile b/gemfiles/rails_7_graphiti_rails.gemfile similarity index 51% rename from gemfiles/rails_5_0_graphiti_rails.gemfile rename to gemfiles/rails_7_graphiti_rails.gemfile index 0d9f4af6..0aae1f4d 100644 --- a/gemfiles/rails_5_0_graphiti_rails.gemfile +++ b/gemfiles/rails_7_graphiti_rails.gemfile @@ -2,12 +2,11 @@ source "https://rubygems.org" -gem "rails", "~> 5.0" +gem "rails", "~> 7.0" gem "rspec-rails" -gem "sqlite3", "~> 1.3.6" +gem "sqlite3", "~> 1.4.0" gem "database_cleaner" -gem "rescue_registry", git: "https://github.com/wagenet/rescue_registry.git", branch: "master" -gem "graphiti-rails", git: "https://github.com/wagenet/graphiti-rails.git", branch: "master" +gem "graphiti-rails", "~> 0.4.0" group :test do gem "pry" diff --git a/graphiti.gemspec b/graphiti.gemspec index 83b6ab4d..40f9589a 100644 --- a/graphiti.gemspec +++ b/graphiti.gemspec @@ -16,20 +16,20 @@ Gem::Specification.new do |spec| spec.bindir = "exe" spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } spec.require_paths = ["lib"] - spec.required_ruby_version = [">= 2.3", "< 3.1"] + spec.required_ruby_version = ">= 2.6" spec.add_dependency "jsonapi-serializable", "~> 0.3.0" spec.add_dependency "jsonapi-renderer", "~> 0.2", ">= 0.2.2" spec.add_dependency "dry-types", ">= 0.15.0", "< 2.0" spec.add_dependency "graphiti_errors", "~> 1.1.0" spec.add_dependency "concurrent-ruby", "~> 1.0" - spec.add_dependency "activesupport", ">= 4.1" + spec.add_dependency "activesupport", ">= 5.2" spec.add_development_dependency "faraday", "~> 0.15" spec.add_development_dependency "kaminari", "~> 0.17" spec.add_development_dependency "bundler" - spec.add_development_dependency "rake", "~> 10.0" - spec.add_development_dependency "standard", "0.4.7" - spec.add_development_dependency "activemodel", ">= 4.1" + spec.add_development_dependency "rake", ">= 10.0" + spec.add_development_dependency "standard", "~> 1.4.0" + spec.add_development_dependency "activemodel", ">= 5.2" spec.add_development_dependency "graphiti_spec_helpers", "1.0.beta.4" end diff --git a/lib/graphiti.rb b/lib/graphiti.rb index 35eaed6c..78904e94 100644 --- a/lib/graphiti.rb +++ b/lib/graphiti.rb @@ -1,5 +1,6 @@ require "json" require "forwardable" +require "uri" require "active_support/core_ext/string" require "active_support/core_ext/enumerable" require "active_support/core_ext/class/attribute" @@ -83,7 +84,12 @@ def self.logger=(val) end def self.log(msg, color = :white, bold = false) - colored = ActiveSupport::LogSubscriber.new.send(:color, msg, color, bold) + colored = if ::ActiveSupport.version >= Gem::Version.new("7.1") + ActiveSupport::LogSubscriber.new.send(:color, msg, color, bold: bold) + else + ActiveSupport::LogSubscriber.new.send(:color, msg, color, bold) + end + logger.debug(colored) end @@ -131,7 +137,6 @@ def self.setup! require "graphiti/request_validator" require "graphiti/request_validators/validator" require "graphiti/request_validators/update_validator" -require "graphiti/query" require "graphiti/scope" require "graphiti/deserializer" require "graphiti/renderer" @@ -170,6 +175,7 @@ def self.setup! require "graphiti/extensions/boolean_attribute" require "graphiti/extensions/temp_id" require "graphiti/serializer" +require "graphiti/query" require "graphiti/debugger" if defined?(ActiveRecord) @@ -208,6 +214,7 @@ def instance_values values end end + class Object prepend InstanceVariableOverride end diff --git a/lib/graphiti/adapters/abstract.rb b/lib/graphiti/adapters/abstract.rb index 08ceda26..92f789cf 100644 --- a/lib/graphiti/adapters/abstract.rb +++ b/lib/graphiti/adapters/abstract.rb @@ -1,7 +1,7 @@ module Graphiti module Adapters class Abstract - require "graphiti/adapters/persistence/associations.rb" + require "graphiti/adapters/persistence/associations" include Graphiti::Adapters::Persistence::Associations attr_reader :resource @@ -415,6 +415,10 @@ def self.numerical_operators [:eq, :not_eq, :gt, :gte, :lt, :lte].freeze end + def can_group? + false + end + private def activerecord_adapter diff --git a/lib/graphiti/adapters/active_record.rb b/lib/graphiti/adapters/active_record.rb index f96e8b50..3cedfae6 100644 --- a/lib/graphiti/adapters/active_record.rb +++ b/lib/graphiti/adapters/active_record.rb @@ -19,24 +19,24 @@ def self.sideloading_classes def filter_eq(scope, attribute, value) scope.where(attribute => value) end - alias filter_integer_eq filter_eq - alias filter_float_eq filter_eq - alias filter_big_decimal_eq filter_eq - alias filter_date_eq filter_eq - alias filter_boolean_eq filter_eq - alias filter_uuid_eq filter_eq - alias filter_enum_eq filter_eq + alias_method :filter_integer_eq, :filter_eq + alias_method :filter_float_eq, :filter_eq + alias_method :filter_big_decimal_eq, :filter_eq + alias_method :filter_date_eq, :filter_eq + alias_method :filter_boolean_eq, :filter_eq + alias_method :filter_uuid_eq, :filter_eq + alias_method :filter_enum_eq, :filter_eq def filter_not_eq(scope, attribute, value) scope.where.not(attribute => value) end - alias filter_integer_not_eq filter_not_eq - alias filter_float_not_eq filter_not_eq - alias filter_big_decimal_not_eq filter_not_eq - alias filter_date_not_eq filter_not_eq - alias filter_boolean_not_eq filter_not_eq - alias filter_uuid_not_eq filter_not_eq - alias filter_enum_not_eq filter_not_eq + alias_method :filter_integer_not_eq, :filter_not_eq + alias_method :filter_float_not_eq, :filter_not_eq + alias_method :filter_big_decimal_not_eq, :filter_not_eq + alias_method :filter_date_not_eq, :filter_not_eq + alias_method :filter_boolean_not_eq, :filter_not_eq + alias_method :filter_uuid_not_eq, :filter_not_eq + alias_method :filter_enum_not_eq, :filter_not_eq def filter_string_eq(scope, attribute, value, is_not: false) column = column_for(scope, attribute) @@ -122,40 +122,40 @@ def filter_gt(scope, attribute, value) column = column_for(scope, attribute) scope.where(column.gt_any(value)) end - alias filter_integer_gt filter_gt - alias filter_float_gt filter_gt - alias filter_big_decimal_gt filter_gt - alias filter_datetime_gt filter_gt - alias filter_date_gt filter_gt + alias_method :filter_integer_gt, :filter_gt + alias_method :filter_float_gt, :filter_gt + alias_method :filter_big_decimal_gt, :filter_gt + alias_method :filter_datetime_gt, :filter_gt + alias_method :filter_date_gt, :filter_gt def filter_gte(scope, attribute, value) column = column_for(scope, attribute) scope.where(column.gteq_any(value)) end - alias filter_integer_gte filter_gte - alias filter_float_gte filter_gte - alias filter_big_decimal_gte filter_gte - alias filter_datetime_gte filter_gte - alias filter_date_gte filter_gte + alias_method :filter_integer_gte, :filter_gte + alias_method :filter_float_gte, :filter_gte + alias_method :filter_big_decimal_gte, :filter_gte + alias_method :filter_datetime_gte, :filter_gte + alias_method :filter_date_gte, :filter_gte def filter_lt(scope, attribute, value) column = column_for(scope, attribute) scope.where(column.lt_any(value)) end - alias filter_integer_lt filter_lt - alias filter_float_lt filter_lt - alias filter_big_decimal_lt filter_lt - alias filter_datetime_lt filter_lt - alias filter_date_lt filter_lt + alias_method :filter_integer_lt, :filter_lt + alias_method :filter_float_lt, :filter_lt + alias_method :filter_big_decimal_lt, :filter_lt + alias_method :filter_datetime_lt, :filter_lt + alias_method :filter_date_lt, :filter_lt def filter_lte(scope, attribute, value) column = column_for(scope, attribute) scope.where(column.lteq_any(value)) end - alias filter_integer_lte filter_lte - alias filter_float_lte filter_lte - alias filter_big_decimal_lte filter_lte - alias filter_date_lte filter_lte + alias_method :filter_integer_lte, :filter_lte + alias_method :filter_float_lte, :filter_lte + alias_method :filter_big_decimal_lte, :filter_lte + alias_method :filter_date_lte, :filter_lte # Ensure fractional seconds don't matter def filter_datetime_eq(scope, attribute, value, is_not: false) @@ -305,6 +305,14 @@ def close ::ActiveRecord::Base.clear_active_connections! end + def can_group? + true + end + + def group(scope, attribute) + scope.group(attribute) + end + private def column_for(scope, name) @@ -317,7 +325,7 @@ def column_for(scope, name) end def sanitized_like_for(scope, attribute, value, &block) - escape_char = '\\' + escape_char = "\\" column = column_for(scope, attribute) map = value.map { |v| v = v.downcase diff --git a/lib/graphiti/adapters/persistence/associations.rb b/lib/graphiti/adapters/persistence/associations.rb index 4c8ab2bc..b4286de5 100644 --- a/lib/graphiti/adapters/persistence/associations.rb +++ b/lib/graphiti/adapters/persistence/associations.rb @@ -5,22 +5,20 @@ module Associations def process_belongs_to(persistence, attributes) parents = [].tap do |processed| persistence.iterate(only: [:polymorphic_belongs_to, :belongs_to]) do |x| - begin - id = x.dig(:attributes, :id) - x[:object] = x[:resource] - .persist_with_relationships(x[:meta], x[:attributes], x[:relationships]) + id = x.dig(:attributes, :id) + x[:object] = x[:resource] + .persist_with_relationships(x[:meta], x[:attributes], x[:relationships]) + processed << x + rescue Graphiti::Errors::RecordNotFound + if Graphiti.config.raise_on_missing_sidepost + path = "relationships/#{x.dig(:meta, :jsonapi_type)}" + raise Graphiti::Errors::RecordNotFound.new(x[:sideload].name, id, path) + else + pointer = "data/relationships/#{x.dig(:meta, :jsonapi_type)}" + object = Graphiti::Errors::NullRelation.new(id.to_s, pointer) + object.errors.add(:base, :not_found, message: "could not be found") + x[:object] = object processed << x - rescue Graphiti::Errors::RecordNotFound - if Graphiti.config.raise_on_missing_sidepost - path = "relationships/#{x.dig(:meta, :jsonapi_type)}" - raise Graphiti::Errors::RecordNotFound.new(x[:sideload].name, id, path) - else - pointer = "data/relationships/#{x.dig(:meta, :jsonapi_type)}" - object = Graphiti::Errors::NullRelation.new(id.to_s, pointer) - object.errors.add(:base, :not_found, message: "could not be found") - x[:object] = object - processed << x - end end end end diff --git a/lib/graphiti/debugger.rb b/lib/graphiti/debugger.rb index 070f4fac..b5fd3991 100644 --- a/lib/graphiti/debugger.rb +++ b/lib/graphiti/debugger.rb @@ -140,7 +140,6 @@ def scrub_params(params) params ||= {} params = params.to_unsafe_h if params.respond_to?(:to_unsafe_h) params.reject! { |k, v| [:controller, :action, :format, :debug].include?(k.to_sym) } - params.reject! { |k, v| k.to_sym == :include } params.deep_symbolize_keys end diff --git a/lib/graphiti/delegates/pagination.rb b/lib/graphiti/delegates/pagination.rb index d308124c..4fb2dfee 100644 --- a/lib/graphiti/delegates/pagination.rb +++ b/lib/graphiti/delegates/pagination.rb @@ -97,10 +97,8 @@ def current_page end def offset - @offset ||= begin - if (value = page_param[:offset]) - value.to_i - end + @offset ||= if (value = page_param[:offset]) + value.to_i end end diff --git a/lib/graphiti/extensions/temp_id.rb b/lib/graphiti/extensions/temp_id.rb index 4da6e1db..39f003ae 100644 --- a/lib/graphiti/extensions/temp_id.rb +++ b/lib/graphiti/extensions/temp_id.rb @@ -13,7 +13,7 @@ module SerializableTempId # Common interface for jsonapi-rb extensions def as_jsonapi(*) super.tap do |hash| - if (temp_id = @object.instance_variable_get(:'@_jsonapi_temp_id')) + if (temp_id = @object.instance_variable_get(:@_jsonapi_temp_id)) hash[:'temp-id'] = temp_id end end diff --git a/lib/graphiti/filter_operators.rb b/lib/graphiti/filter_operators.rb index 5acbeba7..8d5d0979 100644 --- a/lib/graphiti/filter_operators.rb +++ b/lib/graphiti/filter_operators.rb @@ -17,7 +17,6 @@ def initialize(resource, type_name, opts) end end - # rubocop: disable Style/MethodMissingSuper def method_missing(name, *args, &blk) @procs[name] = blk end diff --git a/lib/graphiti/hash_renderer.rb b/lib/graphiti/hash_renderer.rb index 026bd6ea..64bcc6ca 100644 --- a/lib/graphiti/hash_renderer.rb +++ b/lib/graphiti/hash_renderer.rb @@ -192,7 +192,11 @@ def add_stats(hash, top_level_key, options, graphql) if options[:meta] && !options[:meta].empty? if @graphql if (stats = options[:meta][:stats]) - hash[top_level_key][:stats] = stats + camelized = {} + stats.each_pair do |key, value| + camelized[key.to_s.camelize(:lower).to_sym] = value + end + hash[top_level_key][:stats] = camelized end else hash.merge!(options.slice(:meta)) diff --git a/lib/graphiti/query.rb b/lib/graphiti/query.rb index 1a65cc8e..a336a7d3 100644 --- a/lib/graphiti/query.rb +++ b/lib/graphiti/query.rb @@ -69,50 +69,49 @@ def zero_results? end def sideload_hash - @sideload_hash = begin - {}.tap do |hash| - sideloads.each_pair do |key, value| - hash[key] = sideloads[key].hash - end + @sideload_hash = {}.tap do |hash| + sideloads.each_pair do |key, value| + hash[key] = sideloads[key].hash end end end + class RemoteSideloadResource < ::Graphiti::Resource + self.remote = "_remote_sideload_".freeze + self.abstract_class = true # exclude from schema + end + def resource_for_sideload(sideload) if @resource.remote? - Class.new(Graphiti::Resource) { - self.remote = "_remote_sideload_" - }.new + RemoteSideloadResource.new else sideload.resource end end def sideloads - @sideloads ||= begin - {}.tap do |hash| - include_hash.each_pair do |key, sub_hash| - sideload = @resource.class.sideload(key) - - if sideload || @resource.remote? - sl_resource = resource_for_sideload(sideload) - query_parents = parents + [self] - sub_hash = sub_hash[:include] if sub_hash.key?(:include) - - # NB: To handle on__-- - # A) relationship_name == :positions - # B) key == on__employees.positions - # This way A) ensures sideloads are resolved - # And B) ensures nested filters, sorts etc still work - relationship_name = sideload ? sideload.name : key - hash[relationship_name] = Query.new sl_resource, - @params, - key, - sub_hash, - query_parents, :all - else - handle_missing_sideload(key) - end + @sideloads ||= {}.tap do |hash| + include_hash.each_pair do |key, sub_hash| + sideload = @resource.class.sideload(key) + + if sideload || @resource.remote? + sl_resource = resource_for_sideload(sideload) + query_parents = parents + [self] + sub_hash = sub_hash[:include] if sub_hash.key?(:include) + + # NB: To handle on__-- + # A) relationship_name == :positions + # B) key == on__employees.positions + # This way A) ensures sideloads are resolved + # And B) ensures nested filters, sorts etc still work + relationship_name = sideload ? sideload.name : key + hash[relationship_name] = Query.new sl_resource, + @params, + key, + sub_hash, + query_parents, :all + else + handle_missing_sideload(key) end end end @@ -137,27 +136,25 @@ def extra_fields end def filters - @filters ||= begin - {}.tap do |hash| - (@params[:filter] || {}).each_pair do |name, value| - name = name.to_sym - - if legacy_nested?(name) - value.keys.each do |key| - filter_name = key.to_sym - filter_value = value[key] - - if @resource.get_attr!(filter_name, :filterable, request: true) - hash[filter_name] = filter_value - end + @filters ||= {}.tap do |hash| + (@params[:filter] || {}).each_pair do |name, value| + name = name.to_sym + + if legacy_nested?(name) + value.keys.each do |key| + filter_name = key.to_sym + filter_value = value[key] + + if @resource.get_attr!(filter_name, :filterable, request: true) + hash[filter_name] = filter_value end - elsif nested?(name) - name = name.to_s.split(".").last.to_sym - validate!(name, :filterable) - hash[name] = value - elsif top_level? && validate!(name, :filterable) - hash[name] = value end + elsif nested?(name) + name = name.to_s.split(".").last.to_sym + validate!(name, :filterable) + hash[name] = value + elsif top_level? && validate!(name, :filterable) + hash[name] = value end end end @@ -186,19 +183,17 @@ def sorts end def pagination - @pagination ||= begin - {}.tap do |hash| - (@params[:page] || {}).each_pair do |name, value| - if legacy_nested?(name) - value.each_pair do |k, v| - hash[k.to_sym] = cast_page_param(k.to_sym, v) - end - elsif nested?(name) - param_name = name.to_s.split(".").last.to_sym - hash[param_name] = cast_page_param(param_name, value) - elsif top_level? && Scoping::Paginate::PARAMS.include?(name.to_sym) - hash[name.to_sym] = cast_page_param(name.to_sym, value) + @pagination ||= {}.tap do |hash| + (@params[:page] || {}).each_pair do |name, value| + if legacy_nested?(name) + value.each_pair do |k, v| + hash[k.to_sym] = cast_page_param(k.to_sym, v) end + elsif nested?(name) + param_name = name.to_s.split(".").last.to_sym + hash[param_name] = cast_page_param(param_name, value) + elsif top_level? && Scoping::Paginate::PARAMS.include?(name.to_sym) + hash[name.to_sym] = cast_page_param(name.to_sym, value) end end end @@ -221,15 +216,13 @@ def include_hash end def stats - @stats ||= begin - {}.tap do |hash| - (@params[:stats] || {}).each_pair do |k, v| - if legacy_nested?(k) - raise NotImplementedError.new("Association statistics are not currently supported") - elsif top_level? - v = v.split(",") if v.is_a?(String) - hash[k.to_sym] = Array(v).flatten.map(&:to_sym) - end + @stats ||= {}.tap do |hash| + (@params[:stats] || {}).each_pair do |k, v| + if legacy_nested?(k) + raise NotImplementedError.new("Association statistics are not currently supported") + elsif top_level? + v = v.split(",") if v.is_a?(String) + hash[k.to_sym] = Array(v).flatten.map(&:to_sym) end end end diff --git a/lib/graphiti/railtie.rb b/lib/graphiti/railtie.rb index dea9548e..5bcd5fa5 100644 --- a/lib/graphiti/railtie.rb +++ b/lib/graphiti/railtie.rb @@ -110,10 +110,10 @@ def configure_endpoint_lookup end route = begin - ::Rails.application.routes.recognize_path(path, method: method) - rescue - nil - end + ::Rails.application.routes.recognize_path(path, method: method) + rescue + nil + end "#{route[:controller]}_controller".classify.safe_constantize if route } end diff --git a/lib/graphiti/request_validator.rb b/lib/graphiti/request_validator.rb index 1f384026..7b3038d5 100644 --- a/lib/graphiti/request_validator.rb +++ b/lib/graphiti/request_validator.rb @@ -13,7 +13,7 @@ def initialize(root_resource, raw_params, action) class ValidatorFactory def self.create(root_resource, raw_params, action) case action - when :update then + when :update RequestValidators::UpdateValidator else RequestValidators::Validator diff --git a/lib/graphiti/resource/dsl.rb b/lib/graphiti/resource/dsl.rb index 96f11850..8d073b8f 100644 --- a/lib/graphiti/resource/dsl.rb +++ b/lib/graphiti/resource/dsl.rb @@ -9,7 +9,11 @@ def filter(name, *args, &blk) opts = args.extract_options! type_override = args[0] - if (att = get_attr(name, :filterable, raise_error: :only_unsupported)) + if (att = (attributes[name] || extra_attributes[name])) + # We're opting in to filtering, so force this + # UNLESS the filter is guarded at the attribute level + att[:filterable] = true if att[:filterable] == false + aliases = [name, opts[:aliases]].flatten.compact operators = FilterOperators.build(self, att[:type], opts, &blk) @@ -23,6 +27,8 @@ def filter(name, *args, &blk) end required = att[:filterable] == :required || !!opts[:required] + schema = !!opts[:via_attribute_dsl] ? att[:schema] : opts[:schema] != false + config[:filters][name.to_sym] = { aliases: aliases, name: name.to_sym, @@ -32,6 +38,7 @@ def filter(name, *args, &blk) single: !!opts[:single], dependencies: opts[:dependent], required: required, + schema: schema, operators: operators.to_hash, allow_nil: opts.fetch(:allow_nil, filters_accept_nil_by_default), deny_empty: opts.fetch(:deny_empty, filters_deny_empty_by_default) @@ -56,7 +63,7 @@ def filter_group(filter_names, *args) end def sort_all(&blk) - if block_given? + if blk config[:_sort_all] = blk else config[:_sort_all] @@ -130,7 +137,7 @@ def attribute(name, type, options = {}, &blk) options[:sortable] ? sort(name) : config[:sorts].delete(name) if options[:filterable] - filter(name, allow: options[:allow]) + filter(name, allow: options[:allow], via_attribute_dsl: true) else config[:filters].delete(name) end @@ -144,7 +151,8 @@ def extra_attribute(name, type, options = {}, &blk) readable: true, writable: false, sortable: false, - filterable: false + filterable: false, + schema: true } options = defaults.merge(options) attribute_option(options, :readable) @@ -181,9 +189,9 @@ def apply_extra_attributes_to_serializer def attribute_option(options, name, exclusive = false) if options[name] != false default = if (only = options[:only]) && !exclusive - Array(only).include?(name) ? true : false + Array(only).include?(name) elsif (except = options[:except]) && !exclusive - Array(except).include?(name) ? false : true + !Array(except).include?(name) else send(:"attributes_#{name}_by_default") end @@ -197,7 +205,7 @@ def relationship_option(options, name) options[name] ||= send(:"relationships_#{name}_by_default") end end - private :attribute_option + private :relationship_option end end end diff --git a/lib/graphiti/resource/polymorphism.rb b/lib/graphiti/resource/polymorphism.rb index e86e08b0..c8f1cceb 100644 --- a/lib/graphiti/resource/polymorphism.rb +++ b/lib/graphiti/resource/polymorphism.rb @@ -72,7 +72,7 @@ def resource_for_type(type) end def resource_for_model(model) - resource = children.find { |c| model.class == c.model } || + resource = children.find { |c| model.instance_of?(c.model) } || children.find { |c| model.is_a?(c.model) } if resource.nil? raise Errors::PolymorphicResourceChildNotFound.new(self, model: model) diff --git a/lib/graphiti/resource/sideloading.rb b/lib/graphiti/resource/sideloading.rb index adc0c808..5398562b 100644 --- a/lib/graphiti/resource/sideloading.rb +++ b/lib/graphiti/resource/sideloading.rb @@ -68,10 +68,10 @@ def polymorphic_has_many(name, opts = {}, &blk) model_ref = model has_many name, opts do params do |hash| - hash[:filter][:"#{as}_type"] = model_ref.name + hash[:filter][:"#{as}_type"] = {eql: model_ref.name} end - instance_eval(&blk) if block_given? + instance_eval(&blk) if blk end end @@ -82,10 +82,10 @@ def polymorphic_has_one(name, opts = {}, &blk) model_ref = model has_one name, opts do params do |hash| - hash[:filter][:"#{as}_type"] = model_ref.name + hash[:filter][:"#{as}_type"] = {eql: model_ref.name} end - instance_eval(&blk) if block_given? + instance_eval(&blk) if blk end end diff --git a/lib/graphiti/resource_proxy.rb b/lib/graphiti/resource_proxy.rb index f9657eca..d03105be 100644 --- a/lib/graphiti/resource_proxy.rb +++ b/lib/graphiti/resource_proxy.rb @@ -73,7 +73,7 @@ def data records end end - alias to_a data + alias_method :to_a, :data def meta @meta ||= data.respond_to?(:meta) ? data.meta : {} @@ -85,9 +85,15 @@ def each(&blk) def stats @stats ||= if @query.hash[:stats] + scope = @scope.unpaginated_object + if resource.adapter.can_group? + if (group = @query.hash[:stats].delete(:group_by)) + scope = resource.adapter.group(scope, group[0]) + end + end payload = Stats::Payload.new @resource, @query, - @scope.unpaginated_object, + scope, data payload.generate else @@ -147,11 +153,13 @@ def destroy success end - def update_attributes + def update data save(action: :update) end + alias update_attributes update # standard:disable Style/Alias + def include_hash @include_hash ||= begin base = @payload ? @payload.include_hash : {} diff --git a/lib/graphiti/schema.rb b/lib/graphiti/schema.rb index b74a9314..7b08b2ad 100644 --- a/lib/graphiti/schema.rb +++ b/lib/graphiti/schema.rb @@ -153,6 +153,8 @@ def attributes(resource) def extra_attributes(resource) {}.tap do |attrs| resource.extra_attributes.each_pair do |name, config| + next unless config[:schema] + attrs[name] = { type: config[:type].to_s, readable: flag(config[:readable]), @@ -181,11 +183,11 @@ def stats(resource) def sorts(resource) {}.tap do |s| resource.sorts.each_pair do |name, sort| - next unless resource.attributes[name][:schema] + attr = resource.all_attributes[name] + next unless attr[:schema] config = {} config[:only] = sort[:only] if sort[:only] - attr = resource.attributes[name] if attr[:sortable].is_a?(Symbol) config[:guard] = true end @@ -197,7 +199,7 @@ def sorts(resource) def filters(resource) {}.tap do |f| resource.filters.each_pair do |name, filter| - next unless resource.attributes[name][:schema] + next unless resource.filters[name][:schema] config = { type: filter[:type].to_s, @@ -209,7 +211,7 @@ def filters(resource) config[:deny] = filter[:deny].map(&:to_s) if filter[:deny] config[:dependencies] = filter[:dependencies].map(&:to_s) if filter[:dependencies] - attr = resource.attributes[name] + attr = resource.all_attributes[name] if attr[:filterable].is_a?(Symbol) if attr[:filterable] == :required config[:required] = true diff --git a/lib/graphiti/scoping/filter.rb b/lib/graphiti/scoping/filter.rb index 9ab425f1..9d75feef 100644 --- a/lib/graphiti/scoping/filter.rb +++ b/lib/graphiti/scoping/filter.rb @@ -193,14 +193,14 @@ def parse_string_arrays(value, singular_filter) # Find the quoted strings quotes = value.scan(/{{.*?}}/) # remove them from the rest - quotes.each { |q| value.gsub!(q, "") } + non_quotes = quotes.inject(value) { |v, q| v.gsub(q, "") } # remove the quote characters from the quoted strings quotes.each { |q| q.gsub!("{{", "").gsub!("}}", "") } # merge everything back together into an array value = if singular_filter - Array(value) + quotes + Array(non_quotes) + quotes else - Array(value.split(",")) + quotes + Array(non_quotes.split(",")) + quotes end # remove any blanks that are left value.reject! { |v| v.length.zero? } diff --git a/lib/graphiti/scoping/sort.rb b/lib/graphiti/scoping/sort.rb index b81c8a07..61e4c10d 100644 --- a/lib/graphiti/scoping/sort.rb +++ b/lib/graphiti/scoping/sort.rb @@ -59,12 +59,10 @@ def each_sort end def sort_param - @sort_param ||= begin - if query_hash[:sort].blank? - resource.default_sort || [] - else - normalize(query_hash[:sort]) - end + @sort_param ||= if query_hash[:sort].blank? + resource.default_sort || [] + else + normalize(query_hash[:sort]) end end diff --git a/lib/graphiti/serializer.rb b/lib/graphiti/serializer.rb index 6e7ce393..6465bd9e 100644 --- a/lib/graphiti/serializer.rb +++ b/lib/graphiti/serializer.rb @@ -25,8 +25,9 @@ def self.inherited(klass) # See #requested_relationships def self.relationship(name, options = {}, &block) + prev = Util::Hash.deep_dup(field_condition_blocks) super - field_condition_blocks.delete(name) + self.field_condition_blocks = prev _register_condition(relationship_condition_blocks, name, options) end diff --git a/lib/graphiti/sideload/polymorphic_belongs_to.rb b/lib/graphiti/sideload/polymorphic_belongs_to.rb index 9500dd3d..141ad5e1 100644 --- a/lib/graphiti/sideload/polymorphic_belongs_to.rb +++ b/lib/graphiti/sideload/polymorphic_belongs_to.rb @@ -7,7 +7,6 @@ def initialize(name) @calls = [] end - # rubocop: disable Style/MethodMissingSuper def method_missing(name, *args, &blk) @calls << [name, args, blk] end @@ -56,9 +55,9 @@ def apply(sideload, resource_class) args = call[1] opts = args.extract_options! opts.merge! as: sideload.name, - parent: sideload, - group_name: group.name, - polymorphic_child: true + parent: sideload, + group_name: group.name, + polymorphic_child: true unless sideload.resource.class.abstract_class? opts[:foreign_key] ||= sideload.foreign_key opts[:primary_key] ||= sideload.primary_key diff --git a/lib/graphiti/stats/dsl.rb b/lib/graphiti/stats/dsl.rb index f2a6e2a5..ea571120 100644 --- a/lib/graphiti/stats/dsl.rb +++ b/lib/graphiti/stats/dsl.rb @@ -46,7 +46,6 @@ def initialize(adapter, config) # # ...will hit +method_missing+ and store the proc for future reference. # @api private - # rubocop: disable Style/MethodMissingSuper def method_missing(meth, *args, &blk) @calculations[meth] = blk end diff --git a/lib/graphiti/util/serializer_relationships.rb b/lib/graphiti/util/serializer_relationships.rb index 5ab52b2e..70bf886d 100644 --- a/lib/graphiti/util/serializer_relationships.rb +++ b/lib/graphiti/util/serializer_relationships.rb @@ -118,7 +118,7 @@ def validate_link_for_sideload!(sideload) cache_key = :"#{@sideload.object_id}-#{action}" return if self.class.validated_link_cache.include?(cache_key) prc = Graphiti.config.context_for_endpoint - unless prc.call(sideload.resource.endpoint[:full_path], action) + unless prc.call(sideload.resource.endpoint[:full_path].to_s, action) raise Errors::InvalidLink.new(@resource_class, sideload, action) end self.class.validated_link_cache << cache_key diff --git a/lib/graphiti/util/simple_errors.rb b/lib/graphiti/util/simple_errors.rb index eab6a792..4191a23c 100644 --- a/lib/graphiti/util/simple_errors.rb +++ b/lib/graphiti/util/simple_errors.rb @@ -33,7 +33,7 @@ def each def size values.flatten.size end - alias count size + alias_method :count, :size def values messages.values.reject(&:empty?) @@ -48,7 +48,7 @@ def keys def empty? size.zero? end - alias blank? empty? + alias_method :blank?, :empty? def add(attribute, code, message: nil) message ||= "is #{code.to_s.humanize.downcase}" @@ -64,7 +64,7 @@ def added?(attribute, code) def full_messages map { |attribute, message| full_message(attribute, message) } end - alias to_a full_messages + alias_method :to_a, :full_messages def full_messages_for(attribute) attribute = attribute.to_sym diff --git a/lib/graphiti/version.rb b/lib/graphiti/version.rb index ca23ab61..04bbe7cc 100644 --- a/lib/graphiti/version.rb +++ b/lib/graphiti/version.rb @@ -1,3 +1,3 @@ module Graphiti - VERSION = "1.3.1" + VERSION = "1.3.9" end diff --git a/spec/configuration_spec.rb b/spec/configuration_spec.rb index 3d21b231..22acf1f4 100644 --- a/spec/configuration_spec.rb +++ b/spec/configuration_spec.rb @@ -26,7 +26,7 @@ # FIXME: Deprecated describe "when rails is defined" do - let(:logger) { double('debug?': false) } + let(:logger) { double(debug?: false) } let(:rails) do double(root: Pathname.new("/foo/bar"), logger: logger) @@ -53,7 +53,7 @@ # FIXME: Deprecated context "when rails logger is debug level" do - let(:logger) { double('debug?': true) } + let(:logger) { double(debug?: true) } it { is_expected.to eq(true) } end diff --git a/spec/delegates/pagination_spec.rb b/spec/delegates/pagination_spec.rb index cde74813..eb2611e3 100644 --- a/spec/delegates/pagination_spec.rb +++ b/spec/delegates/pagination_spec.rb @@ -148,7 +148,7 @@ def pagination_link(number, size: current_per_page) it "returns 0 if resource.stat(:total, :count) is nil" do expect(proxy.scope).to receive(:unpaginated_object) - expect(proxy.resource).to receive(:stat).with(:total, :count).and_return(lambda { |obj, meth| nil }) + expect(proxy.resource).to receive(:stat).with(:total, :count).and_return(lambda { |obj, meth| }) expect(subject).to eq 0 end diff --git a/spec/filtering_spec.rb b/spec/filtering_spec.rb index 9d634e00..613aa947 100644 --- a/spec/filtering_spec.rb +++ b/spec/filtering_spec.rb @@ -29,6 +29,20 @@ def self.name expect(records.map(&:id)).to eq([employee1.id]) end + context "retains filtering value" do + it "when value includes curly brackets" do + params[:filter] = {first_name: "{{John}}"} + records + expect(params[:filter]).to eq(first_name: "{{John}}") + end + + it "when value does not include curly brackets" do + params[:filter] = {first_name: "John"} + records + expect(params[:filter]).to eq(first_name: "John") + end + end + context "when filter is type hash" do before do resource.filter :by_json, :hash do @@ -433,11 +447,11 @@ def self.name context "one level" do let!(:pos1) do PORO::Position.create title: "foo", - employee_id: employee1.id + employee_id: employee1.id end let!(:pos2) do PORO::Position.create title: "bar", - employee_id: employee1.id + employee_id: employee1.id end before do @@ -460,11 +474,11 @@ def self.name let!(:department2) { PORO::Department.create(name: "bar") } let!(:pos1) do PORO::Position.create department_id: department1.id, - employee_id: employee1.id + employee_id: employee1.id end let!(:pos2) do PORO::Position.create department_id: department2.id, - employee_id: employee1.id + employee_id: employee1.id end before do @@ -920,7 +934,7 @@ def assert_filter_value(value) it "coerces integers" do params[:filter] = {foo: 40} - assert_filter_value([BigDecimal(40)]) + assert_filter_value([BigDecimal("40")]) end it "coerces strings" do @@ -1338,11 +1352,12 @@ def assert_filter_value(value) resource.attributes[:foo][:filterable] = false end - it "raises helpful error" do + it "makes it filterable" do expect { resource.filter :foo do end - }.to raise_error(Graphiti::Errors::AttributeError, "PORO::EmployeeResource: Tried to add filter attribute :foo, but the attribute was marked :filterable => false.") + }.to change { resource.attributes[:foo][:filterable] } + .from(false).to(true) end end end diff --git a/spec/fixtures/poro.rb b/spec/fixtures/poro.rb index 98272edf..31b0d842 100644 --- a/spec/fixtures/poro.rb +++ b/spec/fixtures/poro.rb @@ -58,8 +58,7 @@ def all(params) }.flatten records = apply_filtering(records, params) records = apply_sorting(records, params) - records = apply_pagination(records, params) - records + apply_pagination(records, params) end private @@ -196,7 +195,8 @@ class Employee < Base :credit_card_type, :payment_processor, :salary, - :credit_cards + :credit_cards, + :things def initialize(*) super @@ -305,15 +305,15 @@ def filter(scope, name, value) scope[:conditions][name] = value scope end - alias filter_integer_eq filter - alias filter_string_eq filter - alias filter_big_decimal_eq filter - alias filter_float_eq filter - alias filter_date_eq filter - alias filter_datetime_eq filter - alias filter_boolean_eq filter - alias filter_hash_eq filter - alias filter_array_eq filter + alias_method :filter_integer_eq, :filter + alias_method :filter_string_eq, :filter + alias_method :filter_big_decimal_eq, :filter + alias_method :filter_float_eq, :filter + alias_method :filter_date_eq, :filter + alias_method :filter_datetime_eq, :filter + alias_method :filter_boolean_eq, :filter + alias_method :filter_hash_eq, :filter + alias_method :filter_array_eq, :filter # No need for actual logic to fire def count(scope, attr) diff --git a/spec/integration/rails/cursor_pagination_spec.rb b/spec/integration/rails/cursor_pagination_spec.rb index 261a6c11..25539481 100644 --- a/spec/integration/rails/cursor_pagination_spec.rb +++ b/spec/integration/rails/cursor_pagination_spec.rb @@ -116,7 +116,7 @@ def ids do_index(sort: "last_login", page: {before: cursor, size: 2}) expect(ids).to eq([author3.id, author2.id]) end - + it "works desc" do do_index(sort: "-last_login") expect(ids).to eq([author4.id, author2.id, author3.id, author1.id]) @@ -129,4 +129,4 @@ def ids end end end -end \ No newline at end of file +end diff --git a/spec/integration/rails/finders_spec.rb b/spec/integration/rails/finders_spec.rb index 066d6dc5..325d0abd 100644 --- a/spec/integration/rails/finders_spec.rb +++ b/spec/integration/rails/finders_spec.rb @@ -24,29 +24,29 @@ def resource let!(:author1) do Legacy::Author.create! first_name: "Stephen", - age: 70, - active: true, - float_age: 70.03, - decimal_age: 70.033, - state: state, - organization: org1, - dwelling: house, - created_at: one_day_ago, - last_login: one_day_ago, - created_at_date: one_day_ago.to_date, - identifier: SecureRandom.uuid + age: 70, + active: true, + float_age: 70.03, + decimal_age: 70.033, + state: state, + organization: org1, + dwelling: house, + created_at: one_day_ago, + last_login: one_day_ago, + created_at_date: one_day_ago.to_date, + identifier: SecureRandom.uuid end let!(:author2) do Legacy::Author.create! first_name: "George", - age: 65, - active: false, - float_age: 70.01, - decimal_age: 70.011, - dwelling: condo, - created_at: two_days_ago, - last_login: one_day_ago, - created_at_date: two_days_ago.to_date, - identifier: SecureRandom.uuid + age: 65, + active: false, + float_age: 70.01, + decimal_age: 70.011, + dwelling: condo, + created_at: two_days_ago, + last_login: one_day_ago, + created_at_date: two_days_ago.to_date, + identifier: SecureRandom.uuid end let!(:book1) { Legacy::Book.create!(author: author1, genre: genre, title: "The Shining") } let!(:book2) { Legacy::Book.create!(author: author1, genre: genre, title: "The Stand") } @@ -208,14 +208,14 @@ def resource let!(:author3) do Legacy::Author.create! first_name: "GeOrge", - age: 72, - identifier: "AbC123", - active: true, - float_age: 70.05, - decimal_age: 70.055, - last_login: nil, - created_at: 1.day.from_now, - created_at_date: 1.day.from_now.to_date + age: 72, + identifier: "AbC123", + active: true, + float_age: 70.05, + decimal_age: 70.055, + last_login: nil, + created_at: 1.day.from_now, + created_at_date: 1.day.from_now.to_date end context "when multiple operators" do diff --git a/spec/integration/rails/persistence_spec.rb b/spec/integration/rails/persistence_spec.rb index e72a3bca..420db6aa 100644 --- a/spec/integration/rails/persistence_spec.rb +++ b/spec/integration/rails/persistence_spec.rb @@ -807,12 +807,10 @@ def self.name context "when a has_many relationship has validation error" do around do |e| - begin - Position.validates :title, presence: true - e.run - ensure - Position.clear_validators! - end + Position.validates :title, presence: true + e.run + ensure + Position.clear_validators! end before do @@ -848,12 +846,10 @@ def self.name context "when a belongs_to relationship has a validation error" do around do |e| - begin - Department.validates :name, presence: true - e.run - ensure - Department.clear_validators! - end + Department.validates :name, presence: true + e.run + ensure + Department.clear_validators! end before do @@ -889,12 +885,10 @@ def self.name context "when a many_to_many relationship has a validation error" do around do |e| - begin - Team.validates :name, presence: true - e.run - ensure - Team.clear_validators! - end + Team.validates :name, presence: true + e.run + ensure + Team.clear_validators! end before do @@ -1496,7 +1490,7 @@ def self.name context "when destroying" do let!(:location) do Location.create locatable_id: employee.id, - locatable_type: "Employee" + locatable_type: "Employee" end let(:location_id) { location.id.to_s } let(:location_id_key) { :id } @@ -1511,7 +1505,7 @@ def self.name context "when disassociating" do let!(:location) do Location.create locatable_id: employee.id, - locatable_type: "Employee" + locatable_type: "Employee" end let(:location_id) { location.id.to_s } let(:location_id_key) { :id } @@ -1582,7 +1576,7 @@ def self.name context "when destroying" do let!(:note) do Note.create notable_id: employee.id, - notable_type: "Employee" + notable_type: "Employee" end let(:note_id) { note.id.to_s } let(:note_id_key) { :id } @@ -1599,7 +1593,7 @@ def self.name context "when disassociating" do let!(:note) do Note.create notable_id: employee.id, - notable_type: "Employee" + notable_type: "Employee" end let(:note_id) { note.id.to_s } let(:note_id_key) { :id } diff --git a/spec/integration/rails/stats_spec.rb b/spec/integration/rails/stats_spec.rb new file mode 100644 index 00000000..41b54a97 --- /dev/null +++ b/spec/integration/rails/stats_spec.rb @@ -0,0 +1,40 @@ +if ENV["APPRAISAL_INITIALIZED"] + RSpec.describe "stats" do + let!(:pos1) { Position.create!(title: "a", employee_id: 1) } + let!(:pos2) { Position.create!(title: "b", employee_id: 2) } + let!(:pos3) { Position.create!(title: "c", employee_id: 2) } + let!(:pos4) { Position.create!(title: "d", employee_id: 2) } + let!(:pos5) { Position.create!(title: "e", employee_id: 3) } + let!(:pos6) { Position.create!(title: "f", employee_id: 3) } + + after do + Position.delete_all + end + + context "basic" do + it "works" do + proxy = PositionResource.all(stats: {total: "count"}) + expect(proxy.stats).to eq({ + total: { + count: 6 + } + }) + end + end + + context "when grouping" do + it "works" do + proxy = PositionResource.all(stats: {total: "count", group_by: :employee_id}) + expect(proxy.stats).to eq({ + total: { + count: { + 1 => 1, + 2 => 3, + 3 => 2 + } + } + }) + end + end + end +end diff --git a/spec/persistence_spec.rb b/spec/persistence_spec.rb index 2c9226f6..0911d0d6 100644 --- a/spec/persistence_spec.rb +++ b/spec/persistence_spec.rb @@ -27,12 +27,7 @@ def self.name def expect_errors(object, expected) errors = object.errors.full_messages - if RUBY_VERSION.to_i < 3 - expect(errors).to eq(expected) - else - # This appears to be a Rails issue - expect(errors[0]).to include("translation missing") - end + expect(errors).to eq(expected) end it "can persist single entities" do @@ -1940,11 +1935,11 @@ def save(value) end it "coerces integers" do - expect(save(1)).to eq(BigDecimal(1)) + expect(save(1)).to eq(BigDecimal("1")) end it "coerces strings" do - expect(save("1")).to eq(BigDecimal(1)) + expect(save("1")).to eq(BigDecimal("1")) end it "allows nils" do diff --git a/spec/query_spec.rb b/spec/query_spec.rb index a2e3bfe2..1d72bc51 100644 --- a/spec/query_spec.rb +++ b/spec/query_spec.rb @@ -11,6 +11,7 @@ before do employee_resource.has_many :positions, resource: position_resource + employee_resource.belongs_to :remote, remote: true position_resource.belongs_to :department, resource: department_resource employee_resource.attribute :name, :string @@ -986,11 +987,29 @@ end describe "sideloads" do - before { params[:include] = "positions" } subject(:sideloads) { instance.sideloads } - it "does not cascate the action" do - expect(sideloads.values.map(&:action)).to eq([:all]) + context "when including an has_many resource" do + before { params[:include] = "positions" } + + it "does not cascate the action" do + expect(sideloads.values.map(&:action)).to eq([:all]) + end + end + + context "when including a resource from a remote resource" do + before { params[:include] = "remote.resource" } + + let(:sideloads_of_another_query) { described_class.new(resource, params).sideloads } + + def resource_class_of_remote_sideload(sideloads) + sideloads.fetch(:remote).sideloads.fetch(:resource).resource.class + end + + it "re-uses resource class across multiple queries (avoid memory leak)" do + expect(resource_class_of_remote_sideload(sideloads)) + .to eq(resource_class_of_remote_sideload(sideloads_of_another_query)) + end end end end diff --git a/spec/relationship_identifier_spec.rb b/spec/relationship_identifier_spec.rb index 2ecc75f9..e9d9c1e8 100644 --- a/spec/relationship_identifier_spec.rb +++ b/spec/relationship_identifier_spec.rb @@ -10,11 +10,11 @@ let!(:employee2) { PORO::Employee.create } let!(:position1) do PORO::Position.create employee_id: employee.id, - department_id: department1.id + department_id: department1.id end let!(:position2) do PORO::Position.create employee_id: employee.id, - department_id: department2.id + department_id: department2.id end let!(:department1) { PORO::Department.create } let!(:department2) { PORO::Department.create } diff --git a/spec/rendering_spec.rb b/spec/rendering_spec.rb index eb377cfb..5f6ed247 100644 --- a/spec/rendering_spec.rb +++ b/spec/rendering_spec.rb @@ -13,25 +13,25 @@ def self.name let!(:employee1) do PORO::Employee.create first_name: "John", - last_name: "Doe", - age: 33 + last_name: "Doe", + age: 33 end let!(:employee2) do PORO::Employee.create first_name: "Jane", - last_name: "Dougherty", - age: 44 + last_name: "Dougherty", + age: 44 end let!(:position1) do PORO::Position.create title: "title1", - rank: 1, - employee_id: 1, - department_id: 1 + rank: 1, + employee_id: 1, + department_id: 1 end let!(:position2) do PORO::Position.create title: "title2", - rank: 2, - employee_id: 2, - department_id: 2 + rank: 2, + employee_id: 2, + department_id: 2 end let!(:department1) do PORO::Department.create(name: "dep1", description: "dep1desc") @@ -586,6 +586,21 @@ def self.name }]) end end + + context "when a multi-word stat" do + before do + resource.stat multi_word: [:average] do + average do + 10 + end + end + params[:stats] = {multi_word: "average"} + end + + it "is camelized" do + expect(json[:employees][:stats].keys.first).to eq(:multiWord) + end + end end end diff --git a/spec/resource_spec.rb b/spec/resource_spec.rb index e310c6ff..db1e9e4c 100644 --- a/spec/resource_spec.rb +++ b/spec/resource_spec.rb @@ -1770,8 +1770,7 @@ def around_scoping(scope, query_hash) scope = scope.merge(before: true) yield scope context.after = scope.dup - scope = scope.merge(after: true) - scope + scope.merge(after: true) end def resolve(scope) diff --git a/spec/schema_spec.rb b/spec/schema_spec.rb index bd8fbea7..9d87778c 100644 --- a/spec/schema_spec.rb +++ b/spec/schema_spec.rb @@ -533,6 +533,28 @@ def self.name end end + context "when the attribute is schema: false then .filter called" do + before do + employee_resource.filter :hidden_attribute + end + + it "appears in the schema" do + expect(schema[:resources][0][:filters].key?(:hidden_attribute)) + .to eq(true) + end + + context "when passed schema: false at filter level" do + before do + employee_resource.filter :hidden_attribute, schema: false + end + + it "does not appear in the schema" do + expect(schema[:resources][0][:filters].key?(:hidden_attribute)) + .to eq(false) + end + end + end + context "when attribute changes to schema true" do before do employee_resource.class_eval do @@ -559,6 +581,44 @@ def self.name end end + context "when extra attribute has schema false" do + before do + employee_resource.class_eval do + extra_attribute :net_sales, :float, schema: false + end + end + + it "is not in the list of extra_attributes" do + expect(schema[:resources][0][:extra_attributes]).not_to have_key(:net_sales) + end + end + + context "when extra attribute is also a filter" do + before do + employee_resource.class_eval do + extra_attribute :net_sales, :float, filterable: true + filter :net_sales, only: [:eq] + end + end + + it "is in the list of filters" do + expect(schema[:resources][0][:filters]).to have_key(:net_sales) + end + end + + context "when extra attribute is also a sort" do + before do + employee_resource.class_eval do + extra_attribute :net_sales, :float, sortable: true + sort :net_sales + end + end + + it "is in the list of sorts" do + expect(schema[:resources][0][:sorts]).to have_key(:net_sales) + end + end + context "when an additional statistic/calculations" do before do employee_resource.stat age: [:average, :sum] @@ -941,12 +1001,10 @@ def self.name context "but FORCE_SCHEMA set" do around do |e| - begin - ENV["FORCE_SCHEMA"] = "true" - e.run - ensure - ENV["FORCE_SCHEMA"] = nil - end + ENV["FORCE_SCHEMA"] = "true" + e.run + ensure + ENV["FORCE_SCHEMA"] = nil end it "writes the file" do diff --git a/spec/serialization_spec.rb b/spec/serialization_spec.rb index fb14d99b..7157b47f 100644 --- a/spec/serialization_spec.rb +++ b/spec/serialization_spec.rb @@ -1720,7 +1720,9 @@ def classification context "when specified" do before do - resource.link :test_link do |model| "#{endpoint[:url]}/#{model.id}" end + resource.link :test_link do |model| + "#{endpoint[:url]}/#{model.id}" + end end it "links correctly" do @@ -1732,7 +1734,9 @@ def classification context "nil links" do before do - resource.link :test_link do |model| nil end + resource.link :test_link do |model| + nil + end end specify "are still included" do @@ -1804,51 +1808,49 @@ def classification context "when the attribute is guarded, and the guard fails" do before do + # NB - add 'things' here rather than relying on existing relationships + # This is because order matters - the relationship has to be applied + # *only after* the attribute to truly tests things. This is what happens + # in production when eager loading code, so we should mimic here. resource.class_eval do attribute :first_name, :string - attribute :positions, :string, readable: :admin? + attribute :things, :string, readable: :admin? do + "things!" + end def admin? false end + has_many :things, resource: PORO::PositionResource, + foreign_key: :employee_id end end context "and the relationship is not included" do - before do - params[:include] = "positions" - end - it "does not render the attribute, does render the relationship" do render - expect(json["data"][0]["attributes"]).to_not have_key("positions") - resource_id = {"id" => position.id.to_s, "type" => "positions"} - expect(json["data"][0]["relationships"]["positions"]["data"]) - .to eq([resource_id]) - expect(json["included"][0].slice("id", "type")).to eq(resource_id) + expect(json["data"][0]["attributes"]).to_not have_key("things") + expect(json["data"][0]["relationships"].key?("things")) + .to eq(true) end context "and rendering JSON" do - it "still renders the relationship" do + it "is not present" do json = proxy.as_json[:data][0] - expect(json[:positions]).to eq([{ - id: position.id.to_s, - title: "foo", - rank: nil - }]) + expect(json.key?(:things)).to eq(false) end end end context "and the relationship is included" do before do - params[:include] = "positions" + params[:include] = "things" end it "is still rendered" do render - expect(json["data"][0]["attributes"]).to_not have_key("positions") + expect(json["data"][0]["attributes"]).to_not have_key("things") resource_id = {"id" => position.id.to_s, "type" => "positions"} - expect(json["data"][0]["relationships"]["positions"]["data"]) + expect(json["data"][0]["relationships"]["things"]["data"]) .to eq([resource_id]) expect(json["included"][0].slice("id", "type")).to eq(resource_id) end @@ -1856,7 +1858,7 @@ def admin? context "and rendering JSON" do it "still renders the relationship" do json = proxy.as_json[:data][0] - expect(json[:positions]).to eq([{ + expect(json[:things]).to eq([{ id: position.id.to_s, title: "foo", rank: nil @@ -1995,7 +1997,7 @@ def get_cursor(index) cursor = Base64.encode64({offset: 2}.to_json) params[:page] = {after: cursor} end - + xit "TODO" do render expect(get_cursor(0)).to eq(offset: 2) diff --git a/spec/sideloading_spec.rb b/spec/sideloading_spec.rb index d50be655..767b4695 100644 --- a/spec/sideloading_spec.rb +++ b/spec/sideloading_spec.rb @@ -26,11 +26,11 @@ class PositionSideload < ::Graphiti::Sideload::HasMany let!(:employee) { PORO::Employee.create } let!(:position1) do PORO::Position.create employee_id: employee.id, - department_id: department1.id + department_id: department1.id end let!(:position2) do PORO::Position.create employee_id: employee.id, - department_id: department2.id + department_id: department2.id end let!(:department1) { PORO::Department.create } let!(:department2) { PORO::Department.create } @@ -340,16 +340,16 @@ def self.name let!(:paypal) { PORO::Paypal.create } let!(:employee2) do PORO::Employee.create credit_card_type: "Mastercard", - credit_card_id: mastercard.id + credit_card_id: mastercard.id end let!(:employee3) do PORO::Employee.create credit_card_type: "Paypal", - credit_card_id: paypal.id + credit_card_id: paypal.id end before do employee.update_attributes credit_card_type: "Visa", - credit_card_id: visa.id + credit_card_id: visa.id params[:include] = "credit_card" end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 13f130d3..26485819 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -13,6 +13,7 @@ # Optional dep for cross-api requests require "faraday" +require "base64" RSpec.configure do |config| config.include GraphitiSpecHelpers::RSpec @@ -60,6 +61,6 @@ def check! require "rails_spec_helper" ActiveRecord::Migration.verbose = false ActiveRecord::Base.establish_connection adapter: "sqlite3", - database: ":memory:" + database: ":memory:" Dir[File.dirname(__FILE__) + "/fixtures/**/*.rb"].sort.each { |f| require f } end diff --git a/spec/stats_spec.rb b/spec/stats_spec.rb index e4e71d2b..0a943654 100644 --- a/spec/stats_spec.rb +++ b/spec/stats_spec.rb @@ -7,11 +7,11 @@ let!(:employee1) do PORO::Employee.create first_name: "Stephen", - last_name: "King" + last_name: "King" end let!(:employee1) do PORO::Employee.create first_name: "Stephen", - last_name: "King" + last_name: "King" end context "when total count requested" do diff --git a/spec/support/pagination_link_helper.rb b/spec/support/pagination_link_helper.rb index a684ae07..dec05376 100644 --- a/spec/support/pagination_link_helper.rb +++ b/spec/support/pagination_link_helper.rb @@ -30,9 +30,9 @@ def self.name let(:pagination_delegate) { Graphiti::Delegates::Pagination.new(proxy) } let(:collection) do double(total_pages: total_pages, - prev_page: prev_page, - next_page: next_page, - current_per_page: current_per_page) + prev_page: prev_page, + next_page: next_page, + current_per_page: current_per_page) end let(:total_pages) { 3 } let(:prev_page) { 1 }