datamappify 0.50.0 → 0.51.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 (36) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +7 -0
  3. data/README.md +78 -4
  4. data/lib/datamappify/data/criteria/active_record/find_multiple.rb +28 -0
  5. data/lib/datamappify/data/criteria/common.rb +28 -8
  6. data/lib/datamappify/data/criteria/relational/find_multiple.rb +62 -3
  7. data/lib/datamappify/data/criteria/sequel/find_multiple.rb +31 -0
  8. data/lib/datamappify/data/errors.rb +3 -0
  9. data/lib/datamappify/data/mapper/attribute.rb +81 -3
  10. data/lib/datamappify/data/mapper.rb +12 -12
  11. data/lib/datamappify/data/provider/active_record.rb +20 -5
  12. data/lib/datamappify/data/provider/common_provider.rb +2 -0
  13. data/lib/datamappify/data/provider/sequel.rb +25 -6
  14. data/lib/datamappify/data/record.rb +18 -5
  15. data/lib/datamappify/entity/composable.rb +63 -7
  16. data/lib/datamappify/entity/relation.rb +1 -1
  17. data/lib/datamappify/repository/inheritable.rb +28 -0
  18. data/lib/datamappify/repository/query_method/find_multiple.rb +1 -7
  19. data/lib/datamappify/repository/query_method/method.rb +2 -2
  20. data/lib/datamappify/repository/query_methods.rb +8 -4
  21. data/lib/datamappify/repository.rb +2 -0
  22. data/lib/datamappify/version.rb +1 -1
  23. data/spec/entity/composable_spec.rb +34 -11
  24. data/spec/entity/inheritance_spec.rb +33 -0
  25. data/spec/entity/relation_spec.rb +11 -9
  26. data/spec/repository/finders_spec.rb +185 -7
  27. data/spec/support/entities/admin_user.rb +5 -0
  28. data/spec/support/entities/computer.rb +2 -0
  29. data/spec/support/entities/computer_hardware.rb +4 -0
  30. data/spec/support/entities/computer_software.rb +3 -0
  31. data/spec/support/repositories/active_record/admin_user_repository.rb +5 -0
  32. data/spec/support/repositories/sequel/admin_user_repository.rb +5 -0
  33. data/spec/support/tables/active_record.rb +1 -0
  34. data/spec/support/tables/sequel.rb +1 -0
  35. data/spec/unit/entity/relation_spec.rb +12 -2
  36. metadata +11 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 51bd14425d7e0347cf2a97adff354cb262038152
4
- data.tar.gz: f7fc7a27a71d6e5cb10c971f9a7e57fc968ac8d0
3
+ metadata.gz: f9765005a9b047c0dabab75eeb3da7997761c57d
4
+ data.tar.gz: c6747c591e913b7565a730ea7192703991aaf2b6
5
5
  SHA512:
6
- metadata.gz: ef748d505249785e6da9518a05eaab78cca206517eb708900e43e4342bedf01be6f2c369f1570058866dc10a7ac4c7a560b5ddb0371c4b35e0a6b3abdc2e2eaf
7
- data.tar.gz: e7a1d50c78433c4945b06507ad60fe366f7f9623fa806cafbf54a3b99229232dafca583ea31e1d3a827729eeffcf108f4712e607b5f80fde005abf66782885e4
6
+ metadata.gz: 64566dfe4f603db5104657ca9416aef1d96f4b0327de25c0c9a36a68285b3dcf049efd2c39381f5f4f2ab7b781509c13f0c4ef27b02066e9b634e5cd23c56720
7
+ data.tar.gz: 0bc81a94c2c52c1b3da152d79f39405be5050e2242c526d628650591a4e741250f5d47136c93ef96a2a1f63f6c264200754ad603ea41954aafaffb2ce2f91cda
data/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  ## master
2
2
 
3
+ ## 0.51.0 [2013-06-17]
4
+
5
+ - Enhanced `find` for searching specific attributes.
6
+ - Added support for repository inheritance.
7
+ - `attributes_from` now copies validations as well!
8
+ - `find` now raises an exception if the arguments contain an unknown attribute key.
9
+
3
10
  ## 0.50.0 [2013-05-30]
4
11
 
5
12
  - Added `all` to repository.
data/README.md CHANGED
@@ -50,16 +50,50 @@ class User
50
50
  attribute :driver_license, String
51
51
  attribute :health_care, String
52
52
 
53
- # Nested entity composition - composing the entity with attributes from other entities
53
+ # Nested entity composition - composing the entity with attributes and validations from other entities
54
+ #
55
+ # class Job
56
+ # include Datamappify::Entity
57
+ #
58
+ # attributes :title, String
59
+ # validates :title, :presence => true
60
+ # end
61
+ #
62
+ # class User
63
+ # # ...
64
+ # attributes_from Job
65
+ # end
66
+ #
67
+ # essentially equals:
68
+ #
69
+ # class User
70
+ # # ...
71
+ # attributes :title, String
72
+ # validates :title, :presence => true
73
+ # end
54
74
  attributes_from Job
55
75
 
56
76
  # optionally you may prefix the attributes, so that:
57
77
  #
58
- # :programming
78
+ # class Hobby
79
+ # include Datamappify::Entity
80
+ #
81
+ # attributes :name, String
82
+ # validates :name, :presence => true
83
+ # end
84
+ #
85
+ # class User
86
+ # # ...
87
+ # attributes_from Hobby, :prefix_with => :hobby
88
+ # end
59
89
  #
60
90
  # becomes:
61
91
  #
62
- # :hobby_programming
92
+ # class User
93
+ # # ...
94
+ # attributes :hobby_name, String
95
+ # validates :hobby_name, :presence => true
96
+ # end
63
97
  attributes_from Hobby, :prefix_with => :hobby
64
98
 
65
99
  # Entity reference
@@ -85,6 +119,18 @@ class User
85
119
  end
86
120
  ```
87
121
 
122
+ Inheritance is supported for entities, for example:
123
+
124
+ ```ruby
125
+ class AdminUser < User
126
+ attribute :level, Integer
127
+ end
128
+
129
+ class GuestUser < User
130
+ attribute :expiry, DateTime
131
+ end
132
+ ```
133
+
88
134
  #### Lazy loading
89
135
 
90
136
  Datamappify supports attribute lazy loading via the `Lazy` module.
@@ -126,6 +172,24 @@ class UserRepository
126
172
  end
127
173
  ```
128
174
 
175
+ Inheritance is supported for repositories when your data structure is based on STI ([Single Table Inheritance](http://en.wikipedia.org/wiki/Single_Table_Inheritance)), for example:
176
+
177
+ ```ruby
178
+ class AdminUserRepository < UserRepository
179
+ for_entity AdminUser
180
+ end
181
+
182
+ class GuestUserRepository < UserRepository
183
+ for_entity GuestUser
184
+
185
+ map_attribute :expiry, 'ActiveRecord::User#expiry_date'
186
+ end
187
+ ```
188
+
189
+ In the above example, both repositories deal with the `User` data model.
190
+
191
+ ### Repository APIs
192
+
129
193
  _More repository APIs are being added, below is a list of the currently implemented APIs._
130
194
 
131
195
  #### Retrieving an entity
@@ -146,12 +210,22 @@ UserRepository.exists?(user)
146
210
 
147
211
  #### Retrieving all entities
148
212
 
149
- Returns an array of user entities.
213
+ Returns an array of entities.
150
214
 
151
215
  ```ruby
152
216
  users = UserRepository.all
153
217
  ```
154
218
 
219
+ #### Searching entities
220
+
221
+ Returns an array of entities.
222
+
223
+ ```ruby
224
+ users = UserRepository.find(:first_name => 'Fred', :driver_license => 'AABBCCDD')
225
+ ```
226
+
227
+ _Note: it does not currently support searching attributes from different data providers._
228
+
155
229
  #### Saving/updating entities
156
230
 
157
231
  Pass in an entity.
@@ -5,6 +5,34 @@ module Datamappify
5
5
  module Criteria
6
6
  module ActiveRecord
7
7
  class FindMultiple < Relational::FindMultiple
8
+ private
9
+
10
+ # @return [Array]
11
+ def records
12
+ source_class.joins(@secondaries.map(&:source_key)).where(
13
+ structured_criteria(primaries, secondaries)
14
+ )
15
+ end
16
+
17
+ # @param primaries [Array<Attribute>]
18
+ #
19
+ # @param secondaries [Array<Attribute>]
20
+ #
21
+ # @return [Hash]
22
+ def structured_criteria(primaries, secondaries)
23
+ _criteria = {}
24
+
25
+ primaries.each do |primary|
26
+ _criteria[primary.source_attribute_key] = primary.value
27
+ end
28
+
29
+ secondaries.each do |secondary|
30
+ _criteria[secondary.source_table] ||= {}
31
+ _criteria[secondary.source_table][secondary.source_attribute_key] = secondary.value
32
+ end
33
+
34
+ _criteria
35
+ end
8
36
  end
9
37
  end
10
38
  end
@@ -41,11 +41,19 @@ module Datamappify
41
41
 
42
42
  protected
43
43
 
44
+ # Name of the default source class, e.g. +"User"+,
45
+ # it is determined from either the PK or the entity
46
+ #
47
+ # @return [String]
48
+ def default_source_class_name
49
+ @default_source_class_name ||= pk ? pk.source_class_name : entity.class.name.demodulize
50
+ end
51
+
44
52
  # Key name of either the primary key (e.g. +id+) or foreign key (e.g. +user_id+)
45
53
  #
46
54
  # @return [Symbol]
47
55
  def key_name
48
- primary_record? ? :id : "#{entity.class.name.demodulize.underscore}_id".to_sym
56
+ primary_record? ? :id : any_attribute.primary_reference_key
49
57
  end
50
58
 
51
59
  # The value of {#key_name}
@@ -66,7 +74,7 @@ module Datamappify
66
74
  #
67
75
  # @return [Boolean]
68
76
  def primary_record?
69
- source_class.name.demodulize == entity.class.name.demodulize
77
+ source_class.name.demodulize == default_source_class_name
70
78
  end
71
79
 
72
80
  # Ignores the current Criteria's operation if there is no dirty attributes
@@ -80,15 +88,17 @@ module Datamappify
80
88
  #
81
89
  # @return [Hash]
82
90
  def attributes_and_values
83
- hash = {}
91
+ @attributes_and_values ||= begin
92
+ hash = {}
84
93
 
85
- attributes.each do |attribute|
86
- unless ignore_attribute?(attribute)
87
- hash[attribute.source_attribute_name] = entity.send(attribute.name)
94
+ attributes.each do |attribute|
95
+ unless ignore_attribute?(attribute)
96
+ hash[attribute.source_attribute_name] = entity.send(attribute.name)
97
+ end
88
98
  end
89
- end
90
99
 
91
- hash
100
+ hash
101
+ end
92
102
  end
93
103
 
94
104
  # Stores the attribute value in {Mapper::Attribute} for later use
@@ -100,6 +110,16 @@ module Datamappify
100
110
  end
101
111
  end
102
112
 
113
+ # @return [Attribute]
114
+ def any_attribute
115
+ @any_attribute ||= attributes.first
116
+ end
117
+
118
+ # @return [Attribute]
119
+ def pk
120
+ @pk ||= attributes.find { |attribute| attribute.key == :id }
121
+ end
122
+
103
123
  private
104
124
 
105
125
  # Ignores the attribute if it isn't dirty or if it's a primary key
@@ -5,8 +5,22 @@ module Datamappify
5
5
  class FindMultiple < Common
6
6
  alias_method :entity_class, :entity
7
7
 
8
+ attr_reader :primaries, :secondaries, :structured_criteria
9
+
10
+ def initialize(*args)
11
+ super
12
+
13
+ @primaries = []
14
+ @secondaries = []
15
+
16
+ updated_attributes.each do |attribute|
17
+ collector = attribute.primary_attribute? ? @primaries : @secondaries
18
+ collector << attribute
19
+ end
20
+ end
21
+
22
+ # @return [void]
8
23
  def perform
9
- records = source_class.where(criteria)
10
24
  records.map do |record|
11
25
  entity = entity_class.new
12
26
  update_entity(entity, record)
@@ -16,11 +30,56 @@ module Datamappify
16
30
 
17
31
  private
18
32
 
19
- def update_entity(entity, record)
33
+ # @return [Array]
34
+ def updated_attributes
35
+ unless criteria.keys & attributes.map(&:key) == criteria.keys
36
+ raise EntityAttributeInvalid
37
+ end
38
+
39
+ @updated_attributes ||= attributes.select do |attribute|
40
+ attribute.value = criteria[attribute.key]
41
+ criteria.keys.include?(attribute.key)
42
+ end
43
+ end
44
+
45
+ # @param entity [Entity]
46
+ #
47
+ # @param record [Class]
48
+ #
49
+ # @return [void]
50
+ def update_entity(entity, primary_record)
20
51
  attributes.each do |attribute|
21
- entity.send("#{attribute.name}=", record.send(attribute.source_attribute_name))
52
+ record = data_record_for(attribute, primary_record)
53
+ value = record_value_for(attribute, record)
54
+
55
+ entity.send("#{attribute.name}=", value)
56
+ end
57
+ end
58
+
59
+ # @param attribute [Attribute]
60
+ #
61
+ # @param primary_record [Class]
62
+ # an ORM model object (ActiveRecord or Sequel, etc)
63
+ #
64
+ # @return [Object]
65
+ # an ORM model object (ActiveRecord or Sequel, etc)
66
+ def data_record_for(attribute, primary_record)
67
+ if attribute.primary_attribute?
68
+ primary_record
69
+ else
70
+ primary_record.send(attribute.source_key)
22
71
  end
23
72
  end
73
+
74
+ # @param attribute [Attribute]
75
+ #
76
+ # @param record [Class]
77
+ # an ORM model object (ActiveRecord or Sequel, etc)
78
+ #
79
+ # @return [any]
80
+ def record_value_for(attribute, record)
81
+ record.nil? ? nil : record.send(attribute.source_attribute_name)
82
+ end
24
83
  end
25
84
  end
26
85
  end
@@ -5,6 +5,37 @@ module Datamappify
5
5
  module Criteria
6
6
  module Sequel
7
7
  class FindMultiple < Relational::FindMultiple
8
+ private
9
+
10
+ # @return [Array]
11
+ def records
12
+ query_builder = source_class
13
+
14
+ secondaries.each do |secondary|
15
+ query_builder = query_builder.join(secondary.source_table, secondary.primary_reference_key => :id)
16
+ end
17
+
18
+ query_builder.where(structured_criteria(primaries, secondaries))
19
+ end
20
+
21
+ # @param primaries [Array<Attribute>]
22
+ #
23
+ # @param secondaries [Array<Attribute>]
24
+ #
25
+ # @return [Hash]
26
+ def structured_criteria(primaries, secondaries)
27
+ _criteria = {}
28
+
29
+ primaries.each do |primary|
30
+ _criteria[primary.source_attribute_key] = primary.value
31
+ end
32
+
33
+ secondaries.each do |secondary|
34
+ _criteria[:"#{secondary.source_table}__#{secondary.source_attribute_name}"] = secondary.value
35
+ end
36
+
37
+ _criteria
38
+ end
8
39
  end
9
40
  end
10
41
  end
@@ -18,5 +18,8 @@ module Datamappify
18
18
 
19
19
  class EntityNotDestroyed < Error
20
20
  end
21
+
22
+ class EntityAttributeInvalid < Error
23
+ end
21
24
  end
22
25
  end
@@ -20,6 +20,9 @@ module Datamappify
20
20
  # @return [String]
21
21
  attr_reader :source_attribute_name
22
22
 
23
+ # @return [Class]
24
+ attr_reader :primary_source_class
25
+
23
26
  # @return [any]
24
27
  attr_accessor :value
25
28
 
@@ -29,23 +32,98 @@ module Datamappify
29
32
  # @param source [String]
30
33
  # data provider, class and attribute,
31
34
  # e.g. "ActiveRecord::User#surname"
32
- def initialize(name, source)
33
- @key = name
34
- @name = name.to_s
35
+ #
36
+ # @param primary_source_class [Class]
37
+ def initialize(name, source, primary_source_class)
38
+ @key = name
39
+ @name = name.to_s
40
+ @primary_source_class = primary_source_class
35
41
 
36
42
  @provider_name, @source_class_name, @source_attribute_name = parse_source(source)
43
+
44
+ unless primary_attribute? || external_attribute?
45
+ Record.build_association(self, primary_source_class)
46
+ end
37
47
  end
38
48
 
49
+ # @example
50
+ #
51
+ # UserComment
52
+ #
39
53
  # @return [Class]
40
54
  def source_class
41
55
  @source_class ||= Record.find_or_build(provider_name, source_class_name)
42
56
  end
43
57
 
58
+ # @example
59
+ #
60
+ # "user_comment"
61
+ #
62
+ # @return [String]
63
+ def source_name
64
+ @source_name ||= source_class_name.underscore
65
+ end
66
+
67
+ # @example
68
+ #
69
+ # :user_comment
70
+ #
71
+ # @return [Symbol]
72
+ def source_key
73
+ @source_key ||= source_name.to_sym
74
+ end
75
+
76
+ # @example
77
+ #
78
+ # :title
79
+ #
80
+ # @return [Symbol]
81
+ def source_attribute_key
82
+ @source_attribute_key ||= source_attribute_name.to_sym
83
+ end
84
+
85
+ # @example
86
+ #
87
+ # :user_comments
88
+ #
89
+ # @return [Symbol]
90
+ def source_table
91
+ @source_table ||= source_class_name.pluralize.underscore.to_sym
92
+ end
93
+
44
94
  # @return [Boolean]
45
95
  def primary_key?
46
96
  source_attribute_name == 'id'
47
97
  end
48
98
 
99
+ # @return [Boolean]
100
+ def primary_attribute?
101
+ provider_name == primary_provider_name && primary_source_class == source_class
102
+ end
103
+
104
+ # External attribute is from a different data provider than the primary data provider
105
+ #
106
+ # @return [Boolean]
107
+ def external_attribute?
108
+ provider_name != primary_provider_name
109
+ end
110
+
111
+ # @return [String]
112
+ def primary_provider_name
113
+ @primary_provider_name ||= primary_source_class.parent.to_s.demodulize
114
+ end
115
+
116
+ # Foreign key of the primary record, useful for joins
117
+ #
118
+ # @example
119
+ #
120
+ # :user_id
121
+ #
122
+ # @return [Symbol]
123
+ def primary_reference_key
124
+ @primary_reference_key ||= :"#{primary_source_class.to_s.demodulize.underscore}_id"
125
+ end
126
+
49
127
  private
50
128
 
51
129
  # @return [Array<String>]
@@ -10,6 +10,9 @@ module Datamappify
10
10
  # @return [String]
11
11
  attr_accessor :default_provider_name
12
12
 
13
+ # @return [String]
14
+ attr_writer :default_source_class_name
15
+
13
16
  # @return [Hash]
14
17
  # attribute name to source mapping as specified in {Repository::MappingDSL#map_attribute}
15
18
  attr_accessor :custom_mapping
@@ -24,6 +27,8 @@ module Datamappify
24
27
  @default_provider ||= Provider.const_get(default_provider_name)
25
28
  end
26
29
 
30
+ # @param provider_name [String]
31
+ #
27
32
  # @return [Module]
28
33
  def provider(provider_name)
29
34
  Provider.const_get(provider_name)
@@ -31,12 +36,12 @@ module Datamappify
31
36
 
32
37
  # @return [Class]
33
38
  def default_source_class
34
- @default_source_class ||= default_provider.find_or_build_record_class(entity_class.name)
39
+ @default_source_class ||= default_provider.find_or_build_record_class(default_source_class_name)
35
40
  end
36
41
 
37
42
  # @return [String]
38
43
  def default_source_class_name
39
- entity_class.name
44
+ @default_source_class_name ||= entity_class.name
40
45
  end
41
46
 
42
47
  # @return [Set<Attribute>]
@@ -50,11 +55,6 @@ module Datamappify
50
55
  @classified_attributes ||= Set.new(attributes).classify(&:provider_name)
51
56
  end
52
57
 
53
- # @return [Array<Attribute>]
54
- def attributes_from_default_source
55
- classified_attributes[default_provider_name].classify(&:source_class_name)[default_source_class_name]
56
- end
57
-
58
58
  private
59
59
 
60
60
  # @return [Array<Symbol>]
@@ -78,24 +78,24 @@ module Datamappify
78
78
  # @return [Array<Attribute>]
79
79
  def default_attributes
80
80
  @default_attributes ||= default_attribute_names.collect do |attribute|
81
- Attribute.new(attribute, default_source_for(attribute))
81
+ Attribute.new(attribute, default_source_for(attribute), default_source_class)
82
82
  end
83
83
  end
84
84
 
85
85
  # @return [Array<Attribute>]
86
86
  def custom_attributes
87
87
  @custom_attributes ||= custom_mapping.collect do |attribute, source|
88
- map_attribute(attribute, source)
88
+ map_custom_attribute(attribute, source)
89
89
  end
90
90
  end
91
91
 
92
92
  # @param (see Data::Mapper::Attribute#initialize)
93
93
  #
94
94
  # @return [Attribute]
95
- def map_attribute(name, source)
95
+ def map_custom_attribute(name, source)
96
96
  @custom_attribute_names << name
97
97
 
98
- Attribute.new(name, source)
98
+ Attribute.new(name, source, default_source_class)
99
99
  end
100
100
 
101
101
  # @param attribute [Symbol]
@@ -103,7 +103,7 @@ module Datamappify
103
103
  #
104
104
  # @return [String]
105
105
  def default_source_for(attribute)
106
- "#{default_provider_name}::#{entity_class.name}##{attribute}"
106
+ "#{default_provider_name}::#{default_source_class_name}##{attribute}"
107
107
  end
108
108
  end
109
109
  end
@@ -4,11 +4,26 @@ module Datamappify
4
4
  module ActiveRecord
5
5
  extend CommonProvider
6
6
 
7
- # @return [ActiveRecord::Base]
8
- def self.build_record_class(source_class_name)
9
- Datamappify::Data::Record::ActiveRecord.const_set(
10
- source_class_name, Class.new(::ActiveRecord::Base)
11
- )
7
+ class << self
8
+ # @param source_class_name (see CommonProvider::ModuleMethods#find_or_build_record_class)
9
+ #
10
+ # @return [ActiveRecord::Base]
11
+ def build_record_class(source_class_name)
12
+ Datamappify::Data::Record::ActiveRecord.const_set(
13
+ source_class_name, Class.new(::ActiveRecord::Base)
14
+ )
15
+ end
16
+
17
+ # @return [void]
18
+ def build_record_association(attribute, default_source_class)
19
+ default_source_class.class_eval <<-CODE, __FILE__, __LINE__ + 1
20
+ has_one :#{attribute.source_key}
21
+ CODE
22
+
23
+ attribute.source_class.class_eval <<-CODE, __FILE__, __LINE__ + 1
24
+ belongs_to :#{default_source_class.model_name.element}
25
+ CODE
26
+ end
12
27
  end
13
28
  end
14
29
  end
@@ -30,6 +30,8 @@ module Datamappify
30
30
 
31
31
  # Finds or builds a data record class from the data provider
32
32
  #
33
+ # @param source_class_name [String]
34
+ #
33
35
  # @return [Class]
34
36
  # the data record class
35
37
  def find_or_build_record_class(source_class_name)
@@ -4,12 +4,31 @@ module Datamappify
4
4
  module Sequel
5
5
  extend CommonProvider
6
6
 
7
- # @return [Sequel::Model]
8
- def self.build_record_class(source_class_name)
9
- Record::Sequel.const_set(
10
- source_class_name, Class.new(::Sequel::Model(source_class_name.pluralize.underscore.to_sym))
11
- ).tap do |klass|
12
- klass.raise_on_save_failure = true
7
+ class << self
8
+ # @param source_class_name (see CommonProvider::ModuleMethods#find_or_build_record_class)
9
+ #
10
+ # @return [Sequel::Model]
11
+ def build_record_class(source_class_name)
12
+ Record::Sequel.const_set(
13
+ source_class_name, Class.new(::Sequel::Model(source_class_name.pluralize.underscore.to_sym))
14
+ ).tap do |klass|
15
+ klass.raise_on_save_failure = true
16
+ end
17
+ end
18
+
19
+ # @param attribute (see Record#build_association)
20
+ #
21
+ # @param default_source_class (see Record#build_association)
22
+ #
23
+ # @return [void]
24
+ def build_record_association(attribute, default_source_class)
25
+ default_source_class.class_eval <<-CODE, __FILE__, __LINE__ + 1
26
+ one_to_one :#{attribute.source_key}
27
+ CODE
28
+
29
+ attribute.source_class.class_eval <<-CODE, __FILE__, __LINE__ + 1
30
+ one_to_one :#{default_source_class.table_name.to_s.singularize}
31
+ CODE
13
32
  end
14
33
  end
15
34
  end
@@ -2,11 +2,24 @@ module Datamappify
2
2
  module Data
3
3
  # A convenient class for finding or building a data record
4
4
  module Record
5
- # @param provider_name [String]
6
- #
7
- # @param source_class_name [String]
8
- def self.find_or_build(provider_name, source_class_name)
9
- Provider.const_get(provider_name).find_or_build_record_class(source_class_name)
5
+ class << self
6
+ # @param provider_name [String]
7
+ #
8
+ # @param source_class_name (see CommonProvider::ModuleMethods#find_or_build_record_class)
9
+ #
10
+ # @return [Object]
11
+ def find_or_build(provider_name, source_class_name)
12
+ Provider.const_get(provider_name).find_or_build_record_class(source_class_name)
13
+ end
14
+
15
+ # @param attribute [Attribute]
16
+ #
17
+ # @param default_source_class [Class]
18
+ #
19
+ # @return [void]
20
+ def build_association(attribute, default_source_class)
21
+ Provider.const_get(attribute.provider_name).build_record_association(attribute, default_source_class)
22
+ end
10
23
  end
11
24
  end
12
25
  end