vorpal 1.0.2 → 1.3.0

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 (62) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +73 -26
  3. data/lib/vorpal/aggregate_mapper.rb +13 -2
  4. data/lib/vorpal/aggregate_traversal.rb +9 -8
  5. data/lib/vorpal/config/association_config.rb +84 -0
  6. data/lib/vorpal/config/belongs_to_config.rb +35 -0
  7. data/lib/vorpal/config/class_config.rb +71 -0
  8. data/lib/vorpal/config/configs.rb +54 -0
  9. data/lib/vorpal/config/foreign_key_info.rb +23 -0
  10. data/lib/vorpal/config/has_many_config.rb +38 -0
  11. data/lib/vorpal/config/has_one_config.rb +35 -0
  12. data/lib/vorpal/config/main_config.rb +68 -0
  13. data/lib/vorpal/db_loader.rb +25 -22
  14. data/lib/vorpal/driver/postgresql.rb +42 -6
  15. data/lib/vorpal/dsl/config_builder.rb +26 -73
  16. data/lib/vorpal/dsl/configuration.rb +139 -42
  17. data/lib/vorpal/dsl/defaults_generator.rb +1 -1
  18. data/lib/vorpal/engine.rb +27 -13
  19. data/lib/vorpal/exceptions.rb +4 -0
  20. data/lib/vorpal/identity_map.rb +7 -2
  21. data/lib/vorpal/loaded_objects.rb +57 -14
  22. data/lib/vorpal/util/array_hash.rb +22 -8
  23. data/lib/vorpal/util/hash_initialization.rb +1 -1
  24. data/lib/vorpal/version.rb +1 -1
  25. data/vorpal.gemspec +4 -5
  26. metadata +18 -78
  27. data/.editorconfig +0 -13
  28. data/.envrc +0 -4
  29. data/.gitignore +0 -16
  30. data/.rspec +0 -1
  31. data/.ruby-version +0 -1
  32. data/.travis.yml +0 -18
  33. data/.yardopts +0 -1
  34. data/Appraisals +0 -18
  35. data/Gemfile +0 -4
  36. data/Rakefile +0 -39
  37. data/bin/appraisal +0 -29
  38. data/bin/rake +0 -29
  39. data/bin/rspec +0 -29
  40. data/docker-compose.yml +0 -19
  41. data/gemfiles/rails_5_1.gemfile +0 -11
  42. data/gemfiles/rails_5_1.gemfile.lock +0 -101
  43. data/gemfiles/rails_5_2.gemfile +0 -11
  44. data/gemfiles/rails_5_2.gemfile.lock +0 -101
  45. data/gemfiles/rails_6_0.gemfile +0 -9
  46. data/gemfiles/rails_6_0.gemfile.lock +0 -101
  47. data/lib/vorpal/configs.rb +0 -296
  48. data/spec/acceptance/vorpal/aggregate_mapper_spec.rb +0 -910
  49. data/spec/helpers/codecov_helper.rb +0 -7
  50. data/spec/helpers/db_helpers.rb +0 -69
  51. data/spec/helpers/profile_helpers.rb +0 -26
  52. data/spec/integration/vorpal/driver/postgresql_spec.rb +0 -42
  53. data/spec/integration_spec_helper.rb +0 -29
  54. data/spec/performance/vorpal/performance_spec.rb +0 -305
  55. data/spec/unit/vorpal/configs_spec.rb +0 -117
  56. data/spec/unit/vorpal/db_loader_spec.rb +0 -103
  57. data/spec/unit/vorpal/dsl/config_builder_spec.rb +0 -18
  58. data/spec/unit/vorpal/dsl/defaults_generator_spec.rb +0 -75
  59. data/spec/unit/vorpal/identity_map_spec.rb +0 -62
  60. data/spec/unit/vorpal/loaded_objects_spec.rb +0 -22
  61. data/spec/unit/vorpal/util/string_utils_spec.rb +0 -25
  62. data/spec/unit_spec_helper.rb +0 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6567d89919ba2836790c9d13fa729c43c21723fbcfa12ef8f77be2552e3f5df5
4
- data.tar.gz: 3b5ebeea70138f7cf6e4e68c0e21d4024e3732c1cf6cbe1f04054a03fab815d0
3
+ metadata.gz: c236d5dee0ca27224473d24e59680317a5b30fae7e400a13738298d27e6f1e74
4
+ data.tar.gz: ffe4922245341adc4ef2e4ed25751dee28a03232c2bb5b19699120f07d7fd3d6
5
5
  SHA512:
6
- metadata.gz: 7f991b2d7a25ebf9d85bc7a2af59c02e7105ff981724372167e13e6a30ffcd4a9bfdcd7db5a886b22bbbd9c86e52065a69a9e8c072e805420d16b1cdb4ed0151
7
- data.tar.gz: 5bdcb19c91b9ce1de724caa2231d86903e472d65ebd6a47a7fe39c705b31437b6b797e9a0df70c841251581ea882a5d0a33c1fdc236030fe951fc9bb1cf3c723
6
+ metadata.gz: d6ba4c4ab43cc66a776be399be185130665ea4951a18a34e664b45d851801b37260ce4eee343f81511910e739f7451b3b30ead059c24712e9ee577c579fdeffa
7
+ data.tar.gz: e134e15a04d0e7037fbbedf83f5e381e281e72f8bae51e43b0f9b488877477d7d6dfb7350c037e9bfb06279ac78d74024b1e592725cd8673348bf6ad4d6f61c7
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Vorpal [![Build Status](https://travis-ci.org/nulogy/vorpal.svg?branch=master)](https://travis-ci.org/nulogy/vorpal) [![Code Climate](https://codeclimate.com/github/nulogy/vorpal/badges/gpa.svg)](https://codeclimate.com/github/nulogy/vorpal) [![Code Coverage](https://codecov.io/gh/nulogy/vorpal/branch/master/graph/badge.svg)](https://codecov.io/gh/nulogy/vorpal/branch/master/graph/badge.svg)
1
+ # Vorpal [![Build Status](https://travis-ci.com/nulogy/vorpal.svg?branch=main)](https://travis-ci.com/nulogy/vorpal) [![Code Climate](https://codeclimate.com/github/nulogy/vorpal/badges/gpa.svg)](https://codeclimate.com/github/nulogy/vorpal) [![Code Coverage](https://codecov.io/gh/nulogy/vorpal/branch/main/graph/badge.svg)](https://codecov.io/gh/nulogy/vorpal/branch/main)
2
2
 
3
3
  Separate your domain model from your persistence mechanism. Some problems call for a really sharp tool.
4
4
 
@@ -45,27 +45,21 @@ Or install it yourself as:
45
45
  Start with a domain model of POROs and AR::Base objects that form an aggregate:
46
46
 
47
47
  ```ruby
48
- class Tree; end
49
-
50
48
  class Branch
51
- include Virtus.model
52
-
53
- attribute :id, Integer
54
- attribute :length, Decimal
55
- attribute :diameter, Decimal
56
- attribute :tree, Tree
49
+ attr_accessor :id
50
+ attr_accessor :length
51
+ attr_accessor :diameter
52
+ attr_accessor :tree
57
53
  end
58
54
 
59
- class Gardener < ActiveRecord::Base
55
+ class Gardener
60
56
  end
61
57
 
62
58
  class Tree
63
- include Virtus.model
64
-
65
- attribute :id, Integer
66
- attribute :name, String
67
- attribute :gardener, Gardener
68
- attribute :branches, Array[Branch]
59
+ attr_accessor :id
60
+ attr_accessor :name
61
+ attr_accessor :gardener
62
+ attr_accessor :branches
69
63
  end
70
64
  ```
71
65
 
@@ -162,9 +156,33 @@ TreeRepository.destroy(dead_tree)
162
156
  TreeRepository.destroy_by_id(dead_tree_id)
163
157
  ```
164
158
 
159
+ ### Ids
160
+
161
+ Vorpal by default will use auto-incrementing Integers from a DB sequence for ids. However, UUID v4 ids are also
162
+ supported:
163
+
164
+ ```ruby
165
+ Vorpal.define do
166
+ # UUID v4 id!
167
+ map Tree, primary_key_type: :uuid do
168
+ # ..
169
+ end
170
+
171
+ # Also a UUID v4 id, the Rails Way!
172
+ map Trunk, id: :uuid do
173
+ # ..
174
+ end
175
+
176
+ # If you feel the need to specify an auto-incrementing integer id.
177
+ map Branch, primary_key_type: :serial do
178
+ # ..
179
+ end
180
+ end
181
+ ```
182
+
165
183
  ## API Documentation
166
184
 
167
- http://rubydoc.info/github/nulogy/vorpal/master/frames
185
+ http://rubydoc.info/github/nulogy/vorpal
168
186
 
169
187
  ## Caveats
170
188
  It also does not do some things that you might expect from other ORMs:
@@ -181,13 +199,11 @@ It also does not do some things that you might expect from other ORMs:
181
199
  1. Only supports PostgreSQL.
182
200
 
183
201
  ## Future Enhancements
184
- * Aggregate updated_at.
185
- * Support for other DBMSs (no MySQL support until ids can be generated without inserting into a table!)
186
- * Support for other ORMs.
187
- * Value objects.
188
- * Remove dependency on ActiveRecord (optimistic locking? updated_at, created_at support? Data type conversions? TimeZone support?)
189
- * More efficient updates (use fewer queries.)
202
+ * Support for having foreign keys point to columns other than primary keys.
203
+ * Support for storing entity ids in a column called something other than "id".
190
204
  * Nicer DSL for specifying attributes that have different names in the domain model than in the DB.
205
+ * Aggregate updated_at.
206
+ * Better support for value objects.
191
207
 
192
208
  ## FAQ
193
209
 
@@ -203,11 +219,12 @@ It also does not do some things that you might expect from other ORMs:
203
219
 
204
220
  **A.** Create a method on a [Repository](http://martinfowler.com/eaaCatalog/repository.html)! They have full access to the DB/ORM so you can use [Arel](https://github.com/rails/arel) and go [crazy](http://asciicasts.com/episodes/239-activerecord-relation-walkthrough) or use direct SQL if you want.
205
221
 
206
- For example:
222
+ For example, use the [#query](https://rubydoc.info/github/nulogy/vorpal/main/Vorpal/AggregateMapper#query-instance_method) method on the [AggregateMapper](https://rubydoc.info/github/nulogy/vorpal/main/Vorpal/AggregateMapper) to access the underyling [ActiveRecordRelation](https://api.rubyonrails.org/classes/ActiveRecord/Relation.html):
207
223
 
208
224
  ```ruby
209
- def find_all
210
- @mapper.query.load_all # use the mapper to load all the aggregates
225
+ def find_special_ones
226
+ # use `load_all` or `load_one` to convert from ActiveRecord objects to domain POROs.
227
+ @mapper.query.where(special: true).load_all
211
228
  end
212
229
  ```
213
230
 
@@ -229,6 +246,28 @@ For example:
229
246
 
230
247
  **A.** You can use [ActiveModel::Serialization](http://api.rubyonrails.org/classes/ActiveModel/Serialization.html) or [ActiveModel::Serializers](https://github.com/rails-api/active_model_serializers) but they are not heartily recommended. The former is too coupled to the model and the latter is too coupled to Rails controllers. Vorpal uses [SimpleSerializer](https://github.com/nulogy/simple_serializer) for this purpose.
231
248
 
249
+ **Q.** Are `updated_at` and `created_at` supported?
250
+
251
+ **A.** Yes. If they exist on your database tables, they will behave exactly as if you were using vanilla ActiveRecord.
252
+
253
+ **Q.** How do I tell ActiveRecord to ignore certain columns so that I can rename/remove them using zero-downtime deploys?
254
+
255
+ **A.** You will want to use ActiveRecord's [`ignored_columns=`](https://api.rubyonrails.org/classes/ActiveRecord/ModelSchema/ClassMethods.html#method-i-ignored_columns) method like this:
256
+
257
+ ```ruby
258
+ engine = Vorpal.define do
259
+ map Product do
260
+ attributes(
261
+ :name,
262
+ :description,
263
+ # :column_to_remove
264
+ )
265
+ end
266
+ end
267
+ product_ar_class = engine.mapper_for(Product).db_class
268
+ product_ar_class.ignored_columns = [:column_to_remove]
269
+ ```
270
+
232
271
  ## Contributing
233
272
 
234
273
  1. Fork it ( https://github.com/nulogy/vorpal/fork )
@@ -265,6 +304,14 @@ For example:
265
304
 
266
305
  Please see the [Appraisal gem docs](https://github.com/thoughtbot/appraisal) for more information.
267
306
 
307
+ ### Releasing
308
+
309
+ 1. Update the version number in `lib/vorpal/version.rb`
310
+ 2. Update the version of Vorpal in the Appraisal gemfiles (otherwise Travis CI will fail): `appraisal install`
311
+ 3. Commit the above changes with the message: `Bump version to <X.Y.Z>`
312
+ 4. Release the new version to Rubygems: `rake release`
313
+ 5. Profit!
314
+
268
315
  ## Contributors
269
316
 
270
317
  See who's [contributed](https://github.com/nulogy/vorpal/graphs/contributors)!
@@ -27,12 +27,12 @@ module Vorpal
27
27
  # Loads an aggregate from the DB. Will eagerly load all objects in the
28
28
  # aggregate and on the boundary (owned: false).
29
29
  #
30
- # @param db_root [Object] DB representation of the root of the aggregate to be
30
+ # @param db_root [Object, nil] DB representation of the root of the aggregate to be
31
31
  # loaded. This can be nil.
32
32
  # @param identity_map [IdentityMap] Provide your own IdentityMap instance
33
33
  # if you want entity id -> unique object mapping for a greater scope than one
34
34
  # operation.
35
- # @return [Object] Aggregate root corresponding to the given DB representation.
35
+ # @return [Object, nil] Aggregate root corresponding to the given DB representation.
36
36
  def load_one(db_root, identity_map=IdentityMap.new)
37
37
  @engine.load_one(db_root, @domain_class, identity_map)
38
38
  end
@@ -84,6 +84,17 @@ module Vorpal
84
84
  @engine
85
85
  end
86
86
 
87
+ # Returns a 'Vorpal-aware' [ActiveRecord::Relation](https://api.rubyonrails.org/classes/ActiveRecord/Relation.html)
88
+ # for the ActiveRecord object underlying the domain entity mapped by this mapper.
89
+ #
90
+ # This method allows you to easily access the power of ActiveRecord::Relation to do more complex
91
+ # queries in your repositories.
92
+ #
93
+ # The ActiveRecord::Relation is 'Vorpal-aware' because it has the {#load_one} and {#load_many} methods
94
+ # mixed in so that you can get the POROs from your domain model instead of the ActiveRecord
95
+ # objects normally returned by ActiveRecord::Relation.
96
+ #
97
+ # @return [ActiveRecord::Relation]
87
98
  def query
88
99
  @engine.query(@domain_class)
89
100
  end
@@ -19,19 +19,20 @@ module Vorpal
19
19
  visitor.visit_object(object, config)
20
20
 
21
21
  config.belongs_tos.each do |belongs_to_config|
22
- child = belongs_to_config.get_child(object)
23
- accept(child, visitor, already_visited) if visitor.continue_traversal?(belongs_to_config)
22
+ associate = belongs_to_config.get_associated(object)
23
+ accept(associate, visitor, already_visited) if visitor.continue_traversal?(belongs_to_config)
24
24
  end
25
25
 
26
26
  config.has_ones.each do |has_one_config|
27
- child = has_one_config.get_child(object)
28
- accept(child, visitor, already_visited) if visitor.continue_traversal?(has_one_config)
27
+ associate = has_one_config.get_associated(object)
28
+ accept(associate, visitor, already_visited) if visitor.continue_traversal?(has_one_config)
29
29
  end
30
30
 
31
31
  config.has_manys.each do |has_many_config|
32
- children = has_many_config.get_children(object)
33
- children.each do |child|
34
- accept(child, visitor, already_visited) if visitor.continue_traversal?(has_many_config)
32
+ associates = has_many_config.get_associated(object)
33
+ raise InvariantViolated.new("#{has_many_config.pretty_name} was set to nil. Use an empty array instead.") if associates.nil?
34
+ associates.each do |associate|
35
+ accept(associate, visitor, already_visited) if visitor.continue_traversal?(has_many_config)
35
36
  end
36
37
  end
37
38
  end
@@ -47,4 +48,4 @@ module Vorpal
47
48
  true
48
49
  end
49
50
  end
50
- end
51
+ end
@@ -0,0 +1,84 @@
1
+ require 'vorpal/config/foreign_key_info'
2
+ require 'equalizer'
3
+
4
+ module Vorpal
5
+ module Config
6
+ # @private
7
+ # Object association terminology:
8
+ # - All object associations are uni-directional
9
+ # - The end that holds the association is the 'Owner' and the end that
10
+ # is referred to is the 'Associate' or 'Associates'
11
+ #
12
+ # Relational association terminology:
13
+ # - Local end: has FK
14
+ # - Remote end: has no FK
15
+ #
16
+ class AssociationConfig
17
+ include Equalizer.new(:local_class_config, :fk)
18
+
19
+ attr_reader :local_class_config, :remote_class_configs, :fk
20
+
21
+ # Only one of these two attributes needs to be specified
22
+ # If one is specified, then the association is uni-directional.
23
+ # If both are specified, then the association is bi-directional.
24
+ attr_accessor :local_end_config, :remote_end_config
25
+
26
+ def initialize(local_class_config, fk, fk_type)
27
+ @local_class_config = local_class_config
28
+ @remote_class_configs = {}
29
+ @fk = fk
30
+ @fk_type = fk_type
31
+ end
32
+
33
+ def fk_value(local_db_object)
34
+ local_db_object.send(fk)
35
+ end
36
+
37
+ def associate(local_object, remote_object)
38
+ local_end_config.associate(local_object, remote_object) if local_end_config && local_object
39
+ remote_end_config.associate(remote_object, local_object) if remote_end_config && remote_object
40
+ end
41
+
42
+ def add_remote_class_config(remote_class_configs)
43
+ Array(remote_class_configs).each do |remote_class_config|
44
+ @remote_class_configs[remote_class_config.domain_class.name] = remote_class_config
45
+ end
46
+ end
47
+
48
+ def remote_class_config(local_db_object)
49
+ if polymorphic?
50
+ fk_type_value = local_db_object.send(@fk_type)
51
+ @remote_class_configs[fk_type_value]
52
+ else
53
+ @remote_class_configs.values.first
54
+ end
55
+ end
56
+
57
+ def polymorphic?
58
+ !@fk_type.nil?
59
+ end
60
+
61
+ def set_foreign_key(local_db_object, remote_object)
62
+ local_class_config.set_attribute(local_db_object, @fk, remote_object&.send(unique_key_name))
63
+ local_class_config.set_attribute(local_db_object, @fk_type, remote_object.class.name) if polymorphic?
64
+ end
65
+
66
+ # @return ForeignKeyInfo
67
+ def foreign_key_info(remote_class_config)
68
+ ForeignKeyInfo.new(@fk, @fk_type, remote_class_config.domain_class.name, polymorphic?)
69
+ end
70
+
71
+ def unique_key_name
72
+ (@local_end_config || @remote_end_config).unique_key_name
73
+ end
74
+
75
+ def validate
76
+ if @local_end_config && @remote_end_config
77
+ if @local_end_config.unique_key_name != @remote_end_config.unique_key_name
78
+ raise ConfigurationError.new("#{@local_end_config.pretty_name} and #{@remote_end_config.pretty_name} must have the same unique_key_name/primary_key")
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,35 @@
1
+ require 'vorpal/config/configs'
2
+
3
+ module Vorpal
4
+ module Config
5
+ # @private
6
+ # Object association terminology:
7
+ # - All object associations are uni-directional
8
+ # - The end that holds the association is the 'Owner' and the end that
9
+ # is referred to is the 'Associate' or 'Associates'
10
+ #
11
+ # Relational association terminology:
12
+ # - Local end: has FK
13
+ # - Remote end: has no FK
14
+ #
15
+ class BelongsToConfig
16
+ include Util::HashInitialization
17
+ include LocalEndConfig
18
+
19
+ attr_reader :name, :owned, :fk, :fk_type, :associated_classes, :unique_key_name
20
+ attr_accessor :association_config
21
+
22
+ def get_associated(owner)
23
+ owner.send(name)
24
+ end
25
+
26
+ def associate(owner, associate)
27
+ owner.send("#{name}=", associate)
28
+ end
29
+
30
+ def pretty_name
31
+ "#{association_config.local_class_config.domain_class.name} belongs_to :#{name}"
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,71 @@
1
+ require 'equalizer'
2
+
3
+ module Vorpal
4
+ module Config
5
+ # @private
6
+ class ClassConfig
7
+ include Equalizer.new(:domain_class, :db_class)
8
+ attr_reader :serializer, :deserializer, :domain_class, :db_class, :primary_key_type, :local_association_configs
9
+ attr_accessor :has_manys, :belongs_tos, :has_ones
10
+
11
+ ALLOWED_PRIMARY_KEY_TYPE_OPTIONS = [:serial, :uuid]
12
+
13
+ def initialize(attrs)
14
+ @has_manys = []
15
+ @belongs_tos = []
16
+ @has_ones = []
17
+ @local_association_configs = []
18
+
19
+ @serializer = attrs[:serializer]
20
+ @deserializer = attrs[:deserializer]
21
+ @domain_class = attrs[:domain_class]
22
+ @db_class = attrs[:db_class]
23
+ @primary_key_type = attrs[:primary_key_type]
24
+ raise "Invalid primary_key_type: '#{@primary_key_type}'" unless ALLOWED_PRIMARY_KEY_TYPE_OPTIONS.include?(@primary_key_type)
25
+ end
26
+
27
+ def build_db_object(attributes)
28
+ db_class.new(attributes)
29
+ end
30
+
31
+ def set_db_object_attributes(db_object, attributes)
32
+ db_object.attributes = attributes
33
+ end
34
+
35
+ def get_db_object_attributes(db_object)
36
+ symbolize_keys(db_object.attributes)
37
+ end
38
+
39
+ def serialization_required?
40
+ domain_class.superclass.name != 'ActiveRecord::Base'
41
+ end
42
+
43
+ def serialize(object)
44
+ serializer.serialize(object)
45
+ end
46
+
47
+ def deserialize(db_object)
48
+ attributes = get_db_object_attributes(db_object)
49
+ serialization_required? ? deserializer.deserialize(domain_class.new, attributes) : db_object
50
+ end
51
+
52
+ def set_attribute(db_object, attribute, value)
53
+ db_object.send("#{attribute}=", value)
54
+ end
55
+
56
+ def get_attribute(db_object, attribute)
57
+ db_object.send(attribute)
58
+ end
59
+
60
+ private
61
+
62
+ def symbolize_keys(hash)
63
+ result = {}
64
+ hash.each_key do |key|
65
+ result[key.to_sym] = hash[key]
66
+ end
67
+ result
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,54 @@
1
+ require 'vorpal/util/hash_initialization'
2
+ require 'vorpal/exceptions'
3
+ require 'equalizer'
4
+
5
+ module Vorpal
6
+ module Config
7
+ # @private
8
+ # Object association terminology:
9
+ # - All object associations are uni-directional
10
+ # - The end that holds the association is the 'Owner' and the end that
11
+ # is referred to is the 'Associate' or 'Associates'
12
+ #
13
+ # Relational association terminology:
14
+ # - Local end: has FK
15
+ # - Remote end: has no FK
16
+ #
17
+ module RemoteEndConfig
18
+ def associated_class_config
19
+ association_config.local_class_config
20
+ end
21
+
22
+ def set_foreign_key(db_associate, owner)
23
+ association_config.set_foreign_key(db_associate, owner)
24
+ end
25
+
26
+ def set_class_config(class_config)
27
+ @class_config = class_config
28
+ end
29
+
30
+ def foreign_key_info
31
+ association_config.foreign_key_info(@class_config)
32
+ end
33
+
34
+ def get_unique_key_value(db_owner)
35
+ db_owner.send(unique_key_name)
36
+ end
37
+ end
38
+
39
+ # @private
40
+ module LocalEndConfig
41
+ def associated_class_config(db_owner)
42
+ association_config.remote_class_config(db_owner)
43
+ end
44
+
45
+ def set_foreign_key(db_owner, associate)
46
+ association_config.set_foreign_key(db_owner, associate)
47
+ end
48
+
49
+ def fk_value(db_owner)
50
+ db_owner.send(fk)
51
+ end
52
+ end
53
+ end
54
+ end