joinfix 0.1.1 → 1.0.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.
- 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
|