From 4db3213db37c169e38adfc50b005da269b5cd7b9 Mon Sep 17 00:00:00 2001 From: "Daniel (dB.) Doubrovkine" Date: Wed, 24 Sep 2025 13:04:26 -0400 Subject: [PATCH 1/3] Expanded test matrix. --- .github/workflows/test-activerecord.yml | 4 +++- .github/workflows/test-mongodb.yml | 3 ++- .rubocop_todo.yml | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test-activerecord.yml b/.github/workflows/test-activerecord.yml index 6bf3fce..fb67139 100644 --- a/.github/workflows/test-activerecord.yml +++ b/.github/workflows/test-activerecord.yml @@ -7,7 +7,9 @@ jobs: strategy: matrix: entry: - - { ruby: '3.4', postgresql: '17', activerecord: '~> 8.0.2', grape: '2.4.0' } + - { ruby: '3.4', postgresql: '15', activerecord: '~> 6.1.0', grape: '~> 1.8.0' } + - { ruby: '3.4', postgresql: '16', activerecord: '~> 7.2.0', grape: '~> 2.4.0' } + - { ruby: '3.4', postgresql: '17', activerecord: '~> 8.0.3', grape: '~> 2.4.0' } name: test (ruby=${{ matrix.entry.ruby }}, postgresql=${{ matrix.entry.postgresql }}, activerecord=${{ matrix.entry.activerecord }}, grape=${{ matrix.entry.grape }}) env: ACTIVERECORD_VERSION: ${{ matrix.entry.activerecord }} diff --git a/.github/workflows/test-mongodb.yml b/.github/workflows/test-mongodb.yml index 41fd19c..30118ea 100644 --- a/.github/workflows/test-mongodb.yml +++ b/.github/workflows/test-mongodb.yml @@ -7,7 +7,8 @@ jobs: strategy: matrix: entry: - - { ruby: '3.4', mongoid: '6.4.8', mongodb: '6.0', grape: '1.7.0' } + - { ruby: '3.4', mongoid: '~> 6.4.8', mongodb: '6.0', grape: '~> 1.7.0' } + - { ruby: '3.4', mongoid: '~> 6.4.8', mongodb: '6.0', grape: '~> 2.0.0' } name: test (ruby=${{ matrix.entry.ruby }}, mongoid=${{ matrix.entry.mongoid }}, mongodb=${{ matrix.entry.mongodb }}, grape=${{ matrix.entry.grape }}) env: MONGOID_VERSION: ${{ matrix.entry.mongoid }} diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 5e7e134..2e5b939 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,6 +1,6 @@ # This configuration was generated by # `rubocop --auto-gen-config` -# on 2025-09-24 16:57:49 UTC using RuboCop version 1.80.2. +# on 2025-09-24 17:33:03 UTC using RuboCop version 1.80.2. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new From 597fc05fdaf3d390e3914e2ef28c0cdf5976fd46 Mon Sep 17 00:00:00 2001 From: "Daniel (dB.) Doubrovkine" Date: Wed, 24 Sep 2025 18:58:01 -0400 Subject: [PATCH 2/3] Added support for Mongoid 7. --- .github/workflows/test-mongodb.yml | 1 + .rubocop.yml | 3 - .rubocop_todo.yml | 135 +++++++++++++++++- CHANGELOG.md | 1 + Gemfile | 4 +- README.md | 6 +- .../roar/extensions/relations/adapters.rb | 4 +- .../extensions/relations/adapters/mongoid.rb | 2 +- .../relations/validations/mongoid.rb | 83 +---------- .../relations/validations/mongoid/6.rb | 82 +++++++++++ .../relations/validations/mongoid/7.rb | 75 ++++++++++ spec/decorator_spec.rb | 2 - .../relations/adapters/active_record_spec.rb | 38 ++--- .../adapters/adapters_module_spec.rb | 5 +- .../relations/adapters/mongoid_spec.rb | 38 ++--- spec/extensions/relations/dsl_methods_spec.rb | 26 ++-- spec/extensions/relations/mapper_spec.rb | 20 +-- .../validations/active_record_spec.rb | 67 ++++----- .../relations/validations/mongoid_spec.rb | 125 ++++++++-------- spec/nested_representer_spec.rb | 2 - spec/present_with_spec.rb | 2 - spec/relations_spec.rb | 11 +- spec/representer_spec.rb | 12 +- spec/spec_helper.rb | 1 + spec/support/all/grape_app_context.rb | 2 - 25 files changed, 482 insertions(+), 265 deletions(-) create mode 100644 lib/grape/roar/extensions/relations/validations/mongoid/6.rb create mode 100644 lib/grape/roar/extensions/relations/validations/mongoid/7.rb diff --git a/.github/workflows/test-mongodb.yml b/.github/workflows/test-mongodb.yml index 30118ea..acc3743 100644 --- a/.github/workflows/test-mongodb.yml +++ b/.github/workflows/test-mongodb.yml @@ -9,6 +9,7 @@ jobs: entry: - { ruby: '3.4', mongoid: '~> 6.4.8', mongodb: '6.0', grape: '~> 1.7.0' } - { ruby: '3.4', mongoid: '~> 6.4.8', mongodb: '6.0', grape: '~> 2.0.0' } + - { ruby: '3.4', mongoid: '~> 7.5.4', mongodb: '7.0', grape: '~> 2.4.0' } name: test (ruby=${{ matrix.entry.ruby }}, mongoid=${{ matrix.entry.mongoid }}, mongodb=${{ matrix.entry.mongodb }}, grape=${{ matrix.entry.grape }}) env: MONGOID_VERSION: ${{ matrix.entry.mongoid }} diff --git a/.rubocop.yml b/.rubocop.yml index 340e4ed..a388da0 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -2,9 +2,6 @@ AllCops: TargetRubyVersion: 3.0 NewCops: enable Exclude: - - bin/**/* - - gemfiles/**/* - - spec/**/* - vendor/**/* inherit_from: .rubocop_todo.yml diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 2e5b939..c4238fb 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,6 +1,6 @@ # This configuration was generated by # `rubocop --auto-gen-config` -# on 2025-09-24 17:33:03 UTC using RuboCop version 1.80.2. +# on 2025-09-25 02:12:55 UTC using RuboCop version 1.80.2. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new @@ -26,7 +26,7 @@ Naming/FileName: - 'Rakefile.rb' - 'lib/grape-roar.rb' -# Offense count: 6 +# Offense count: 9 # Configuration parameters: NamePrefix, ForbiddenPrefixes, AllowedMethods, MethodDefinitionMacros, UseSorbetSigs. # NamePrefix: is_, has_, have_, does_ # ForbiddenPrefixes: is_, has_, have_, does_ @@ -36,9 +36,121 @@ Naming/PredicatePrefix: Exclude: - 'spec/**/*' - 'lib/grape/roar/extensions/relations/validations/active_record.rb' - - 'lib/grape/roar/extensions/relations/validations/mongoid.rb' + - 'lib/grape/roar/extensions/relations/validations/mongoid/6.rb' + - 'lib/grape/roar/extensions/relations/validations/mongoid/7.rb' -# Offense count: 14 +# Offense count: 8 +# This cop supports unsafe autocorrection (--autocorrect-all). +RSpec/BeEql: + Exclude: + - 'spec/extensions/relations/adapters/active_record_spec.rb' + - 'spec/extensions/relations/adapters/mongoid_spec.rb' + - 'spec/extensions/relations/validations/active_record_spec.rb' + - 'spec/extensions/relations/validations/mongoid_spec.rb' + - 'spec/relations_spec.rb' + +# Offense count: 11 +# Configuration parameters: Prefixes, AllowedPatterns. +# Prefixes: when, with, without +RSpec/ContextWording: + Exclude: + - 'spec/decorator_spec.rb' + - 'spec/extensions/relations/validations/mongoid_spec.rb' + - 'spec/nested_representer_spec.rb' + - 'spec/present_with_spec.rb' + - 'spec/relations_spec.rb' + - 'spec/representer_spec.rb' + - 'spec/support/all/grape_app_context.rb' + +# Offense count: 4 +# Configuration parameters: CountAsOne. +RSpec/ExampleLength: + Max: 14 + +# Offense count: 3 +RSpec/ExpectInHook: + Exclude: + - 'spec/extensions/relations/dsl_methods_spec.rb' + - 'spec/extensions/relations/validations/active_record_spec.rb' + - 'spec/extensions/relations/validations/mongoid_spec.rb' + +# Offense count: 1 +RSpec/MessageChain: + Exclude: + - 'spec/extensions/relations/dsl_methods_spec.rb' + +# Offense count: 18 +# Configuration parameters: . +# SupportedStyles: have_received, receive +RSpec/MessageSpies: + EnforcedStyle: receive + +# Offense count: 10 +RSpec/MultipleExpectations: + Max: 5 + +# Offense count: 58 +# Configuration parameters: EnforcedStyle, IgnoreSharedExamples. +# SupportedStyles: always, named_only +RSpec/NamedSubject: + Exclude: + - 'spec/decorator_spec.rb' + - 'spec/extensions/relations/adapters/active_record_spec.rb' + - 'spec/extensions/relations/adapters/mongoid_spec.rb' + - 'spec/extensions/relations/dsl_methods_spec.rb' + - 'spec/extensions/relations/mapper_spec.rb' + - 'spec/extensions/relations/validations/active_record_spec.rb' + - 'spec/extensions/relations/validations/mongoid_spec.rb' + - 'spec/nested_representer_spec.rb' + - 'spec/present_with_spec.rb' + - 'spec/relations_spec.rb' + - 'spec/representer_spec.rb' + - 'spec/support/all/grape_app_context.rb' + +# Offense count: 2 +# Configuration parameters: AllowedGroups. +RSpec/NestedGroups: + Max: 4 + +# Offense count: 2 +# This cop supports unsafe autocorrection (--autocorrect-all). +RSpec/ReceiveMessages: + Exclude: + - 'spec/extensions/relations/mapper_spec.rb' + +# Offense count: 8 +# Configuration parameters: CustomTransform, IgnoreMethods, IgnoreMetadata. +RSpec/SpecFilePathFormat: + Exclude: + - '**/spec/routing/**/*' + - 'spec/decorator_spec.rb' + - 'spec/extensions/relations/adapters/adapters_module_spec.rb' + - 'spec/extensions/relations/dsl_methods_spec.rb' + - 'spec/extensions/relations/mapper_spec.rb' + - 'spec/nested_representer_spec.rb' + - 'spec/present_with_spec.rb' + - 'spec/relations_spec.rb' + - 'spec/representer_spec.rb' + +# Offense count: 3 +RSpec/StubbedMock: + Exclude: + - 'spec/extensions/relations/dsl_methods_spec.rb' + - 'spec/extensions/relations/mapper_spec.rb' + +# Offense count: 11 +RSpec/SubjectStub: + Exclude: + - 'spec/extensions/relations/dsl_methods_spec.rb' + - 'spec/extensions/relations/mapper_spec.rb' + +# Offense count: 1 +# This cop supports unsafe autocorrection (--autocorrect-all). +RSpec/VerifiedDoubleReference: + Exclude: + - 'spec/extensions/relations/mapper_spec.rb' + +# Offense count: 15 # Configuration parameters: AllowedConstants. Style/Documentation: Exclude: @@ -54,13 +166,26 @@ Style/Documentation: - 'lib/grape/roar/extensions/relations/mapper.rb' - 'lib/grape/roar/extensions/relations/validations/active_record.rb' - 'lib/grape/roar/extensions/relations/validations/misc.rb' - - 'lib/grape/roar/extensions/relations/validations/mongoid.rb' + - 'lib/grape/roar/extensions/relations/validations/mongoid/6.rb' + - 'lib/grape/roar/extensions/relations/validations/mongoid/7.rb' - 'lib/grape/roar/formatter.rb' - 'lib/grape/roar/representer.rb' +# Offense count: 2 +Style/OpenStructUse: + Exclude: + - 'spec/extensions/relations/dsl_methods_spec.rb' + # Offense count: 1 # Configuration parameters: AllowedMethods. # AllowedMethods: respond_to_missing? Style/OptionalBooleanParameter: Exclude: - 'lib/grape/roar/extensions/relations/dsl_methods.rb' + +# Offense count: 2 +# This cop supports safe autocorrection (--autocorrect). +# Configuration parameters: AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, IgnoreCopDirectives, AllowedPatterns, SplitStrings. +# URISchemes: http, https +Layout/LineLength: + Max: 304 diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d2b12f..1c64c47 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ ### 0.5.0 (Next) +* [#31](https://github.com/ruby-grape/grape-roar/pull/31): Add support for Mongoid 7 - [@dblock](https://github.com/dblock). * [#23](https://github.com/ruby-grape/grape-roar/pull/23): Resolves pollution issue with invoking representers on singletons - [@mach-kernel](https://github.com/mach-kernel). * [#28](https://github.com/ruby-grape/grape-roar/pull/28): Replaced Travis-CI with GHA - [@dblock](https://github.com/dblock). * Your contribution here. diff --git a/Gemfile b/Gemfile index 5a5363f..6c091fd 100644 --- a/Gemfile +++ b/Gemfile @@ -2,12 +2,12 @@ source 'https://rubygems.org' -gemspec - gem 'activerecord', ENV['ACTIVERECORD_VERSION'], require: 'active_record' if ENV.key?('ACTIVERECORD_VERSION') gem 'grape', ENV['GRAPE_VERSION'] if ENV.key?('GRAPE_VERSION') gem 'mongoid', ENV['MONGOID_VERSION'], require: 'mongoid' if ENV.key?('MONGOID_VERSION') +gemspec + group :test do gem 'rack-test' gem 'rspec' diff --git a/README.md b/README.md index 1a8e56a..7ae91bc 100644 --- a/README.md +++ b/README.md @@ -246,11 +246,11 @@ Although this example uses `Grape::Roar::Decorator`, you can also use a module a #### Errors -Should you incorrectly describe a relationship (e.g. you specify has_one but your model specifies has_many), an exception will be raised to notify you of the mismatch: +Should you incorrectly describe a relationship (e.g. you specify `has_one` but your model specifies `has_many`), an exception will be raised to notify you of the mismatch: ```ruby Grape::Roar::Extensions::Relations::Exceptions::InvalidRelationError: - Expected Mongoid::Relations::Referenced::One, got Mongoid::Relations::Referenced::Many! + Expected Mongoid::Association::Referenced::HasOne, got Mongoid::Association::Referenced::HasMany! ``` #### Change how URLs are presented @@ -308,7 +308,7 @@ module Extensions include Validations::ActiveRecord # We map your domain object to the correct adapter - # during runtime. + # at runtime. valid_for { |klass| klass < ::ActiveRecord::Base } def collection_methods diff --git a/lib/grape/roar/extensions/relations/adapters.rb b/lib/grape/roar/extensions/relations/adapters.rb index cbcb103..a1f32aa 100644 --- a/lib/grape/roar/extensions/relations/adapters.rb +++ b/lib/grape/roar/extensions/relations/adapters.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true require 'grape/roar/extensions/relations/adapters/base' -require 'grape/roar/extensions/relations/adapters/active_record' -require 'grape/roar/extensions/relations/adapters/mongoid' +require 'grape/roar/extensions/relations/adapters/active_record' if defined?(ActiveRecord) +require 'grape/roar/extensions/relations/adapters/mongoid' if defined?(Mongoid) module Grape module Roar diff --git a/lib/grape/roar/extensions/relations/adapters/mongoid.rb b/lib/grape/roar/extensions/relations/adapters/mongoid.rb index b75bf9d..5059298 100644 --- a/lib/grape/roar/extensions/relations/adapters/mongoid.rb +++ b/lib/grape/roar/extensions/relations/adapters/mongoid.rb @@ -18,7 +18,7 @@ def collection_methods def name_for_represented(represented) klass_name = if represented.instance_of?( - ::Mongoid::Relations::Targets::Enumerable + ::Enumerable ) represented.klass.name else diff --git a/lib/grape/roar/extensions/relations/validations/mongoid.rb b/lib/grape/roar/extensions/relations/validations/mongoid.rb index fa71bf4..a75830d 100644 --- a/lib/grape/roar/extensions/relations/validations/mongoid.rb +++ b/lib/grape/roar/extensions/relations/validations/mongoid.rb @@ -1,82 +1,9 @@ # frozen_string_literal: true -module Grape - module Roar - module Extensions - module Relations - module Validations - module Mongoid - include Validations::Misc - - def belongs_to_valid?(relation) - relation = klass.reflect_on_association(relation) - - return true if relation[:relation] == - ::Mongoid::Relations::Referenced::In - - invalid_relation( - ::Mongoid::Relations::Referenced::In, relation[:relation] - ) - end - - def embeds_many_valid?(relation) - relation = klass.reflect_on_association(relation) - - return true if relation[:relation] == - ::Mongoid::Relations::Embedded::Many - - invalid_relation( - ::Mongoid::Relations::Embedded::Many, relation[:relation] - ) - end - - def embeds_one_valid?(relation) - relation = klass.reflect_on_association(relation) - - return true if relation[:relation] == - ::Mongoid::Relations::Embedded::One - - invalid_relation( - ::Mongoid::Relations::Embedded::One, relation[:relation] - ) - end - - def has_many_valid?(relation) - relation = klass.reflect_on_association(relation) - - return true if relation[:relation] == - ::Mongoid::Relations::Referenced::Many - - invalid_relation( - ::Mongoid::Relations::Referenced::Many, relation[:relation] - ) - end - - def has_and_belongs_to_many_valid?(relation) - relation = klass.reflect_on_association(relation) - - return true if relation[:relation] == - ::Mongoid::Relations::Referenced::ManyToMany - - invalid_relation( - ::Mongoid::Relations::Referenced::ManyToMany, - relation[:relation] - ) - end - - def has_one_valid?(relation) - relation = klass.reflect_on_association(relation) - - return true if relation[:relation] == - ::Mongoid::Relations::Referenced::One - - invalid_relation( - ::Mongoid::Relations::Referenced::One, relation[:relation] - ) - end - end - end - end - end +if defined?(Mongoid) + if Gem::Version.new(Mongoid::VERSION) < Gem::Version.new('7') + require_relative 'mongoid/6' + else + require_relative 'mongoid/7' end end diff --git a/lib/grape/roar/extensions/relations/validations/mongoid/6.rb b/lib/grape/roar/extensions/relations/validations/mongoid/6.rb new file mode 100644 index 0000000..fa71bf4 --- /dev/null +++ b/lib/grape/roar/extensions/relations/validations/mongoid/6.rb @@ -0,0 +1,82 @@ +# frozen_string_literal: true + +module Grape + module Roar + module Extensions + module Relations + module Validations + module Mongoid + include Validations::Misc + + def belongs_to_valid?(relation) + relation = klass.reflect_on_association(relation) + + return true if relation[:relation] == + ::Mongoid::Relations::Referenced::In + + invalid_relation( + ::Mongoid::Relations::Referenced::In, relation[:relation] + ) + end + + def embeds_many_valid?(relation) + relation = klass.reflect_on_association(relation) + + return true if relation[:relation] == + ::Mongoid::Relations::Embedded::Many + + invalid_relation( + ::Mongoid::Relations::Embedded::Many, relation[:relation] + ) + end + + def embeds_one_valid?(relation) + relation = klass.reflect_on_association(relation) + + return true if relation[:relation] == + ::Mongoid::Relations::Embedded::One + + invalid_relation( + ::Mongoid::Relations::Embedded::One, relation[:relation] + ) + end + + def has_many_valid?(relation) + relation = klass.reflect_on_association(relation) + + return true if relation[:relation] == + ::Mongoid::Relations::Referenced::Many + + invalid_relation( + ::Mongoid::Relations::Referenced::Many, relation[:relation] + ) + end + + def has_and_belongs_to_many_valid?(relation) + relation = klass.reflect_on_association(relation) + + return true if relation[:relation] == + ::Mongoid::Relations::Referenced::ManyToMany + + invalid_relation( + ::Mongoid::Relations::Referenced::ManyToMany, + relation[:relation] + ) + end + + def has_one_valid?(relation) + relation = klass.reflect_on_association(relation) + + return true if relation[:relation] == + ::Mongoid::Relations::Referenced::One + + invalid_relation( + ::Mongoid::Relations::Referenced::One, relation[:relation] + ) + end + end + end + end + end + end +end diff --git a/lib/grape/roar/extensions/relations/validations/mongoid/7.rb b/lib/grape/roar/extensions/relations/validations/mongoid/7.rb new file mode 100644 index 0000000..c0566e8 --- /dev/null +++ b/lib/grape/roar/extensions/relations/validations/mongoid/7.rb @@ -0,0 +1,75 @@ +# frozen_string_literal: true + +module Grape + module Roar + module Extensions + module Relations + module Validations + module Mongoid + include Validations::Misc + + def belongs_to_valid?(relation) + _valid_relation?( + relation, + ::Mongoid::Association::Referenced::BelongsTo, + ::Mongoid::Association::Referenced::BelongsTo::Proxy + ) + end + + def embeds_many_valid?(relation) + _valid_relation?( + relation, + ::Mongoid::Association::Embedded::EmbedsMany, + ::Mongoid::Association::Embedded::EmbedsMany::Proxy + ) + end + + def embeds_one_valid?(relation) + _valid_relation?( + relation, + ::Mongoid::Association::Embedded::EmbedsOne, + ::Mongoid::Association::Embedded::EmbedsOne::Proxy + ) + end + + def has_many_valid?(relation) + _valid_relation?( + relation, + ::Mongoid::Association::Referenced::HasMany, + ::Mongoid::Association::Referenced::HasMany::Proxy + ) + end + + def has_and_belongs_to_many_valid?(relation) + _valid_relation?( + relation, + ::Mongoid::Association::Referenced::HasAndBelongsToMany, + ::Mongoid::Association::Referenced::HasAndBelongsToMany::Proxy + ) + end + + def has_one_valid?(relation) + _valid_relation?( + relation, + ::Mongoid::Association::Referenced::HasOne, + ::Mongoid::Association::Referenced::HasOne::Proxy + ) + end + + private + + def _valid_relation?(relation, relation_klass, relation_proxy_klass) + relation = klass.reflect_on_association(relation) + + related = relation.is_a?(Hash) ? relation[:relation] : relation.relation + + return true if related == relation_klass || related == relation_proxy_klass + + invalid_relation(relation.class, related) + end + end + end + end + end + end +end diff --git a/spec/decorator_spec.rb b/spec/decorator_spec.rb index 1709de8..6c317c3 100644 --- a/spec/decorator_spec.rb +++ b/spec/decorator_spec.rb @@ -1,7 +1,5 @@ # frozen_string_literal: true - - describe Grape::Roar::Decorator do subject do Class.new(Grape::API) diff --git a/spec/extensions/relations/adapters/active_record_spec.rb b/spec/extensions/relations/adapters/active_record_spec.rb index ad2271b..c454587 100644 --- a/spec/extensions/relations/adapters/active_record_spec.rb +++ b/spec/extensions/relations/adapters/active_record_spec.rb @@ -1,26 +1,30 @@ # frozen_string_literal: true -describe Grape::Roar::Extensions::Relations::Adapters::ActiveRecord, activerecord: true do - let(:model) { Class.new(ActiveRecord::Base) } - subject { described_class.new(model) } - context '.valid_for?' do - it 'is only valid for ActiveRecord::Base descendants' do - expect(described_class.valid_for?(model)).to eql(true) - expect(described_class.valid_for?('foo')).to eql(false) +if defined?(ActiveRecord) + describe Grape::Roar::Extensions::Relations::Adapters::ActiveRecord, :activerecord do + let(:model) { Class.new(ActiveRecord::Base) } + + subject { described_class.new(model) } + + describe '.valid_for?' do + it 'is only valid for ActiveRecord::Base descendants' do + expect(described_class.valid_for?(model)).to eql(true) + expect(described_class.valid_for?('foo')).to eql(false) + end end - end - context '#collection_methods' do - it 'should return all collection methods' do - expect(subject.collection_methods) - .to match_array(%i[has_many has_and_belongs_to_many]) + describe '#collection_methods' do + it 'returns all collection methods' do + expect(subject.collection_methods) + .to match_array(%i[has_many has_and_belongs_to_many]) + end end - end - context '#single_entity_methods' do - it 'should return all single entity methods' do - expect(subject.single_entity_methods) - .to match_array(%i[has_one belongs_to]) + describe '#single_entity_methods' do + it 'returns all single entity methods' do + expect(subject.single_entity_methods) + .to match_array(%i[has_one belongs_to]) + end end end end diff --git a/spec/extensions/relations/adapters/adapters_module_spec.rb b/spec/extensions/relations/adapters/adapters_module_spec.rb index e9ab854..079d951 100644 --- a/spec/extensions/relations/adapters/adapters_module_spec.rb +++ b/spec/extensions/relations/adapters/adapters_module_spec.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true -describe Grape::Roar::Extensions::Relations::Adapters, activerecord: true do - context '.for' do + +describe Grape::Roar::Extensions::Relations::Adapters, :activerecord do + describe '.for' do let(:model) { Class.new(ActiveRecord::Base) } it 'looks up the correct adapter for a given class' do diff --git a/spec/extensions/relations/adapters/mongoid_spec.rb b/spec/extensions/relations/adapters/mongoid_spec.rb index 4d9e085..ae5644f 100644 --- a/spec/extensions/relations/adapters/mongoid_spec.rb +++ b/spec/extensions/relations/adapters/mongoid_spec.rb @@ -1,26 +1,30 @@ # frozen_string_literal: true -describe Grape::Roar::Extensions::Relations::Adapters::Mongoid, mongoid: true do - let(:model) { Class.new.tap { |c| c.include(::Mongoid::Document) } } - subject { described_class.new(model) } - context '.valid_for?' do - it 'is only valid for classes that mixed in Mongoid::Document' do - expect(described_class.valid_for?(model)).to eql(true) - expect(described_class.valid_for?('foo')).to eql(false) +if defined?(Mongoid) + describe Grape::Roar::Extensions::Relations::Adapters::Mongoid, :mongoid do + let(:model) { Class.new.tap { |c| c.include(Mongoid::Document) } } + + subject { described_class.new(model) } + + describe '.valid_for?' do + it 'is only valid for classes that mixed in Mongoid::Document' do + expect(described_class.valid_for?(model)).to eql(true) + expect(described_class.valid_for?('foo')).to eql(false) + end end - end - context '#collection_methods' do - it 'should return all collection methods' do - expect(subject.collection_methods) - .to match_array(%i[embeds_many has_many has_and_belongs_to_many]) + describe '#collection_methods' do + it 'returns all collection methods' do + expect(subject.collection_methods) + .to match_array(%i[embeds_many has_many has_and_belongs_to_many]) + end end - end - context '#single_entity_methods' do - it 'should return all single entity methods' do - expect(subject.single_entity_methods) - .to match_array(%i[has_one belongs_to embeds_one]) + describe '#single_entity_methods' do + it 'returns all single entity methods' do + expect(subject.single_entity_methods) + .to match_array(%i[has_one belongs_to embeds_one]) + end end end end diff --git a/spec/extensions/relations/dsl_methods_spec.rb b/spec/extensions/relations/dsl_methods_spec.rb index e5530a6..1260167 100644 --- a/spec/extensions/relations/dsl_methods_spec.rb +++ b/spec/extensions/relations/dsl_methods_spec.rb @@ -5,7 +5,7 @@ Class.new.tap { |c| c.singleton_class.include(described_class) } end - context '#link_relation' do + describe '#link_relation' do let(:relation) { double } let(:collection) { false } @@ -29,7 +29,7 @@ end end - context '#name_for_represented' do + describe '#name_for_represented' do let(:represented) { double } after { subject.name_for_represented(represented) } @@ -49,7 +49,7 @@ .and_return(relational_mapper) end - context '#relation' do + describe '#relation' do let(:relation_name) { double } let(:relation_kind) { :has_many } let(:opts) { {} } @@ -63,7 +63,7 @@ end end - context '#link_self' do + describe '#link_self' do after { subject.link_self } it 'calls the method correctly' do @@ -74,7 +74,7 @@ end end - context '#map_base_url' do + describe '#map_base_url' do let(:grape_request) do OpenStruct.new(base_url: 'foo/', script_name: 'v1') end @@ -93,14 +93,14 @@ context 'with user provided block' do let(:block) { proc {} } - it 'should return the user block' do + it 'returns the user block' do subject.map_base_url(&block) expect(subject.map_base_url).to eql(block) end end end - context '#map_self_url' do + describe '#map_self_url' do after { subject.map_self_url } it 'calls the correct method' do @@ -108,7 +108,7 @@ end end - context '#map_resource_path' do + describe '#map_resource_path' do let(:object) { OpenStruct.new(id: 4) } let(:opts) { double } let(:relation) { 'baz' } @@ -122,14 +122,14 @@ context 'with user provided block' do let(:block) { proc {} } - it 'should return the user block' do + it 'returns the user block' do subject.map_resource_path(&block) expect(subject.map_resource_path).to eql(block) end end end - context '#represent' do + describe '#represent' do let(:object) { double } let(:options) { double } @@ -142,7 +142,7 @@ after { subject.represent(object, options) } - it 'should map relations and invoke super' do + it 'maps relations and invoke super' do expect(subject).to receive(:map_relations).with(object) expect(subject.superclass).to receive(:represent).with(object, options) end @@ -150,8 +150,8 @@ context 'with relations mapped' do let(:relations_mapped) { true } - it 'should not map relations and invoke super' do - expect(subject).to_not receive(:map_relations).with(object) + it 'does not map relations and invoke super' do + expect(subject).not_to receive(:map_relations).with(object) expect(subject.superclass).to receive(:represent).with(object, options) end end diff --git a/spec/extensions/relations/mapper_spec.rb b/spec/extensions/relations/mapper_spec.rb index 4b85010..ecabbb9 100644 --- a/spec/extensions/relations/mapper_spec.rb +++ b/spec/extensions/relations/mapper_spec.rb @@ -1,12 +1,12 @@ # frozen_string_literal: true describe Grape::Roar::Extensions::Relations::Mapper do + subject { described_class.new(entity) } + let(:entity) { double } let(:klass) { double } - subject { described_class.new(entity) } - - context '#initialize' do + describe '#initialize' do it 'assigns the correct variables' do expect(subject.instance_variable_get(:@entity)).to eql(entity) expect(subject.instance_variable_get(:@config)).to eql({}) @@ -14,7 +14,7 @@ end end - context '#adapter' do + describe '#adapter' do let(:klass) { class_double('ActiveRecord::Base') } before do @@ -23,18 +23,18 @@ after { subject.adapter } - it 'should call the correct method' do + it 'calls the correct method' do expect(Grape::Roar::Extensions::Relations::Adapters) .to receive(:for).with(klass) end end - context '#decorate' do + describe '#decorate' do let(:adapter) { double } let(:config) do { test_single: { - relation_kind: :belongs_to, embedded: true, misc_opt: 'foo' - }, + relation_kind: :belongs_to, embedded: true, misc_opt: 'foo' + }, test_collection: { relation_kind: :has_many, embedded: true, misc_opt: 'baz' } } @@ -52,7 +52,7 @@ subject.instance_variable_set(:@config, config) end - it 'should correctly decorate the entity' do + it 'correctlies decorate the entity' do expect(adapter).to receive(:belongs_to_valid?).with( 'test_single' ).and_return(true) @@ -78,7 +78,7 @@ { test_single: { relation_kind: :has_baz, misc_opt: 'foo' } } end - it 'will raise the correct exception' do + it 'raises the correct exception' do expect { subject.decorate(klass) }.to raise_error( Grape::Roar::Extensions::Relations::Exceptions::UnsupportedRelationError ) diff --git a/spec/extensions/relations/validations/active_record_spec.rb b/spec/extensions/relations/validations/active_record_spec.rb index 89d390a..74d9e3d 100644 --- a/spec/extensions/relations/validations/active_record_spec.rb +++ b/spec/extensions/relations/validations/active_record_spec.rb @@ -1,45 +1,48 @@ # frozen_string_literal: true -describe Grape::Roar::Extensions::Relations::Validations::ActiveRecord, activerecord: true do - let(:model_klass) { double } - subject do - klass = Class.new do - def initialize(klass) - @klass = klass +if defined?(ActiveRecord) + describe Grape::Roar::Extensions::Relations::Validations::ActiveRecord, :activerecord do + let(:model_klass) { double } + + subject do + klass = Class.new do + def initialize(klass) + @klass = klass + end + + attr_reader :klass end - attr_reader :klass + klass.include(described_class) + klass.new(model_klass) end - klass.include(described_class) - klass.new(model_klass) - end - - before do - expect(model_klass).to receive(:reflections).twice - .and_return(reflections) - end + before do + expect(model_klass).to receive(:reflections).twice + .and_return(reflections) + end - %w[ - belongs_to - has_many - has_one - has_and_belongs_to_many - ].each do |relation| - context "##{relation}_valid?" do - let(:relation_klass) do - "::ActiveRecord::Reflection::#{relation.camelize}"\ + %w[ + belongs_to + has_many + has_one + has_and_belongs_to_many + ].each do |relation| + describe "##{relation}_valid?" do + let(:relation_klass) do + "::ActiveRecord::Reflection::#{relation.camelize}" \ 'Reflection'.constantize.allocate - end + end - let(:reflections) { { test: relation_klass, fail: Class.new } } + let(:reflections) { { test: relation_klass, fail: Class.new } } - it 'properly validates the relation' do - expect(subject.send("#{relation}_valid?", :test)).to eql(true) - expect { subject.send("#{relation}_valid?", :fail) }.to raise_error( - Grape::Roar::Extensions::Relations::\ - Exceptions::InvalidRelationError - ) + it 'properly validates the relation' do + expect(subject.send("#{relation}_valid?", :test)).to eql(true) + expect { subject.send("#{relation}_valid?", :fail) }.to raise_error( + Grape::Roar::Extensions::Relations:: + Exceptions::InvalidRelationError + ) + end end end end diff --git a/spec/extensions/relations/validations/mongoid_spec.rb b/spec/extensions/relations/validations/mongoid_spec.rb index ec374c6..5458ad7 100644 --- a/spec/extensions/relations/validations/mongoid_spec.rb +++ b/spec/extensions/relations/validations/mongoid_spec.rb @@ -1,86 +1,89 @@ # frozen_string_literal: true -describe Grape::Roar::Extensions::Relations::Validations::Mongoid, mongoid: true do - let(:model_klass) { double } - subject do - klass = Class.new do - def initialize(klass) - @klass = klass +if defined?(Mongoid) + describe Grape::Roar::Extensions::Relations::Validations::Mongoid, :mongoid do + let(:model_klass) { double } + let(:map_reflection) do + proc do |r| + case r + when /and/ then 'ManyToMany' + when /many/ then 'Many' + when /one/ then 'One' + when /belongs/ then 'In' + end end - - attr_reader :klass end - klass.include(described_class) - klass.new(model_klass) - end + subject do + klass = Class.new do + def initialize(klass) + @klass = klass + end - let(:map_reflection) do - proc do |r| - case r - when /and/ then 'ManyToMany' - when /many/ then 'Many' - when /one/ then 'One' - when /belongs/ then 'In' + attr_reader :klass end + + klass.include(described_class) + klass.new(model_klass) end - end - before do - expect(model_klass).to receive(:reflect_on_association).twice do |r| - if r == :test_rel - { - relation: "Mongoid::Relations::#{relation_klass}"\ - "::#{map_reflection.call(test_method)}".constantize - } - else - { relation: double } + before do + expect(model_klass).to receive(:reflect_on_association).twice do |r| + if r == :test_rel + if Gem::Version.new(Mongoid::VERSION) < Gem::Version.new('7') + { relation: "Mongoid::Relations::#{relation_klass}::#{map_reflection.call(test_method)}".constantize } + else + { relation: "Mongoid::Association::#{relation_klass}::#{test_method.to_s.camelize}".constantize } + end + else + { relation: double } + end end end - end - context 'referenced' do - let(:relation_klass) { 'Referenced' } + context 'referenced' do + let(:relation_klass) { 'Referenced' } - %i[ - belongs_to - has_and_belongs_to_many - has_many - has_one - ].each do |test_method| - context "##{test_method}_valid? validates the relation" do - let(:test_method) { test_method } + %i[ + belongs_to + has_and_belongs_to_many + has_many + has_one + ].each do |test_method| + describe "##{test_method}_valid? validates the relation" do + let(:test_method) { test_method } - it 'properly validates the relation' do - expect(subject.send("#{test_method}_valid?", :test_rel)).to eql(true) + it 'properly validates the relation' do + expect(subject.send("#{test_method}_valid?", :test_rel)).to eql(true) - expect do - subject.send("#{test_method}_valid?", :fail) - end.to raise_error( - Grape::Roar::Extensions::Relations::\ - Exceptions::InvalidRelationError - ) + expect do + subject.send("#{test_method}_valid?", :fail) + end.to raise_error( + Grape::Roar::Extensions::Relations:: + Exceptions::InvalidRelationError + ) + end end end end - end - context 'referenced' do - let(:relation_klass) { 'Embedded' } + context 'embedded' do + let(:relation_klass) { 'Embedded' } - %i[embeds_one embeds_many].each do |test_method| - context "##{test_method}_valid? validates the relation" do - let(:test_method) { test_method } + %i[embeds_one embeds_many].each do |test_method| + describe "##{test_method}_valid? validates the relation" do + let(:test_method) { test_method } - it 'properly validates the relation' do - expect(subject.send("#{test_method}_valid?", :test_rel)).to eql(true) + it 'properly validates the relation' do + expect(subject.send("#{test_method}_valid?", :test_rel)).to eql(true) - expect do - subject.send("#{test_method}_valid?", :fail) - end.to raise_error( - Grape::Roar::Extensions::Relations::\ - Exceptions::InvalidRelationError - ) + expect do + subject.send("#{test_method}_valid?", :fail) + end.to raise_error( + Grape::Roar::Extensions::Relations:: + Exceptions::InvalidRelationError + ) + end end end end diff --git a/spec/nested_representer_spec.rb b/spec/nested_representer_spec.rb index b2be90b..e9fd12d 100644 --- a/spec/nested_representer_spec.rb +++ b/spec/nested_representer_spec.rb @@ -1,7 +1,5 @@ # frozen_string_literal: true - - describe Grape::Roar do context 'nested representer' do include_context 'Grape API App' diff --git a/spec/present_with_spec.rb b/spec/present_with_spec.rb index 733029b..482045c 100644 --- a/spec/present_with_spec.rb +++ b/spec/present_with_spec.rb @@ -1,7 +1,5 @@ # frozen_string_literal: true - - describe Grape::Roar do include_context 'Grape API App' diff --git a/spec/relations_spec.rb b/spec/relations_spec.rb index c0aa309..df7922e 100644 --- a/spec/relations_spec.rb +++ b/spec/relations_spec.rb @@ -1,8 +1,9 @@ # frozen_string_literal: true describe Grape::Roar::Extensions::Relations do - context '.included' do + describe '.included' do subject { Class.new } + after { subject.include(described_class) } it 'mixes DSLMethods into the singleton class of its target' do @@ -12,11 +13,11 @@ end end - context 'with mongoid', mongoid: true do + context 'with mongoid', :mongoid do include_context 'Grape API App' # Make sure Mongo is empty - before(:each) { Mongoid::Config.purge! } + before { Mongoid::Config.purge! } let(:result) { JSON.parse(last_response.body) } @@ -39,7 +40,7 @@ it 'correctly generates links for items' do expect(result['_links']['items'].map { |l| l['href'] }).to all( - match(/^http:\/\/example\.org\/items\/[A-Za-z0-9]*$/) + match(%r{^http://example\.org/items/[A-Za-z0-9]*$}) ) end end @@ -64,7 +65,7 @@ it 'correctly represents the embedded cart' do expect(result['_embedded']['cart']['_links']['self']['href']) - .to match(/^http:\/\/example\.org\/carts\/[A-Za-z0-9]*$/) + .to match(%r{^http://example\.org/carts/[A-Za-z0-9]*$}) expect(result['_embedded']['cart']['_links']['items'].count).to eql(1) expect(result['_embedded']['cart']['_links']['items'].first).to eql( diff --git a/spec/representer_spec.rb b/spec/representer_spec.rb index c27adc2..d4ebf26 100644 --- a/spec/representer_spec.rb +++ b/spec/representer_spec.rb @@ -17,9 +17,9 @@ end end - context 'without an instance singleton' do - context 'as nil' do - before do + context 'without an instance singleton' do + context 'as nil' do + before do subject.get('/article/:id') do present(nil, with: ArticleRepresenter) end @@ -30,8 +30,8 @@ end end - context 'as another immediate value' do - before do + context 'as another immediate value' do + before do subject.get('/article/:id') do present(5, with: ArticleRepresenter) end @@ -40,7 +40,7 @@ it 'raises TypeError' do expect { get '/article/666' }.to raise_error(TypeError) end - end + end end end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 787a387..e0858f1 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -6,6 +6,7 @@ require 'ostruct' require 'bigdecimal' require 'base64' +require 'logger' require 'bundler' Bundler.setup :default, :test diff --git a/spec/support/all/grape_app_context.rb b/spec/support/all/grape_app_context.rb index 0c79bc3..693096b 100644 --- a/spec/support/all/grape_app_context.rb +++ b/spec/support/all/grape_app_context.rb @@ -1,7 +1,5 @@ # frozen_string_literal: true - - shared_context 'Grape API App' do subject do Class.new(Grape::API) From 54a6c8ac5b7b6f9033113a5b0fb4a5257270b608 Mon Sep 17 00:00:00 2001 From: "Daniel (dB.) Doubrovkine" Date: Wed, 24 Sep 2025 22:19:41 -0400 Subject: [PATCH 3/3] Refactor valid relation checks. --- .github/workflows/test-mongodb.yml | 2 + .rubocop_todo.yml | 2 +- CHANGELOG.md | 2 +- .../relations/validations/active_record.rb | 49 ++++----------- .../relations/validations/mongoid/6.rb | 60 ++++--------------- 5 files changed, 28 insertions(+), 87 deletions(-) diff --git a/.github/workflows/test-mongodb.yml b/.github/workflows/test-mongodb.yml index acc3743..8ae8493 100644 --- a/.github/workflows/test-mongodb.yml +++ b/.github/workflows/test-mongodb.yml @@ -10,6 +10,8 @@ jobs: - { ruby: '3.4', mongoid: '~> 6.4.8', mongodb: '6.0', grape: '~> 1.7.0' } - { ruby: '3.4', mongoid: '~> 6.4.8', mongodb: '6.0', grape: '~> 2.0.0' } - { ruby: '3.4', mongoid: '~> 7.5.4', mongodb: '7.0', grape: '~> 2.4.0' } + - { ruby: '3.4', mongoid: '~> 8.1.11', mongodb: '8.0', grape: '~> 2.4.0' } + - { ruby: '3.4', mongoid: '~> 9.0.8', mongodb: '8.0', grape: '~> 2.4.0' } name: test (ruby=${{ matrix.entry.ruby }}, mongoid=${{ matrix.entry.mongoid }}, mongodb=${{ matrix.entry.mongodb }}, grape=${{ matrix.entry.grape }}) env: MONGOID_VERSION: ${{ matrix.entry.mongoid }} diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index c4238fb..a137357 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1,6 +1,6 @@ # This configuration was generated by # `rubocop --auto-gen-config` -# on 2025-09-25 02:12:55 UTC using RuboCop version 1.80.2. +# on 2025-09-25 02:19:21 UTC using RuboCop version 1.80.2. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c64c47..31a27e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ ### 0.5.0 (Next) -* [#31](https://github.com/ruby-grape/grape-roar/pull/31): Add support for Mongoid 7 - [@dblock](https://github.com/dblock). +* [#31](https://github.com/ruby-grape/grape-roar/pull/31): Add support for Mongoid 7, 8 and 9 - [@dblock](https://github.com/dblock). * [#23](https://github.com/ruby-grape/grape-roar/pull/23): Resolves pollution issue with invoking representers on singletons - [@mach-kernel](https://github.com/mach-kernel). * [#28](https://github.com/ruby-grape/grape-roar/pull/28): Replaced Travis-CI with GHA - [@dblock](https://github.com/dblock). * Your contribution here. diff --git a/lib/grape/roar/extensions/relations/validations/active_record.rb b/lib/grape/roar/extensions/relations/validations/active_record.rb index ad2630f..bde93da 100644 --- a/lib/grape/roar/extensions/relations/validations/active_record.rb +++ b/lib/grape/roar/extensions/relations/validations/active_record.rb @@ -9,55 +9,28 @@ module ActiveRecord include Validations::Misc def belongs_to_valid?(relation) - relation = klass.reflections[relation] - - return true if relation.is_a?( - ::ActiveRecord::Reflection::BelongsToReflection - ) - - invalid_relation( - ::ActiveRecord::Reflection::BelongsToReflection, - relation.class - ) + _valid_relation? relation, ::ActiveRecord::Reflection::BelongsToReflection end def has_many_valid?(relation) - relation = klass.reflections[relation] - - return true if relation.is_a?( - ::ActiveRecord::Reflection::HasManyReflection - ) - - invalid_relation( - ::ActiveRecord::Reflection::HasManyReflection, - relation.class - ) + _valid_relation? relation, ::ActiveRecord::Reflection::HasManyReflection end def has_and_belongs_to_many_valid?(relation) - relation = klass.reflections[relation] - - return true if relation.is_a?( - ::ActiveRecord::Reflection::HasAndBelongsToManyReflection - ) - - invalid_relation( - ::ActiveRecord::Reflection::HasAndBelongsToManyReflection, - relation.class - ) + _valid_relation? relation, ::ActiveRecord::Reflection::HasAndBelongsToManyReflection end def has_one_valid?(relation) - relation = klass.reflections[relation] + _valid_relation? relation, ::ActiveRecord::Reflection::HasOneReflection + end + + private - return true if relation.is_a?( - ::ActiveRecord::Reflection::HasOneReflection - ) + def _valid_relation?(relation, relation_klass) + relation = klass.reflections[relation] + return true if relation.is_a?(relation_klass) - invalid_relation( - ::ActiveRecord::Reflection::HasOneReflection, - relation.class - ) + invalid_relation(relation_klass, relation.class) end end end diff --git a/lib/grape/roar/extensions/relations/validations/mongoid/6.rb b/lib/grape/roar/extensions/relations/validations/mongoid/6.rb index fa71bf4..a21dadc 100644 --- a/lib/grape/roar/extensions/relations/validations/mongoid/6.rb +++ b/lib/grape/roar/extensions/relations/validations/mongoid/6.rb @@ -9,70 +9,36 @@ module Mongoid include Validations::Misc def belongs_to_valid?(relation) - relation = klass.reflect_on_association(relation) - - return true if relation[:relation] == - ::Mongoid::Relations::Referenced::In - - invalid_relation( - ::Mongoid::Relations::Referenced::In, relation[:relation] - ) + _valid_relation? relation, ::Mongoid::Relations::Referenced::In end def embeds_many_valid?(relation) - relation = klass.reflect_on_association(relation) - - return true if relation[:relation] == - ::Mongoid::Relations::Embedded::Many - - invalid_relation( - ::Mongoid::Relations::Embedded::Many, relation[:relation] - ) + _valid_relation? relation, ::Mongoid::Relations::Embedded::Many end def embeds_one_valid?(relation) - relation = klass.reflect_on_association(relation) - - return true if relation[:relation] == - ::Mongoid::Relations::Embedded::One - - invalid_relation( - ::Mongoid::Relations::Embedded::One, relation[:relation] - ) + _valid_relation? relation, ::Mongoid::Relations::Embedded::One end def has_many_valid?(relation) - relation = klass.reflect_on_association(relation) - - return true if relation[:relation] == - ::Mongoid::Relations::Referenced::Many - - invalid_relation( - ::Mongoid::Relations::Referenced::Many, relation[:relation] - ) + _valid_relation? relation, ::Mongoid::Relations::Referenced::Many end def has_and_belongs_to_many_valid?(relation) - relation = klass.reflect_on_association(relation) - - return true if relation[:relation] == - ::Mongoid::Relations::Referenced::ManyToMany - - invalid_relation( - ::Mongoid::Relations::Referenced::ManyToMany, - relation[:relation] - ) + _valid_relation? relation, ::Mongoid::Relations::Referenced::ManyToMany end def has_one_valid?(relation) - relation = klass.reflect_on_association(relation) + _valid_relation? relation, ::Mongoid::Relations::Referenced::One + end - return true if relation[:relation] == - ::Mongoid::Relations::Referenced::One + private + + def _valid_relation?(relation, relation_klass) + relation = klass.reflect_on_association(relation) + return true if relation[:relation] == relation_klass - invalid_relation( - ::Mongoid::Relations::Referenced::One, relation[:relation] - ) + invalid_relation(relation_klass, relation[:relation]) end end end