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/README +157 -85
- data/Rakefile +38 -0
- data/TEST_README +45 -44
- data/lib/joinfix/error.rb +220 -0
- data/lib/joinfix/fixture.rb +3 -4
- data/lib/joinfix/fixtures.rb +107 -145
- data/lib/joinfix/fixtures_class.rb +51 -97
- data/lib/joinfix.rb +118 -84
- data/rails/app/models/group.rb +3 -3
- data/rails/db/migrate/004_create_users.rb +1 -1
- data/rails/db/schema.rb +1 -1
- data/rails/log/development.log +409 -57
- data/rails/log/test.log +350 -23
- data/rails/test/fixtures/groups.yml +2 -1
- data/rails/test/fixtures/users.yml +27 -19
- data/rails/test/unit/user_test.rb +25 -4
- data/test/has_many_test.rb +16 -0
- data/test/joinfix_test.rb +6 -69
- data/test/joinfix_test_suite.rb +1 -1
- metadata +9 -5
- data/test/todo +0 -15
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
|
8
|
-
# extends hashes that contain the entry data.
|
9
|
-
#
|
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
|
-
#
|
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
|
-
#
|
19
|
-
#
|
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
|
-
#
|
25
|
-
#
|
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
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
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
|
-
|
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 =>
|
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
|
-
|
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 =>
|
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
|
-
|
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 =>
|
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] =
|
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
|
-
|
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 =>
|
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
|
-
|
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
|
-
|
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
|
-
#
|
129
|
-
|
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
|
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
|
-
#
|
172
|
+
# [:foreign_key, :child_table]
|
139
173
|
#
|
140
174
|
# Required configurations for belongs_to :polymorphic
|
141
|
-
#
|
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
|
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
|
-
#
|
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
|
-
|
182
|
-
#
|
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
|
-
#
|
220
|
+
# [:parent_table, :foreign_key]
|
210
221
|
#
|
211
222
|
# Required configurations for has_many :as
|
212
|
-
#
|
223
|
+
# [:parent_table, :as (=association)]
|
213
224
|
#
|
214
225
|
# Required configurations for has_many :through
|
215
|
-
#
|
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
|
242
|
-
def validate_inclusion(config, *
|
243
|
-
|
244
|
-
raise ArgumentError, "Missing configuration '#{
|
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
|
|
data/rails/app/models/group.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
class Group < ActiveRecord::Base
|
2
|
-
|
3
|
-
has_many :users, :through => :
|
1
|
+
class Group < ActiveRecord::Base
|
2
|
+
has_many :user_groups
|
3
|
+
has_many :users, :through => :user_groups
|
4
4
|
end
|