joinfix 0.1.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/joinfix.rb CHANGED
@@ -3,26 +3,34 @@ require 'active_record/fixtures'
3
3
  require 'joinfix/fixtures_class'
4
4
  require 'joinfix/fixtures'
5
5
  require 'joinfix/fixture'
6
+ require 'joinfix/error'
6
7
 
7
- # The actual JoinFix module contains methods managing joins. In practice, JoinFix
8
- # extends hashes that contain the entry data. Calls to the join_(type) methods define
9
- # join references like ['child_id', 'child_table'] => 'child_entry_name'. This reference
10
- # specifies that 'child_id' should be set to the primary key of 'child_entry_name' in
11
- # 'child_table'.
8
+ # The JoinFix module contains methods to make joins. In practice, JoinFix
9
+ # extends hashes that contain the entry data. Joins are created by calling the
10
+ # join_(macro) methods, which creates join references like:
12
11
  #
13
- # The join methods each take a child entry and a config. The instance calling the join
14
- # method is considered the parent entry. References are added where appropriate.
15
- # A separate join entry will be returned if required, as for has_and_belongs_to_many
16
- # or has_many :through.
12
+ # ['child_id', 'child_table'] => 'child_entry_name'
17
13
  #
18
- # The configurations required by the join methods can be created from an ActiveRecord::Base
19
- # class and the name of the association through the +configure+ method.
14
+ # Later on these references are resolved so that 'child_id' will be set to
15
+ # the primary key of 'child_entry_name' in 'child_table'.
16
+ #
17
+ # The hash extended by the JoinFix module is considered the parent entry. Each
18
+ # join_(macro) method takes a child entry and a config specifiying details of the join.
19
+ # A separate join entry will be returned if required (as for 'has_and_belongs_to_many'
20
+ # or 'has_many :through'). The configurations required by the join methods can be
21
+ # created by providing an ActiveRecord::Base class and association name to
22
+ # +configure+.
23
+ #
24
+ # A little terminology:
25
+ # macro:: Refers to the join macro (ex :belongs_to, :has_many)
26
+ # associable:: The association name in a belongs_to :polymorphic association
27
+ # associable_type:: The field holding the class name of the associated entry (ie "#{association.name}_type") in a belongs_to :polymorphic association
20
28
  module JoinFix
21
29
  class << self
22
30
  # Returns true if the input macro allows for multiple joins.
23
31
  #
24
- # [:belongs_to, :has_one] => false
25
- # [:has_many, :has_and_belongs_to_many] => true
32
+ # [:belongs_to, :has_one] => false
33
+ # [:has_many, :has_and_belongs_to_many] => true
26
34
  def macro_allows_multiple(macro)
27
35
  case macro.to_sym
28
36
  when :belongs_to then false
@@ -32,23 +40,22 @@ module JoinFix
32
40
  end
33
41
  end
34
42
 
35
- def configure(klass, assoc_name)
36
- association = association(klass, assoc_name)
37
- send("configure_#{association.macro}", klass, association).with_indifferent_access
38
- end
39
-
40
- protected
41
-
42
- def association(klass, assoc_name)
43
- association = klass.reflect_on_association(assoc_name.to_sym)
44
- raise ArgumentError, "Unknown association '#{assoc_name}' for '#{klass}'." unless association
45
- association.check_validity!
46
- association
43
+ # Constructs a join configuration for the specified assocation.
44
+ def configure(active_record_class, assoc_name)
45
+ association = association(active_record_class, assoc_name)
46
+ send("configure_#{association.macro}", active_record_class, association).with_indifferent_access
47
47
  end
48
-
49
- def configure_belongs_to(klass, association)
48
+
49
+ # Returns a hash of configurations for a belongs_to association.
50
+ #
51
+ # Returns configurations:
52
+ # [:macro,:parent_table, :foreign_key, :child_table]
53
+ #
54
+ # Returned configurations for belongs_to :polymorphic
55
+ # [:macro, parent_table, :foreign_key, :polymorphic (=true), :associable, :associable_type]
56
+ def configure_belongs_to(active_record_class, association)
50
57
  join_config = {
51
- :parent_table => klass.table_name,
58
+ :parent_table => active_record_class.table_name,
52
59
  :macro => association.macro,
53
60
  :foreign_key => association.primary_key_name
54
61
  }
@@ -64,9 +71,13 @@ module JoinFix
64
71
  join_config
65
72
  end
66
73
 
67
- def configure_has_one(klass, association)
74
+ # Returns a hash of configurations for a has_one association
75
+ #
76
+ # Returns configurations:
77
+ # [:macro, :parent_table, :child_table, :foreign_key]
78
+ def configure_has_one(active_record_class, association)
68
79
  join_config = {
69
- :parent_table => klass.table_name,
80
+ :parent_table => active_record_class.table_name,
70
81
  :child_table => association.table_name,
71
82
  :macro => association.macro,
72
83
  :foreign_key => association.primary_key_name
@@ -75,9 +86,19 @@ module JoinFix
75
86
  join_config
76
87
  end
77
88
 
78
- def configure_has_many(klass, association)
89
+ # Returns a hash of configurations for a has_many association
90
+ #
91
+ # Returns configurations:
92
+ # [:macro, :parent_table, :child_table, :foreign_key]
93
+ #
94
+ # Returned configurations for has_many :as
95
+ # [:macro, :parent_table, :child_table, :foreign_key, :as, :parent_class]
96
+ #
97
+ # Returned configurations for has_many :through
98
+ # [:macro, :parent_table, :child_table, :through, :join_table, :foreign_key, :association_foreign_key]
99
+ def configure_has_many(active_record_class, association)
79
100
  join_config = {
80
- :parent_table => klass.table_name,
101
+ :parent_table => active_record_class.table_name,
81
102
  :child_table => association.table_name,
82
103
  :macro => association.macro,
83
104
  :foreign_key => association.primary_key_name
@@ -85,7 +106,7 @@ module JoinFix
85
106
 
86
107
  if association.options[:as]
87
108
  join_config[:as] = association.options[:as]
88
- join_config[:parent_class] = klass.class_name
109
+ join_config[:parent_class] = active_record_class.class_name
89
110
  elsif association.options[:through]
90
111
  join_config[:through] = association.options[:through]
91
112
  join_config[:join_table] = association.through_reflection.table_name
@@ -96,9 +117,13 @@ module JoinFix
96
117
  join_config
97
118
  end
98
119
 
99
- def configure_has_and_belongs_to_many(klass, association)
120
+ # Returns a hash of configurations for a has_and_belongs_to_many association
121
+ #
122
+ # Returns configurations:
123
+ # [:macro, :parent_table, :child_table, :join_table, :foreign_key, :association_foreign_key]
124
+ def configure_has_and_belongs_to_many(active_record_class, association)
100
125
  join_config = {
101
- :parent_table => klass.table_name,
126
+ :parent_table => active_record_class.table_name,
102
127
  :child_table => association.table_name,
103
128
  :macro => association.macro,
104
129
  :join_table => association.options[:join_table],
@@ -108,37 +133,46 @@ module JoinFix
108
133
 
109
134
  join_config
110
135
  end
136
+
137
+ protected
138
+
139
+ # Looks up the association and checks that the association is valid.
140
+ def association(active_record_class, assoc_name) # :nodoc:
141
+ association = active_record_class.reflect_on_association(assoc_name.to_sym)
142
+ raise ArgumentError, "Unknown association '#{assoc_name}' for '#{active_record_class}'." unless association
143
+ association.check_validity!
144
+ association
145
+ end
111
146
  end
112
147
 
113
- # Convenience method for setting up a JoinFix.
148
+ attr_accessor :entry_name
149
+
150
+ # Convenience method for setting up a JoinFix. Simply takes the input hash extends with
151
+ # JoinFix and sets the entry_name.
114
152
  def self.new(entry_name, hash={})
115
153
  hash.extend JoinFix
116
154
  hash.entry_name = entry_name
117
155
  hash
118
156
  end
119
157
 
120
- attr_accessor :entry_name
121
-
122
- # extract_if extracts values where the key is specified
123
- # or returns true when passed to the optional block.
124
- def extract_if(keys=[], &block)
158
+ # Extracts values if the key is specified in keys or if the block returns true.
159
+ def extract_if(keys=[], &block) # :yields: key, value
125
160
  extract(:if, keys, block)
126
161
  end
127
162
 
128
- # extract_unless extracts values where the key is not specified
129
- # or does not return true when passed to the optional block.
130
- def extract_unless(keys=[], &block)
163
+ # Extracts values unless the key is specified in keys or if the block returns false.
164
+ def extract_unless(keys=[], &block) # :yields: key, value
131
165
  extract(:unless, keys, block)
132
166
  end
133
167
 
134
- # Creates the required key-value pairs for a 'belongs_to' association between the fixture
135
- # and the input entry. Returns nil
168
+ # Creates the required key-value pairs for a 'belongs_to' association between self
169
+ # and the input entry. Returns nil.
136
170
  #
137
171
  # Required configurations:
138
- # * [:foreign_key, :child_table]
172
+ # [:foreign_key, :child_table]
139
173
  #
140
174
  # Required configurations for belongs_to :polymorphic
141
- # * [:polymorphic (=true), :associable, :foreign_key, :child_table, :child_class]
175
+ # [:associable, :child_table, :child_class, :polymorphic (=true), :foreign_key]
142
176
  def join_belongs_to(entry, config)
143
177
  if config[:polymorphic]
144
178
  # produce entries like:
@@ -162,11 +196,11 @@ module JoinFix
162
196
  nil
163
197
  end
164
198
 
165
- # Creates the required key-value pairs for a 'has_one' association between the fixture
166
- # and the input entry. Returns nil
199
+ # Creates the required key-value pairs for a 'has_one' association between self
200
+ # and the input entry. Returns nil.
167
201
  #
168
202
  # Required configurations:
169
- # * [:foreign_key, :parent_table]
203
+ # [:parent_table, :foreign_key]
170
204
  def join_has_one(entry, config)
171
205
  # produces entries like:
172
206
  # entry[foreign_key_ref_parent_table] = self.entry_name
@@ -178,41 +212,18 @@ module JoinFix
178
212
  nil
179
213
  end
180
214
 
181
- # Creates the required key-value pairs for a 'has_and_belongs_to_many' association between
182
- # the fixture and the input entry. Returns a join entry.
183
- #
184
- # Required configurations:
185
- # * [:foreign_key, :association_foreign_key, :parent_table, :child_table]
186
- def join_has_and_belongs_to_many(entry, config)
187
- # produces entries like:
188
- # join[foreign_key_ref_parent_table] = self.entry_name
189
- # join[association_foreign_key_ref_child_table] = entry.entry_name
190
- # later resolved to:
191
- # join[foreign_key_id] = self.id
192
- # join[association_foreign_key_id] = entry.id
193
- validate_inclusion(config, :foreign_key, :association_foreign_key, :parent_table, :child_table)
194
-
195
- join_entry_name = self.entry_name < entry.entry_name ?
196
- "#{self.entry_name}_#{entry.entry_name}" :
197
- "#{entry.entry_name}_#{self.entry_name}"
198
-
199
- JoinFix.new join_entry_name,
200
- [config[:foreign_key], config[:parent_table]] => self.entry_name,
201
- [config[:association_foreign_key], config[:child_table]] => entry.entry_name
202
- end
203
-
204
- # Creates the required key-value pairs for a 'has_many' association between
205
- # the fixture and the input entry. Returns a join entry if configuration :through is
215
+ # Creates the required key-value pairs for a 'has_many' association between
216
+ # self and the input entry. Returns a join entry if configuration :through is
206
217
  # specified, otherwise returns nil.
207
218
  #
208
219
  # Required configurations:
209
- # * [:foreign_key, :parent_table]
220
+ # [:parent_table, :foreign_key]
210
221
  #
211
222
  # Required configurations for has_many :as
212
- # * [:as (=association), :parent_table]
223
+ # [:parent_table, :as (=association)]
213
224
  #
214
225
  # Required configurations for has_many :through
215
- # * [:through (=join table), :foreign_key, :association_foreign_key, :parent_table, :child_table]
226
+ # [:parent_table, :child_table, :through (=join table), :foreign_key, :association_foreign_key]
216
227
  def join_has_many(entry, config)
217
228
  if config[:as]
218
229
  # produces entries like:
@@ -235,18 +246,41 @@ module JoinFix
235
246
  join_has_one(entry, config)
236
247
  end
237
248
  end
249
+
250
+ # Creates the required key-value pairs for a 'has_and_belongs_to_many' association between
251
+ # self and the input entry. Returns a join entry.
252
+ #
253
+ # Required configurations:
254
+ # [:parent_table, :child_table, :foreign_key, :association_foreign_key]
255
+ def join_has_and_belongs_to_many(entry, config)
256
+ # produces entries like:
257
+ # join[foreign_key_ref_parent_table] = self.entry_name
258
+ # join[association_foreign_key_ref_child_table] = entry.entry_name
259
+ # later resolved to:
260
+ # join[foreign_key_id] = self.id
261
+ # join[association_foreign_key_id] = entry.id
262
+ validate_inclusion(config, :foreign_key, :association_foreign_key, :parent_table, :child_table)
263
+
264
+ join_entry_name = self.entry_name < entry.entry_name ?
265
+ "#{self.entry_name}_#{entry.entry_name}" :
266
+ "#{entry.entry_name}_#{self.entry_name}"
267
+
268
+ JoinFix.new join_entry_name,
269
+ [config[:foreign_key], config[:parent_table]] => self.entry_name,
270
+ [config[:association_foreign_key], config[:child_table]] => entry.entry_name
271
+ end
238
272
 
239
273
  protected
240
274
 
241
- # Checks each input type is present in the config; raises an error when it is not.
242
- def validate_inclusion(config, *types)
243
- types.each do |type|
244
- raise ArgumentError, "Missing configuration '#{type}'" unless config.has_key?(type)
275
+ # Checks each key is present in the config; raises an error when it is not.
276
+ def validate_inclusion(config, *keys) # :nodoc:
277
+ keys.each do |key|
278
+ raise ArgumentError, "Missing configuration '#{key}'" unless config.has_key?(key)
245
279
  end
246
280
  end
247
281
 
248
282
  # The generalized extraction method called by extract_if and extract_unless
249
- def extract(method, keys, block)
283
+ def extract(method, keys, block) # :nodoc:
250
284
  # be sure that allowed keys is an array, to respond to include?
251
285
  keys = [keys] unless keys.kind_of?(Array)
252
286
 
@@ -1,4 +1,4 @@
1
- class Group < ActiveRecord::Base
2
- has_many :group_users, :class_name => 'UserGroup'
3
- has_many :users, :through => :group_users
1
+ class Group < ActiveRecord::Base
2
+ has_many :user_groups
3
+ has_many :users, :through => :user_groups
4
4
  end
@@ -1,7 +1,7 @@
1
1
  class CreateUsers < ActiveRecord::Migration
2
2
  def self.up
3
3
  create_table :users do |t|
4
- t.column :login, :string
4
+ t.column :full_name, :string
5
5
  end
6
6
  end
7
7
 
data/rails/db/schema.rb CHANGED
@@ -29,7 +29,7 @@ ActiveRecord::Schema.define(:version => 6) do
29
29
  end
30
30
 
31
31
  create_table "users", :force => true do |t|
32
- t.column "login", :string
32
+ t.column "full_name", :string
33
33
  end
34
34
 
35
35
  end