hobo 0.6.1 → 0.6.2
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/hobo +3 -2
- data/hobo_files/plugin/CHANGES.txt +299 -2
- data/hobo_files/plugin/Rakefile +12 -10
- data/hobo_files/plugin/generators/hobo/templates/guest.rb +1 -13
- data/hobo_files/plugin/generators/hobo_migration/hobo_migration_generator.rb +11 -7
- data/hobo_files/plugin/generators/hobo_rapid/hobo_rapid_generator.rb +1 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/hobo_rapid.js +1 -1
- data/hobo_files/plugin/generators/hobo_rapid/templates/lowpro.js +405 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/views/application.dryml +1 -1
- data/hobo_files/plugin/generators/hobo_user_model/templates/model.rb +1 -9
- data/hobo_files/plugin/init.rb +5 -0
- data/hobo_files/plugin/lib/active_record/has_many_association.rb +1 -1
- data/hobo_files/plugin/lib/extensions.rb +26 -5
- data/hobo_files/plugin/lib/extensions/test_case.rb +1 -1
- data/hobo_files/plugin/lib/hobo.rb +37 -11
- data/hobo_files/plugin/lib/hobo/authenticated_user.rb +7 -2
- data/hobo_files/plugin/lib/hobo/authentication_support.rb +7 -6
- data/hobo_files/plugin/lib/hobo/composite_model.rb +5 -0
- data/hobo_files/plugin/lib/hobo/controller.rb +4 -4
- data/hobo_files/plugin/lib/hobo/dryml.rb +5 -5
- data/hobo_files/plugin/lib/hobo/dryml/part_context.rb +3 -6
- data/hobo_files/plugin/lib/hobo/dryml/template.rb +16 -15
- data/hobo_files/plugin/lib/hobo/dryml/template_environment.rb +24 -20
- data/hobo_files/plugin/lib/hobo/email_address.rb +4 -0
- data/hobo_files/plugin/lib/hobo/field_spec.rb +2 -1
- data/hobo_files/plugin/lib/hobo/guest.rb +21 -0
- data/hobo_files/plugin/lib/hobo/hobo_helper.rb +42 -2
- data/hobo_files/plugin/lib/hobo/http_parameters.rb +225 -0
- data/hobo_files/plugin/lib/hobo/model.rb +55 -37
- data/hobo_files/plugin/lib/hobo/model_controller.rb +151 -151
- data/hobo_files/plugin/lib/hobo/model_queries.rb +30 -5
- data/hobo_files/plugin/lib/hobo/user_controller.rb +27 -16
- data/hobo_files/plugin/lib/hobo/where_fragment.rb +6 -1
- data/hobo_files/plugin/tags/rapid.dryml +88 -58
- data/hobo_files/plugin/tags/rapid_document_tags.dryml +5 -5
- data/hobo_files/plugin/tags/rapid_editing.dryml +3 -3
- data/hobo_files/plugin/tags/rapid_forms.dryml +35 -26
- data/hobo_files/plugin/tags/rapid_navigation.dryml +13 -12
- data/hobo_files/plugin/tags/rapid_pages.dryml +35 -31
- data/hobo_files/plugin/tags/rapid_plus.dryml +41 -0
- data/hobo_files/plugin/tags/rapid_support.dryml +18 -9
- data/hobo_files/plugin/tasks/dump_fixtures.rake +61 -0
- metadata +7 -11
- data/hobo_files/plugin/spec/fixtures/users.yml +0 -9
- data/hobo_files/plugin/spec/spec.opts +0 -6
- data/hobo_files/plugin/spec/spec_helper.rb +0 -28
- data/hobo_files/plugin/spec/unit/hobo/dryml/template_spec.rb +0 -650
@@ -194,11 +194,13 @@ module Hobo::Dryml
|
|
194
194
|
def new_object_context(new_this)
|
195
195
|
new_context do
|
196
196
|
@_this_parent,@_this_field,@_this_type = if new_this.respond_to?(:proxy_reflection)
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
197
|
+
refl = new_this.proxy_reflection
|
198
|
+
[new_this.proxy_owner, refl.name, refl]
|
199
|
+
else
|
200
|
+
# In dryml, TrueClass is the 'boolean' class
|
201
|
+
t = new_this.class == FalseClass ? TrueClass : new_this.class
|
202
|
+
[nil, nil, t]
|
203
|
+
end
|
202
204
|
@_this = new_this
|
203
205
|
yield
|
204
206
|
end
|
@@ -207,27 +209,25 @@ module Hobo::Dryml
|
|
207
209
|
|
208
210
|
def new_field_context(field_path, tag_this=nil)
|
209
211
|
new_context do
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
obj = tag_this || this
|
219
|
-
for field in path
|
220
|
-
parent = obj
|
221
|
-
obj = Hobo.get_field(parent, field)
|
222
|
-
end
|
212
|
+
path = if field_path.is_a? Array
|
213
|
+
field_path
|
214
|
+
elsif field_path.is_a? String
|
215
|
+
field_path.split('.')
|
216
|
+
else
|
217
|
+
[field_path]
|
218
|
+
end
|
219
|
+
parent, field, obj = Hobo.get_field_path(tag_this || this, path)
|
223
220
|
|
224
221
|
type = if (obj.nil? or obj.respond_to?(:proxy_reflection)) and
|
225
222
|
parent.class.respond_to?(:field_type) and field_type = parent.class.field_type(field)
|
226
223
|
field_type
|
224
|
+
elsif obj == false
|
225
|
+
# In dryml, TrueClass is the 'boolean' class
|
226
|
+
TrueClass
|
227
227
|
else
|
228
228
|
obj.class
|
229
229
|
end
|
230
|
-
|
230
|
+
|
231
231
|
@_this, @_this_parent, @_this_field, @_this_type = obj, parent, field, type
|
232
232
|
@_form_field_path += path if @_form_field_path
|
233
233
|
yield
|
@@ -409,7 +409,11 @@ module Hobo::Dryml
|
|
409
409
|
|
410
410
|
|
411
411
|
def render_tag(tag_name, attributes)
|
412
|
-
(
|
412
|
+
if respond_to?(tag_name)
|
413
|
+
(send(tag_name, attributes) + part_contexts_storage_tag).strip
|
414
|
+
else
|
415
|
+
false
|
416
|
+
end
|
413
417
|
end
|
414
418
|
|
415
419
|
|
@@ -23,7 +23,8 @@ module Hobo
|
|
23
23
|
elsif options[:length]
|
24
24
|
:string
|
25
25
|
else
|
26
|
-
Hobo.field_types[type]
|
26
|
+
field_type = Hobo.field_types[type]
|
27
|
+
field_type && field_type::COLUMN_TYPE or raise UnknownSqlTypeError, [model, name, type]
|
27
28
|
end
|
28
29
|
end
|
29
30
|
end
|
@@ -15,13 +15,13 @@ module Hobo
|
|
15
15
|
@current_user = if session and id = session[:user]
|
16
16
|
Hobo.object_from_dom_id(id)
|
17
17
|
else
|
18
|
-
Guest.new
|
18
|
+
::Guest.new
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
22
22
|
|
23
23
|
def logged_in?
|
24
|
-
!
|
24
|
+
!current_user.guest?
|
25
25
|
end
|
26
26
|
|
27
27
|
|
@@ -307,6 +307,46 @@ module Hobo
|
|
307
307
|
end
|
308
308
|
|
309
309
|
|
310
|
+
# Login url for a given user record or user class
|
311
|
+
def login_url(user_or_class)
|
312
|
+
c = user_or_class.is_a?(Class) ? user_or_class : user_or_class.class
|
313
|
+
send("#{c.name.underscore}_login_url") rescue nil
|
314
|
+
end
|
315
|
+
|
316
|
+
|
317
|
+
# Login url for a given user record or user class
|
318
|
+
def logout_url(user_or_class=nil)
|
319
|
+
c = if user_or_class.nil?
|
320
|
+
current_user.class
|
321
|
+
elsif user_or_class.is_a?(Class)
|
322
|
+
user_or_class
|
323
|
+
else
|
324
|
+
user_or_class.class
|
325
|
+
end
|
326
|
+
send("#{c.name.underscore}_logout_url") rescue nil
|
327
|
+
end
|
328
|
+
|
329
|
+
|
330
|
+
# Sign-up url for a given user record or user class
|
331
|
+
def signup_url(user_or_class)
|
332
|
+
c = user_or_class.is_a?(Class) ? user_or_class : user_or_class.class
|
333
|
+
send("#{c.name.underscore}_signup_url") rescue nil
|
334
|
+
end
|
335
|
+
|
336
|
+
def query_params
|
337
|
+
query = request.request_uri.match(/(?:\?(.+))/)._?[1]
|
338
|
+
if query
|
339
|
+
params = query.split('&')
|
340
|
+
pairs = params.map do |param|
|
341
|
+
pair = param.split('=')
|
342
|
+
pair.length == 1 ? pair + [''] : pair
|
343
|
+
end
|
344
|
+
Hash[*pairs.flatten]
|
345
|
+
else
|
346
|
+
{}
|
347
|
+
end
|
348
|
+
end
|
349
|
+
|
310
350
|
# debugging support
|
311
351
|
|
312
352
|
def abort_with(*args)
|
@@ -0,0 +1,225 @@
|
|
1
|
+
module Hobo
|
2
|
+
|
3
|
+
module HttpParameters
|
4
|
+
|
5
|
+
class PermissionDeniedError < RuntimeError; end
|
6
|
+
class InvalidError < RuntimeError; end
|
7
|
+
|
8
|
+
def initialize_record(record, params)
|
9
|
+
update_without_tracking(record, params)
|
10
|
+
record.set_creator(current_user)
|
11
|
+
(@to_create ||= []) << record
|
12
|
+
record
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
def update_record(record, params)
|
17
|
+
return if params.blank?
|
18
|
+
|
19
|
+
original = record.duplicate
|
20
|
+
# 'duplicate' can set these, but they can
|
21
|
+
# conflict with the changes so we clear them
|
22
|
+
@this.send(:clear_aggregation_cache)
|
23
|
+
@this.send(:clear_association_cache)
|
24
|
+
|
25
|
+
(@to_update ||= []) << [original, record]
|
26
|
+
|
27
|
+
update_without_tracking(record, params)
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
def update_without_tracking(record, params)
|
32
|
+
params && params.each_pair do |field_name, value|
|
33
|
+
field = if (create = field_name =~ /^\+/)
|
34
|
+
field_name[1..-1].to_sym
|
35
|
+
else
|
36
|
+
field_name.to_sym
|
37
|
+
end
|
38
|
+
refl = record.class.reflections[field]
|
39
|
+
|
40
|
+
if refl._?.macro == :belongs_to
|
41
|
+
if create
|
42
|
+
new_for_belongs_to(record, refl, value)
|
43
|
+
else
|
44
|
+
update_belongs_to(record, refl, value)
|
45
|
+
end
|
46
|
+
|
47
|
+
elsif Hobo.simple_has_many_association?(refl)
|
48
|
+
raise HoboError, "invalid HTTP parameter #{field_name}" if create
|
49
|
+
update_has_many(record, refl, value)
|
50
|
+
|
51
|
+
else
|
52
|
+
raise HoboError, "invalid HTTP parameter #{field_name}" if create
|
53
|
+
update_primitive(record, field, value)
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
def new_for_belongs_to(record, refl, fields)
|
61
|
+
# person[+home][address]=blah Create new home and set address (PUT POST)
|
62
|
+
|
63
|
+
target = refl.klass.new
|
64
|
+
initialize_record(target, fields)
|
65
|
+
record.send("#{refl.name}=", target)
|
66
|
+
end
|
67
|
+
|
68
|
+
|
69
|
+
def update_belongs_to(record, refl, value)
|
70
|
+
if value.is_a? String
|
71
|
+
# Update belongs_to to reference some existing record
|
72
|
+
|
73
|
+
target = if value.starts_with?('@')
|
74
|
+
# person[home]=@home_12 Reference different existing home (PUT POST)
|
75
|
+
|
76
|
+
Hobo.object_from_dom_id(value[1..-1])
|
77
|
+
elsif refl.klass.id_name?
|
78
|
+
# product[category]=garden Reference existing category with id or name (PUT POST)
|
79
|
+
|
80
|
+
refl.klass.find_by_id_name(value)
|
81
|
+
else
|
82
|
+
raise HoboError, "invalid HTTP parameter" if create
|
83
|
+
end
|
84
|
+
record.send("#{refl.name}=", target)
|
85
|
+
|
86
|
+
else
|
87
|
+
# Update state of current belongs_to target
|
88
|
+
# person[home][address]=blah Update existing home.address (PUT)
|
89
|
+
raise HoboError, "invalid HTTP parameter" unless params[:action] == "update"
|
90
|
+
|
91
|
+
target = record.send(refl.name)
|
92
|
+
raise HoboError, "invalid HTTP parameter" if target.nil?
|
93
|
+
update_record(target, value)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
|
98
|
+
def update_has_many(record, refl, items)
|
99
|
+
new_items, changed_items = items.partition_hash {|k,v| k =~ /^\+/}
|
100
|
+
|
101
|
+
new_items.keys.sort_by{|k|k.to_i}.each do |k|
|
102
|
+
# home[people][+1][name]=blah Create new Person with fkey refing to this home and set name (PUT POST)
|
103
|
+
fields = new_items[k]
|
104
|
+
new_for_has_many(record, refl, fields)
|
105
|
+
end
|
106
|
+
|
107
|
+
changed_items.each_pair do |id, value|
|
108
|
+
# Change to existing record - only valid on PUTs
|
109
|
+
raise HoboError, "invalid HTTP parameter" unless params[:action] == "update"
|
110
|
+
|
111
|
+
target = id =~ /_/ ? Hobo.object_from_dom_id(id) : refl.klass.find(id)
|
112
|
+
# Ensure the target is actually in this has_many
|
113
|
+
raise HoboError, "invalid http parameter" unless target.send(refl.primary_key_name) == record.id
|
114
|
+
|
115
|
+
if value.is_a?(String) && value.downcase == "delete"
|
116
|
+
# home[people][45]=delete Delete Person[45] (PUT)
|
117
|
+
delete_record(target)
|
118
|
+
|
119
|
+
else
|
120
|
+
# home[people][45][name]=blah Update Person[45].name (PUT)
|
121
|
+
raise HoboError, "invalid http parameter" unless value.is_a?(Hash) # field/value pairs
|
122
|
+
update_record(target, value)
|
123
|
+
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
|
129
|
+
def new_for_has_many(record, refl, value)
|
130
|
+
# home[people][+1][name]=blah Create new Person with fkey refing to this home and set name (PUT POST)
|
131
|
+
|
132
|
+
new_record = record.send(refl.name).new
|
133
|
+
initialize_record(new_record, value)
|
134
|
+
record.send("#{refl.name}").target << new_record
|
135
|
+
end
|
136
|
+
|
137
|
+
|
138
|
+
def delete_record(record)
|
139
|
+
raise HoboError, "invalid HTTP parameter" unless params[:action] == "update"
|
140
|
+
(@to_delete ||= []) << record
|
141
|
+
end
|
142
|
+
|
143
|
+
|
144
|
+
def update_primitive(record, field, value)
|
145
|
+
# person[name]=fred (POST PUT)
|
146
|
+
field_type = record.class.field_type(field)
|
147
|
+
record.send("#{field}=", param_to_value(field_type, value))
|
148
|
+
end
|
149
|
+
|
150
|
+
|
151
|
+
def parse_datetime(s)
|
152
|
+
defined?(Chronic) ? Chronic.parse(s) : Time.parse(s)
|
153
|
+
end
|
154
|
+
|
155
|
+
|
156
|
+
def param_to_value(field_type, value)
|
157
|
+
if field_type.nil?
|
158
|
+
value
|
159
|
+
elsif field_type <= Date
|
160
|
+
if value.is_a? Hash
|
161
|
+
Date.new(*(%w{year month day}.map{|s| value[s].to_i}))
|
162
|
+
elsif value.is_a? String
|
163
|
+
dt = parse_datetime(value)
|
164
|
+
dt && dt.to_date
|
165
|
+
end
|
166
|
+
elsif field_type <= Time
|
167
|
+
if value.is_a? Hash
|
168
|
+
Time.local(*(%w{year month day hour minute}.map{|s| value[s].to_i}))
|
169
|
+
elsif value.is_a? String
|
170
|
+
parse_datetime(value)
|
171
|
+
end
|
172
|
+
elsif field_type <= TrueClass
|
173
|
+
(value.is_a?(String) && value.strip.downcase.in?(['0', 'false']) || value.blank?) ? false : true
|
174
|
+
else
|
175
|
+
# primitive field
|
176
|
+
value
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
|
181
|
+
def check_permissions_and_apply_changes
|
182
|
+
valid = true
|
183
|
+
for old, new in @to_update
|
184
|
+
raise PermissionDeniedError unless Hobo.can_update?(current_user, old, new)
|
185
|
+
new_valid = new.save
|
186
|
+
valid &&= new_valid
|
187
|
+
end if @to_update
|
188
|
+
|
189
|
+
for record in @to_create
|
190
|
+
raise PermissionDeniedError unless Hobo.can_create?(current_user, record)
|
191
|
+
# check if it's new because it might have already been saved as a result of the updates
|
192
|
+
record_valid = record.save if record.new_record?
|
193
|
+
valid &&= record_valid
|
194
|
+
end if @to_create
|
195
|
+
|
196
|
+
for record in @to_delete
|
197
|
+
raise PermissionDeniedError unless Hobo.can_delete?(current_user, record)
|
198
|
+
record.destroy
|
199
|
+
end if @to_delete
|
200
|
+
|
201
|
+
valid
|
202
|
+
ensure
|
203
|
+
@to_update = @to_create = @to_delete = nil
|
204
|
+
end
|
205
|
+
|
206
|
+
|
207
|
+
def secure_change_transaction
|
208
|
+
valid = nil
|
209
|
+
begin
|
210
|
+
ActiveRecord::Base.transaction do
|
211
|
+
yield
|
212
|
+
valid = check_permissions_and_apply_changes
|
213
|
+
raise InvalidError unless valid
|
214
|
+
end
|
215
|
+
rescue PermissionDeniedError
|
216
|
+
return :not_allowed
|
217
|
+
rescue InvalidError
|
218
|
+
return :invalid
|
219
|
+
end
|
220
|
+
:valid
|
221
|
+
end
|
222
|
+
|
223
|
+
end
|
224
|
+
|
225
|
+
end
|
@@ -28,6 +28,8 @@ module Hobo
|
|
28
28
|
alias_method_chain :has_many, :defined_scopes
|
29
29
|
alias_method_chain :belongs_to, :foreign_key_declaration
|
30
30
|
end
|
31
|
+
# respond_to? is slow on AR objects, use this instead where possible
|
32
|
+
base.send(:alias_method, :has_hobo_method?, :respond_to_without_attributes?)
|
31
33
|
end
|
32
34
|
|
33
35
|
module ClassMethods
|
@@ -64,10 +66,13 @@ module Hobo
|
|
64
66
|
|
65
67
|
def field(name, *args)
|
66
68
|
type = args.shift
|
67
|
-
options =
|
69
|
+
options = args.extract_options!
|
68
70
|
@model.send(:set_field_type, name => type) unless
|
69
71
|
type.in?(@model.connection.native_database_types.keys - [:text])
|
70
72
|
@model.field_specs[name] = FieldSpec.new(@model, name, type, options)
|
73
|
+
|
74
|
+
@model.send(:validates_presence_of, name) if :required.in?(args)
|
75
|
+
@model.send(:validates_uniqueness_of, name) if :unique.in?(args)
|
71
76
|
end
|
72
77
|
|
73
78
|
def method_missing(name, *args)
|
@@ -102,6 +107,13 @@ module Hobo
|
|
102
107
|
types.each_pair do |field, type|
|
103
108
|
type_class = Hobo.field_types[type] || type
|
104
109
|
field_types[field] = type_class
|
110
|
+
|
111
|
+
if "validate".in?(type_class.instance_methods)
|
112
|
+
self.validate do |record|
|
113
|
+
v = record.send(field).validate
|
114
|
+
record.errors.add(field, v) if v.is_a?(String)
|
115
|
+
end
|
116
|
+
end
|
105
117
|
end
|
106
118
|
end
|
107
119
|
|
@@ -129,15 +141,10 @@ module Hobo
|
|
129
141
|
public :never_show?
|
130
142
|
|
131
143
|
def set_creator_attr(attr)
|
132
|
-
|
133
|
-
def creator
|
134
|
-
#{attr};
|
135
|
-
end
|
136
|
-
def creator=(x)
|
137
|
-
self.#{attr} = x;
|
138
|
-
end
|
139
|
-
}
|
144
|
+
@creator_attr = attr.to_sym
|
140
145
|
end
|
146
|
+
attr_reader :creator_attr
|
147
|
+
public :creator_attr
|
141
148
|
|
142
149
|
def set_search_columns(*columns)
|
143
150
|
class_eval %{
|
@@ -225,27 +232,34 @@ module Hobo
|
|
225
232
|
end
|
226
233
|
|
227
234
|
|
228
|
-
def conditions(&b)
|
229
|
-
|
235
|
+
def conditions(*args, &b)
|
236
|
+
if args.empty?
|
237
|
+
ModelQueries.new(self).instance_eval(&b).to_sql
|
238
|
+
else
|
239
|
+
ModelQueries.new(self).instance_exec(*args, &b).to_sql
|
240
|
+
end
|
230
241
|
end
|
231
|
-
|
242
|
+
|
232
243
|
|
233
244
|
def find(*args, &b)
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
245
|
+
options = args.extract_options!
|
246
|
+
if args.first.in?([:all, :first]) && options[:order] == :default
|
247
|
+
options = if default_order.blank?
|
248
|
+
options - [:order]
|
249
|
+
else
|
250
|
+
options.merge(:order => "#{table_name}.#{default_order}")
|
251
|
+
end
|
252
|
+
end
|
241
253
|
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
254
|
+
if b && !(block_conditions = conditions(&b)).blank?
|
255
|
+
c = if !options[:conditions].blank?
|
256
|
+
"(#{options[:conditons]}) and (#{block_conditions})"
|
257
|
+
else
|
258
|
+
block_conditions
|
259
|
+
end
|
260
|
+
super(args.first, options.merge(:conditions => c))
|
247
261
|
else
|
248
|
-
super(*args)
|
262
|
+
super(*args + [options])
|
249
263
|
end
|
250
264
|
end
|
251
265
|
|
@@ -271,8 +285,8 @@ module Hobo
|
|
271
285
|
end
|
272
286
|
end
|
273
287
|
|
274
|
-
def
|
275
|
-
|
288
|
+
def creator_type
|
289
|
+
reflections[@creator_attr]._?.klass
|
276
290
|
end
|
277
291
|
|
278
292
|
def search_columns
|
@@ -366,12 +380,13 @@ module Hobo
|
|
366
380
|
if find_scope
|
367
381
|
# Calling instance_variable_get directly causes self to
|
368
382
|
# get loaded, hence this trick
|
369
|
-
assoc = Kernel.instance_method(:instance_variable_get).bind(self).call("@#{name}")
|
383
|
+
assoc = Kernel.instance_method(:instance_variable_get).bind(self).call("@#{name}_scope")
|
370
384
|
|
371
385
|
unless assoc
|
372
386
|
options = proxy_reflection.options
|
373
387
|
has_many_conditions = options.has_key?(:conditions)
|
374
|
-
|
388
|
+
source = proxy_reflection.source_reflection
|
389
|
+
scope_conditions = find_scope[:conditions]
|
375
390
|
conditions = if has_many_conditions && scope_conditions
|
376
391
|
"(#{scope_conditions}) AND (#{has_many_conditions})"
|
377
392
|
else
|
@@ -379,23 +394,26 @@ module Hobo
|
|
379
394
|
end
|
380
395
|
|
381
396
|
options = options.merge(find_scope).update(:conditions => conditions,
|
382
|
-
|
383
|
-
|
397
|
+
:class_name => proxy_reflection.klass.name,
|
398
|
+
:foreign_key => proxy_reflection.primary_key_name)
|
399
|
+
options[:source] = source.name if source
|
400
|
+
|
384
401
|
r = ActiveRecord::Reflection::AssociationReflection.new(:has_many,
|
385
402
|
name,
|
386
403
|
options,
|
387
|
-
|
404
|
+
proxy_owner.class)
|
405
|
+
|
388
406
|
@reflections ||= {}
|
389
407
|
@reflections[name] = r
|
390
408
|
|
391
|
-
assoc = if
|
409
|
+
assoc = if source
|
392
410
|
ActiveRecord::Associations::HasManyThroughAssociation
|
393
411
|
else
|
394
412
|
ActiveRecord::Associations::HasManyAssociation
|
395
413
|
end.new(self.proxy_owner, r)
|
396
|
-
|
414
|
+
|
397
415
|
# Calling directly causes self to get loaded
|
398
|
-
Kernel.instance_method(:instance_variable_set).bind(self).call("@#{name}", assoc)
|
416
|
+
Kernel.instance_method(:instance_variable_set).bind(self).call("@#{name}_scope", assoc)
|
399
417
|
end
|
400
418
|
assoc
|
401
419
|
else
|
@@ -407,7 +425,7 @@ module Hobo
|
|
407
425
|
|
408
426
|
|
409
427
|
def has_many_with_defined_scopes(name, *args, &block)
|
410
|
-
options =
|
428
|
+
options = args.extract_options!
|
411
429
|
if options.has_key?(:extend) || block
|
412
430
|
# Normal has_many
|
413
431
|
has_many_without_defined_scopes(name, *args + [options], &block)
|
@@ -420,7 +438,7 @@ module Hobo
|
|
420
438
|
|
421
439
|
|
422
440
|
def set_creator(user)
|
423
|
-
self.
|
441
|
+
self.send("#{self.class.creator_attr}=", user) if (t = self.class.creator_type) && user.is_a?(t)
|
424
442
|
end
|
425
443
|
|
426
444
|
|