dirty_seed 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +44 -0
  4. data/Rakefile +22 -0
  5. data/lib/dirty_seed.rb +23 -0
  6. data/lib/dirty_seed/assigners/dirty_assigner.rb +32 -0
  7. data/lib/dirty_seed/assigners/dirty_boolean.rb +14 -0
  8. data/lib/dirty_seed/assigners/dirty_date.rb +14 -0
  9. data/lib/dirty_seed/assigners/dirty_float.rb +28 -0
  10. data/lib/dirty_seed/assigners/dirty_integer.rb +86 -0
  11. data/lib/dirty_seed/assigners/dirty_string.rb +49 -0
  12. data/lib/dirty_seed/assigners/dirty_time.rb +14 -0
  13. data/lib/dirty_seed/data_model.rb +80 -0
  14. data/lib/dirty_seed/dirty_association.rb +106 -0
  15. data/lib/dirty_seed/dirty_attribute.rb +64 -0
  16. data/lib/dirty_seed/dirty_model.rb +123 -0
  17. data/lib/dirty_seed/engine.rb +11 -0
  18. data/lib/dirty_seed/exceptions.rb +6 -0
  19. data/lib/dirty_seed/method_missing_helper.rb +49 -0
  20. data/lib/dirty_seed/sorter.rb +118 -0
  21. data/lib/dirty_seed/version.rb +6 -0
  22. data/lib/tasks/dirty_seed_tasks.rake +8 -0
  23. data/spec/dummy/Rakefile +6 -0
  24. data/spec/dummy/app/assets/config/manifest.js +3 -0
  25. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  26. data/spec/dummy/app/channels/application_cable/channel.rb +4 -0
  27. data/spec/dummy/app/channels/application_cable/connection.rb +4 -0
  28. data/spec/dummy/app/controllers/application_controller.rb +2 -0
  29. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  30. data/spec/dummy/app/javascript/packs/application.js +15 -0
  31. data/spec/dummy/app/jobs/application_job.rb +7 -0
  32. data/spec/dummy/app/mailers/application_mailer.rb +4 -0
  33. data/spec/dummy/app/models/alfa.rb +5 -0
  34. data/spec/dummy/app/models/application_record.rb +3 -0
  35. data/spec/dummy/app/models/bravo.rb +3 -0
  36. data/spec/dummy/app/models/charlie.rb +5 -0
  37. data/spec/dummy/app/models/delta.rb +4 -0
  38. data/spec/dummy/app/models/echo.rb +3 -0
  39. data/spec/dummy/app/models/foxtrot.rb +2 -0
  40. data/spec/dummy/app/models/golf.rb +2 -0
  41. data/spec/dummy/app/models/hotel.rb +4 -0
  42. data/spec/dummy/app/models/india.rb +4 -0
  43. data/spec/dummy/app/models/juliett.rb +21 -0
  44. data/spec/dummy/app/models/kilo.rb +2 -0
  45. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  46. data/spec/dummy/app/views/layouts/mailer.html.erb +13 -0
  47. data/spec/dummy/app/views/layouts/mailer.text.erb +1 -0
  48. data/spec/dummy/bin/rails +4 -0
  49. data/spec/dummy/bin/rake +4 -0
  50. data/spec/dummy/bin/setup +33 -0
  51. data/spec/dummy/config.ru +5 -0
  52. data/spec/dummy/config/application.rb +18 -0
  53. data/spec/dummy/config/boot.rb +5 -0
  54. data/spec/dummy/config/cable.yml +10 -0
  55. data/spec/dummy/config/database.yml +25 -0
  56. data/spec/dummy/config/environment.rb +5 -0
  57. data/spec/dummy/config/environments/development.rb +62 -0
  58. data/spec/dummy/config/environments/production.rb +112 -0
  59. data/spec/dummy/config/environments/test.rb +49 -0
  60. data/spec/dummy/config/initializers/application_controller_renderer.rb +8 -0
  61. data/spec/dummy/config/initializers/assets.rb +12 -0
  62. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  63. data/spec/dummy/config/initializers/content_security_policy.rb +28 -0
  64. data/spec/dummy/config/initializers/cookies_serializer.rb +5 -0
  65. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  66. data/spec/dummy/config/initializers/inflections.rb +27 -0
  67. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  68. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  69. data/spec/dummy/config/locales/en.yml +33 -0
  70. data/spec/dummy/config/puma.rb +38 -0
  71. data/spec/dummy/config/routes.rb +3 -0
  72. data/spec/dummy/config/spring.rb +6 -0
  73. data/spec/dummy/config/storage.yml +34 -0
  74. data/spec/dummy/db/development.sqlite3 +0 -0
  75. data/spec/dummy/db/migrate/20200923100317_create_alfas.rb +15 -0
  76. data/spec/dummy/db/migrate/20200923100328_create_bravos.rb +12 -0
  77. data/spec/dummy/db/migrate/20200923100329_create_charlies.rb +9 -0
  78. data/spec/dummy/db/migrate/20200923100330_create_deltas.rb +10 -0
  79. data/spec/dummy/db/migrate/20200923100331_create_echos.rb +9 -0
  80. data/spec/dummy/db/migrate/20200923100332_create_foxtrots.rb +9 -0
  81. data/spec/dummy/db/migrate/20200923100334_create_hotels.rb +7 -0
  82. data/spec/dummy/db/migrate/20200923100335_create_indias.rb +11 -0
  83. data/spec/dummy/db/migrate/20200923100336_create_julietts.rb +11 -0
  84. data/spec/dummy/db/migrate/20200923100337_create_kilos.rb +13 -0
  85. data/spec/dummy/db/schema.rb +105 -0
  86. data/spec/dummy/db/test.sqlite3 +0 -0
  87. data/spec/dummy/log/development.log +217 -0
  88. data/spec/dummy/log/test.log +95379 -0
  89. data/spec/dummy/public/404.html +67 -0
  90. data/spec/dummy/public/422.html +67 -0
  91. data/spec/dummy/public/500.html +66 -0
  92. data/spec/dummy/public/apple-touch-icon-precomposed.png +0 -0
  93. data/spec/dummy/public/apple-touch-icon.png +0 -0
  94. data/spec/dummy/public/favicon.ico +0 -0
  95. data/spec/dummy/spec/factories/bars.rb +8 -0
  96. data/spec/dummy/spec/factories/foos.rb +7 -0
  97. data/spec/dummy/spec/models/alfa_spec.rb +6 -0
  98. data/spec/dummy/spec/models/bravo_spec.rb +6 -0
  99. data/spec/dummy/spec/models/charlie_spec.rb +6 -0
  100. data/spec/dummy/spec/models/delta_spec.rb +6 -0
  101. data/spec/dummy/spec/models/echo_spec.rb +6 -0
  102. data/spec/dummy/tmp/development_secret.txt +1 -0
  103. data/spec/lib/dirty_seed/assigners/dirty_assigner_spec.rb +13 -0
  104. data/spec/lib/dirty_seed/assigners/dirty_boolean_spec.rb +13 -0
  105. data/spec/lib/dirty_seed/assigners/dirty_date_spec.rb +15 -0
  106. data/spec/lib/dirty_seed/assigners/dirty_float_spec.rb +47 -0
  107. data/spec/lib/dirty_seed/assigners/dirty_integer_spec.rb +49 -0
  108. data/spec/lib/dirty_seed/assigners/dirty_string_spec.rb +15 -0
  109. data/spec/lib/dirty_seed/assigners/dirty_time_spec.rb +15 -0
  110. data/spec/lib/dirty_seed/data_model_spec.rb +72 -0
  111. data/spec/lib/dirty_seed/dirty_association_spec.rb +64 -0
  112. data/spec/lib/dirty_seed/dirty_attribute_spec.rb +49 -0
  113. data/spec/lib/dirty_seed/dirty_model_spec.rb +102 -0
  114. data/spec/lib/dirty_seed/sorter_spec.rb +46 -0
  115. data/spec/lib/tasks/dirty_seed_tasks_spec.rb +21 -0
  116. data/spec/rails_helper.rb +44 -0
  117. data/spec/support/helpers.rb +30 -0
  118. metadata +282 -0
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DirtySeed
4
+ # Represents an Active Record association
5
+ class DirtyAssociation
6
+ extend ::DirtySeed::MethodMissingHelper
7
+ forward_missing_methods_to :reflections
8
+
9
+ attr_reader :dirty_model, :reflection
10
+
11
+ # Initializes an instance
12
+ # @param dirty_model [DirtySeed::DirtyModel]
13
+ # @param reflection [ActiveRecord::Reflection::BelongsToReflection]
14
+ # @return [DirtySeed::DirtyAssociation]
15
+ def initialize(dirty_model, reflection)
16
+ @dirty_model = dirty_model
17
+ @reflection = reflection
18
+ end
19
+
20
+ # Assigns a random value to the association
21
+ # @param instance [Object] an instance of a class inheriting from ApplicationRecord
22
+ # @return [void]
23
+ def assign_value(instance)
24
+ return if associated_models.empty?
25
+
26
+ instance.public_send(:"#{name}=", value)
27
+ rescue ArgumentError => e
28
+ @errors << e
29
+ end
30
+
31
+ # Returns a random instance matching the reflection
32
+ # @return [Object] an instance of a class inheriting from ApplicationRecord
33
+ def value
34
+ random_model = associated_models.sample
35
+ random_id = random_model.pluck(:id).sample
36
+ random_model.find_by(id: random_id)
37
+ end
38
+
39
+ # Returns the reflection name
40
+ # @return [String]
41
+ def name
42
+ reflection.name
43
+ end
44
+
45
+ # Returns the attribute containing the foreign key
46
+ # @return [Symbol]
47
+ def attribute
48
+ :"#{name}_id"
49
+ end
50
+
51
+ # Returns the attribute containing the foreign type (for polymorphic associations)
52
+ # @example
53
+ # Given Bar.belongs_to(:barable, polymorphic: true)
54
+ # And self.model == Bar
55
+ # Then it returns barable_type
56
+ # @return [Symbol]
57
+ def type_key
58
+ reflection.foreign_type&.to_sym
59
+ end
60
+
61
+ # Returns or defines associated_models
62
+ # @return [Array<Class>] a class inheriting from ApplicationRecord
63
+ def associated_models
64
+ polymorphic? ? polymorphic_associations : regular_associations
65
+ end
66
+
67
+ # Returns true if the reflection is polymorphic
68
+ # @example
69
+ # Given Bar.belongs_to(:barable, polymorphic: true)
70
+ # And self.model == Bar
71
+ # Then it returns true
72
+ # @return [Boolean]
73
+ def polymorphic?
74
+ reflection.options[:polymorphic]
75
+ end
76
+
77
+ private
78
+
79
+ # Returns the reflected models for a regular association
80
+ # @example
81
+ # Given Bar.belongs_to(:foo)
82
+ # And Foo.has_many(:bars)
83
+ # And self.model == Bar
84
+ # Then it returns [Foo]
85
+ # @return [Array<Class>] a class inheriting from ApplicationRecord
86
+ def regular_associations
87
+ [reflection.klass]
88
+ end
89
+
90
+ # Returns the reflected models for a polymorphic association
91
+ # @example
92
+ # Given Bar.belongs_to(:barable, polymorphic: true)
93
+ # And Foo.has_many(:bars, as: :barable)
94
+ # And Zed.has_many(:bars, as: :barable)
95
+ # And #model is Bar
96
+ # Then it returns [Foo, Zed]
97
+ # @return [Array<Class>] a class inheriting from ApplicationRecord
98
+ def polymorphic_associations
99
+ DirtySeed::DataModel.active_record_models.select do |active_record_model|
100
+ active_record_model.reflections.values.any? do |arm_reflection|
101
+ arm_reflection.options[:as]&.to_sym == name
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DirtySeed
4
+ # Represents an Active Record attribute
5
+ class DirtyAttribute
6
+ extend ::DirtySeed::MethodMissingHelper
7
+ forward_missing_methods_to :column
8
+
9
+ attr_reader :dirty_model, :column
10
+
11
+ # Initializes an instance
12
+ # @param dirty_model [DirtySeed::DirtyModel]
13
+ # @param column [ActiveRecord::ConnectionAdapters::Column]
14
+ # @return [DirtySeed::DirtyAttribute]
15
+ def initialize(dirty_model, column)
16
+ @dirty_model = dirty_model
17
+ @column = column
18
+ end
19
+
20
+ # Assigns a value to the attribute
21
+ # @param instance [Object] an instance of a class inheriting from ApplicationRecord
22
+ # @param sequence [Integer]
23
+ # @return [void]
24
+ def assign_value(instance, sequence)
25
+ instance.assign_attributes(name => value(sequence))
26
+ rescue ArgumentError => e
27
+ dirty_model.errors << e
28
+ end
29
+
30
+ # Returns a value matching type and validators
31
+ # @param sequence [Integer]
32
+ # @return [Object, nil]
33
+ def value(sequence)
34
+ assigner = "DirtySeed::Assigners::Dirty#{type.capitalize}".constantize
35
+ assigner.new(self, sequence).value
36
+ # If attribute type is not currently handled (json, array...) return nil
37
+ rescue NameError
38
+ nil
39
+ end
40
+
41
+ # Returns attribute name
42
+ # @return [Symbol]
43
+ def name
44
+ column.name.to_sym
45
+ end
46
+
47
+ # Returns attribute type
48
+ # @return [Symbol]
49
+ def type
50
+ return :float if column.sql_type_metadata.type == :decimal
51
+ return :time if column.sql_type_metadata.type == :datetime
52
+
53
+ column.sql_type_metadata.type
54
+ end
55
+
56
+ # Returns an validators related to the current attribute
57
+ # @return [Array<ActiveModel::Validations::EachValidators>]
58
+ def validators
59
+ dirty_model.validators.select do |validator|
60
+ validator.attributes.include? name
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,123 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DirtySeed
4
+ # Represents an Active Record model
5
+ class DirtyModel
6
+ extend ::DirtySeed::MethodMissingHelper
7
+ forward_missing_methods_to :model
8
+
9
+ attr_reader :model, :seeded
10
+ attr_writer :errors
11
+
12
+ PROTECTED_COLUMNS = %w[
13
+ id
14
+ type
15
+ created_at
16
+ updated_at
17
+ encrypted_password
18
+ reset_password_token
19
+ reset_password_sent_at
20
+ remember_created_at
21
+ ].freeze
22
+ private_constant :PROTECTED_COLUMNS
23
+
24
+ # Initializes an instance
25
+ # @param model [Class] a class inheriting from ApplicationRecord
26
+ # @return [DirtySeed::DirtyModel]
27
+ def initialize(model)
28
+ @model = model
29
+ end
30
+
31
+ # Returns models where models are associated to the current model through a has_many or has_one associations
32
+ # @return [Array<Class>] ActiveRecord models
33
+ def associated_models
34
+ associations.map(&:associated_models).flatten
35
+ end
36
+
37
+ # Returns an dirty associations representing the self.model belongs_to associations
38
+ # @return [Array<DirtySeed::DirtyAssociation>]
39
+ def associations
40
+ included_reflections.map do |reflection|
41
+ DirtySeed::DirtyAssociation.new(self, reflection)
42
+ end
43
+ end
44
+
45
+ # Returns model attributes
46
+ # @return [Array<String>]
47
+ def attributes
48
+ included_columns.map do |column|
49
+ DirtySeed::DirtyAttribute.new(self, column)
50
+ end
51
+ end
52
+
53
+ # Returns uniq errors
54
+ # @return [Array<Error>]
55
+ def errors
56
+ @errors.flatten.uniq
57
+ end
58
+
59
+ # Creates instances for each model
60
+ # @param count [Integer]
61
+ # @return [void]
62
+ def seed(count = 5)
63
+ reset_info
64
+ count.times do |sequence|
65
+ create_instance(sequence)
66
+ end
67
+ end
68
+
69
+ private
70
+
71
+ # Reset seed info
72
+ # @return [void]
73
+ def reset_info
74
+ @seeded = 0
75
+ @errors = []
76
+ end
77
+
78
+ # Creates an instance
79
+ # @param sequence [Integer]
80
+ # @return [void]
81
+ def create_instance(sequence)
82
+ instance = model.new
83
+ associations.each { |association| association.assign_value(instance) }
84
+ attributes.each { |attribute| attribute.assign_value(instance, sequence) }
85
+ if instance.save
86
+ @seeded += 1
87
+ else
88
+ @errors << instance.errors.full_messages
89
+ end
90
+ rescue ActiveRecord::ActiveRecordError => e
91
+ errors << e
92
+ end
93
+
94
+ # Returns columns that should be treated as regular attributes
95
+ # @return [Array<ActiveRecord::ConnectionAdapters::Column>]
96
+ def included_columns
97
+ excluded = PROTECTED_COLUMNS + reflection_related_attributes
98
+ model.columns.reject do |column|
99
+ column.name.in? excluded
100
+ end
101
+ end
102
+
103
+ # Returns attributes related to an association
104
+ # @example
105
+ # ["foo_id", "doable_id", "doable_type"]
106
+ # @return [Array<String>]
107
+ def reflection_related_attributes
108
+ all_reflection_related_attributes =
109
+ associations.map do |association|
110
+ [association.attribute, association.type_key].compact
111
+ end
112
+ all_reflection_related_attributes.flatten.map(&:to_s)
113
+ end
114
+
115
+ # Returns reflections of the model
116
+ # @return [Array<ActiveRecord::Reflection::BelongsToReflection>]
117
+ def included_reflections
118
+ model.reflections.values.select do |reflection|
119
+ reflection.is_a? ActiveRecord::Reflection::BelongsToReflection
120
+ end
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DirtySeed
4
+ class Engine < ::Rails::Engine
5
+ isolate_namespace DirtySeed
6
+
7
+ config.generators do |generators|
8
+ generators.test_framework :rspec
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DirtySeed
4
+ class Error < StandardError; end
5
+ class CyclicalAssociationsError < StandardError; end
6
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DirtySeed
4
+ # Forwards missing method to another object if it matches
5
+ module MethodMissingHelper
6
+ # Defines missing_method and respond_to_missing? methods
7
+ # @param addressee_key [Object]
8
+ # @return [Object]
9
+ # @raise [NoMethodError]
10
+ def forward_missing_methods_to(addressee_key)
11
+ define_addressee(addressee_key)
12
+ define_method_missing
13
+ define_respond_to_missing?
14
+ end
15
+
16
+ # Defines addressee method
17
+ # @param addressee_key [Object]
18
+ # @return [void]
19
+ def define_addressee(addressee_key)
20
+ define_method :addressee do
21
+ public_send(addressee_key)
22
+ end
23
+ end
24
+
25
+ # Defines missing_method method so it returns the adressee or calls super
26
+ # @example
27
+ # calling #name on a DirtyModel instance will call name on its @model object
28
+ # @return [void]
29
+ def define_method_missing
30
+ define_method :method_missing do |method_name, *args, &block|
31
+ return super(method_name, *args, &block) unless addressee.respond_to?(method_name)
32
+
33
+ addressee.public_send(method_name, *args, &block)
34
+ end
35
+ end
36
+
37
+ # Defines respond_to_missing? method to matches the method_missing behavior
38
+ # @return [void]
39
+ def define_respond_to_missing?
40
+ define_method :respond_to_missing? do |method_name, _include_private = false|
41
+ # :nocov:
42
+ return super(method_name, _include_private = false) unless addressee.respond_to?(method_name)
43
+
44
+ true
45
+ # :nocov:
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,118 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DirtySeed
4
+ # Sorts ActiveRecord models depending on their associations
5
+ class Sorter
6
+ attr_reader :models, :sorted, :checked, :current, :skip_optional
7
+ alias unsorted models
8
+
9
+ # Initializes an instance
10
+ # @param models [Array<Class>] models inheriting from ActiveRecord::Base
11
+ # @return [DirtySeed::Sorter]
12
+ def initialize(models = [])
13
+ @models = models
14
+ @sorted = []
15
+ @checked = []
16
+ @current = unsorted.first
17
+ end
18
+
19
+ # Sorts models depending on their associations
20
+ # @return [Array<Class>] classes inheriting from ActiveRecord::Base
21
+ def sort!
22
+ return sorted if unsorted.empty?
23
+
24
+ set_current
25
+ # active skip_optional option to prevent infinite loop
26
+ skip_optional! if break_loop?
27
+ insert_or_rotate
28
+ sort!
29
+ end
30
+
31
+ private
32
+
33
+ # Defines the current model to be sorted and add it to the checked ones
34
+ # @return [Class] class inheriting from ActiveRecord::Base
35
+ def set_current
36
+ @current = unsorted.first
37
+ checked << current
38
+ end
39
+
40
+ # Returns true if the current model has already been checked and skip optional option is not already activated
41
+ # @return [Boolean]
42
+ def break_loop?
43
+ current.in?(checked) && !skip_optional
44
+ end
45
+
46
+ # Activates skip_optional option to prevent infinite loop
47
+ # @return [void]
48
+ def skip_optional!
49
+ # if skip_optional is already true
50
+ # there is an infinite loop of belongs_to associations
51
+ raise DirtySeed::CyclicalAssociationsError if skip_optional
52
+
53
+ @skip_optional = true
54
+ @checked = []
55
+ end
56
+
57
+ # Chooses if current should be added to sorted ones or not
58
+ # @return [void]
59
+ def insert_or_rotate
60
+ # if the current is dependent form a non-sorted model
61
+ if dependent?
62
+ # rotate models array so current will be sorted at the end
63
+ unsorted.rotate!
64
+ else
65
+ # removed current from unsorted and add it to sorted
66
+ sorted << unsorted.shift
67
+ end
68
+ end
69
+
70
+ # Returns true if @current belongs_to a model that has not been sorted yet
71
+ # @return [Boolean]
72
+ def dependent?
73
+ return false if unsorted.one?
74
+
75
+ current.reflections.values.any? do |reflection|
76
+ belongs_to?(reflection) &&
77
+ unsorted.any? do |model|
78
+ mirror?(model, reflection)
79
+ end
80
+ end
81
+ end
82
+
83
+ # Returns true if relfection is a :belongs_to kind
84
+ # @param reflection [ActiveRecord::Reflection::AssociationReflection]
85
+ # @return [Boolean]
86
+ def belongs_to?(reflection)
87
+ return false if reflection.options[:optional] && skip_optional
88
+
89
+ reflection.is_a?(ActiveRecord::Reflection::BelongsToReflection)
90
+ end
91
+
92
+ # Returns true if model is or can be the <reflection> mirror
93
+ # @param model [ActiveRecord model]
94
+ # @param reflection [ActiveRecord::Reflection::AssociationReflection]
95
+ # @example
96
+ # Given `Foo.belongs_to(:bar)`
97
+ # And `Bar.has_many(:foos)`
98
+ # And <reflection> is the belongs_to reflection
99
+ # Then mirror?(Bar, reflection) returns true
100
+ # @example
101
+ # Given `Foo.belongs_to(:foable, polymorphic: true)`
102
+ # And `Bar.has_many(:foos, as: :foable)`
103
+ # And `Baz.has_many(:foos, as: :foable)`
104
+ # And <reflection> is the Foo "belongs_to" reflection
105
+ # Then mirror?(Bar, reflection) returns true
106
+ # And mirror?(Baz, reflection) returns true
107
+ # @return [Boolean]
108
+ def mirror?(model, reflection)
109
+ if reflection.options[:polymorphic]
110
+ model.reflections.values.any? do |model_reflection|
111
+ model_reflection.options[:as] == reflection.name
112
+ end
113
+ else
114
+ model == reflection.klass
115
+ end
116
+ end
117
+ end
118
+ end