hobo 0.5.3
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE.txt +22 -0
- data/README.txt +18 -0
- data/bin/hobo +81 -0
- data/hobo_files/plugin/CHANGES.txt +963 -0
- data/hobo_files/plugin/LICENSE.txt +22 -0
- data/hobo_files/plugin/README +4 -0
- data/hobo_files/plugin/Rakefile +11 -0
- data/hobo_files/plugin/generators/hobo/hobo_generator.rb +37 -0
- data/hobo_files/plugin/generators/hobo/templates/application.dryml +2 -0
- data/hobo_files/plugin/generators/hobo/templates/guest.rb +31 -0
- data/hobo_files/plugin/generators/hobo_front_controller/USAGE +11 -0
- data/hobo_files/plugin/generators/hobo_front_controller/hobo_front_controller_generator.rb +90 -0
- data/hobo_files/plugin/generators/hobo_front_controller/templates/controller.rb +51 -0
- data/hobo_files/plugin/generators/hobo_front_controller/templates/functional_test.rb +18 -0
- data/hobo_files/plugin/generators/hobo_front_controller/templates/helper.rb +2 -0
- data/hobo_files/plugin/generators/hobo_front_controller/templates/index.dryml +43 -0
- data/hobo_files/plugin/generators/hobo_front_controller/templates/login.dryml +44 -0
- data/hobo_files/plugin/generators/hobo_front_controller/templates/search.dryml +18 -0
- data/hobo_files/plugin/generators/hobo_front_controller/templates/signup.dryml +45 -0
- data/hobo_files/plugin/generators/hobo_model/USAGE +26 -0
- data/hobo_files/plugin/generators/hobo_model/hobo_model_generator.rb +38 -0
- data/hobo_files/plugin/generators/hobo_model/templates/fixtures.yml +11 -0
- data/hobo_files/plugin/generators/hobo_model/templates/migration.rb +13 -0
- data/hobo_files/plugin/generators/hobo_model/templates/model.rb +24 -0
- data/hobo_files/plugin/generators/hobo_model/templates/unit_test.rb +10 -0
- data/hobo_files/plugin/generators/hobo_model_controller/USAGE +30 -0
- data/hobo_files/plugin/generators/hobo_model_controller/hobo_model_controller_generator.rb +43 -0
- data/hobo_files/plugin/generators/hobo_model_controller/templates/controller.rb +5 -0
- data/hobo_files/plugin/generators/hobo_model_controller/templates/functional_test.rb +18 -0
- data/hobo_files/plugin/generators/hobo_model_controller/templates/helper.rb +2 -0
- data/hobo_files/plugin/generators/hobo_model_controller/templates/view.rhtml +2 -0
- data/hobo_files/plugin/generators/hobo_rapid/hobo_rapid_generator.rb +51 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/hobo_rapid.js +436 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/default_mapping.rb +11 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/banner.gif +0 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/bkg_bodytop.gif +0 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/bkg_corner_01.gif +0 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/bkg_corner_02.gif +0 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/bkg_corner_03.gif +0 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/bkg_corner_04.gif +0 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/bkg_shadow_bottom.gif +0 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/bkg_shadow_left.gif +0 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/bkg_shadow_right.gif +0 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/bkg_shadow_top.gif +0 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/header_blue.gif +0 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/header_dblue.gif +0 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/header_green.gif +0 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/header_purple.gif +0 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/header_red.gif +0 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/logo.gif +0 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/spinner.gif +0 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/txt_list_img_dblue.gif +0 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/txt_list_img_green.gif +0 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/txt_list_img_purple.gif +0 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/txt_list_img_red.gif +0 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/window_corner_01.gif +0 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/window_corner_02.gif +0 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/window_corner_03.gif +0 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/window_corner_04.gif +0 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/window_shadow_bottom.gif +0 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/window_shadow_left.gif +0 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/window_shadow_right.gif +0 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/window_shadow_top.gif +0 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/stylesheets/application.css +390 -0
- data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/views/application.dryml +104 -0
- data/hobo_files/plugin/generators/hobo_user_model/USAGE +26 -0
- data/hobo_files/plugin/generators/hobo_user_model/hobo_user_model_generator.rb +38 -0
- data/hobo_files/plugin/generators/hobo_user_model/templates/fixtures.yml +11 -0
- data/hobo_files/plugin/generators/hobo_user_model/templates/migration.rb +15 -0
- data/hobo_files/plugin/generators/hobo_user_model/templates/model.rb +58 -0
- data/hobo_files/plugin/generators/hobo_user_model/templates/unit_test.rb +10 -0
- data/hobo_files/plugin/init.rb +44 -0
- data/hobo_files/plugin/lib/action_view_extensions/base.rb +14 -0
- data/hobo_files/plugin/lib/active_record/has_many_association.rb +54 -0
- data/hobo_files/plugin/lib/active_record/has_many_through_association.rb +22 -0
- data/hobo_files/plugin/lib/active_record/table_definition.rb +34 -0
- data/hobo_files/plugin/lib/extensions.rb +245 -0
- data/hobo_files/plugin/lib/extensions/test_case.rb +130 -0
- data/hobo_files/plugin/lib/hobo.rb +353 -0
- data/hobo_files/plugin/lib/hobo/HtmlString +3 -0
- data/hobo_files/plugin/lib/hobo/authenticated_user.rb +106 -0
- data/hobo_files/plugin/lib/hobo/authentication_support.rb +108 -0
- data/hobo_files/plugin/lib/hobo/composite_model.rb +66 -0
- data/hobo_files/plugin/lib/hobo/controller.rb +134 -0
- data/hobo_files/plugin/lib/hobo/controller_helpers.rb +135 -0
- data/hobo_files/plugin/lib/hobo/core.rb +475 -0
- data/hobo_files/plugin/lib/hobo/define_tags.rb +56 -0
- data/hobo_files/plugin/lib/hobo/dryml.rb +161 -0
- data/hobo_files/plugin/lib/hobo/dryml/dryml_builder.rb +126 -0
- data/hobo_files/plugin/lib/hobo/dryml/tag_module.rb +9 -0
- data/hobo_files/plugin/lib/hobo/dryml/taglib.rb +57 -0
- data/hobo_files/plugin/lib/hobo/dryml/template.rb +586 -0
- data/hobo_files/plugin/lib/hobo/dryml/template_environment.rb +302 -0
- data/hobo_files/plugin/lib/hobo/dryml/template_handler.rb +19 -0
- data/hobo_files/plugin/lib/hobo/generator.rb +25 -0
- data/hobo_files/plugin/lib/hobo/html_string.rb +3 -0
- data/hobo_files/plugin/lib/hobo/lazy_hash.rb +28 -0
- data/hobo_files/plugin/lib/hobo/mapping_tags.rb +262 -0
- data/hobo_files/plugin/lib/hobo/markdown_string.rb +7 -0
- data/hobo_files/plugin/lib/hobo/model.rb +391 -0
- data/hobo_files/plugin/lib/hobo/model_controller.rb +676 -0
- data/hobo_files/plugin/lib/hobo/model_queries.rb +92 -0
- data/hobo_files/plugin/lib/hobo/model_support.rb +44 -0
- data/hobo_files/plugin/lib/hobo/password_string.rb +3 -0
- data/hobo_files/plugin/lib/hobo/predicate_dispatch.rb +78 -0
- data/hobo_files/plugin/lib/hobo/proc_binding.rb +32 -0
- data/hobo_files/plugin/lib/hobo/rapid.rb +447 -0
- data/hobo_files/plugin/lib/hobo/static_tags +92 -0
- data/hobo_files/plugin/lib/hobo/text.rb +3 -0
- data/hobo_files/plugin/lib/hobo/textile_string.rb +13 -0
- data/hobo_files/plugin/lib/hobo/undefined.rb +41 -0
- data/hobo_files/plugin/lib/hobo/undefined_access_error.rb +5 -0
- data/hobo_files/plugin/lib/hobo/where_fragment.rb +23 -0
- data/hobo_files/plugin/lib/rexml.rb +345 -0
- data/hobo_files/plugin/tags/core.dryml +6 -0
- data/hobo_files/plugin/tags/rapid.dryml +177 -0
- data/hobo_files/plugin/tags/rapid_editing.dryml +168 -0
- data/hobo_files/plugin/tags/rapid_navigation.dryml +95 -0
- data/hobo_files/plugin/tags/rapid_pages.dryml +175 -0
- data/hobo_files/plugin/tasks/environments.rake +19 -0
- data/hobo_files/plugin/tasks/hobo_tasks.rake +4 -0
- data/hobo_files/plugin/test/hobo_dryml_template_test.rb +7 -0
- data/hobo_files/plugin/test/hobo_test.rb +7 -0
- data/hobo_files/plugin/uninstall.rb +1 -0
- metadata +206 -0
@@ -0,0 +1,391 @@
|
|
1
|
+
module Hobo
|
2
|
+
|
3
|
+
module Model
|
4
|
+
|
5
|
+
def self.included(base)
|
6
|
+
Hobo.register_model(base)
|
7
|
+
base.extend(ClassMethods)
|
8
|
+
base.set_field_type({})
|
9
|
+
class << base
|
10
|
+
alias_method_chain :has_many, :defined_scopes
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
module ClassMethods
|
15
|
+
|
16
|
+
# include methods also shared by CompositeModel
|
17
|
+
include ModelSupport::ClassMethods
|
18
|
+
|
19
|
+
def method_added(name)
|
20
|
+
# avoid error when running model generators before
|
21
|
+
# db exists
|
22
|
+
return unless connected?
|
23
|
+
|
24
|
+
aliased_name = "#{name}_without_hobo_type"
|
25
|
+
return if name.to_s.ends_with?('without_hobo_type') or aliased_name.in?(instance_methods)
|
26
|
+
|
27
|
+
type_wrapper = self.field_type(name)
|
28
|
+
if type_wrapper && type_wrapper.is_a?(Class) && type_wrapper < String
|
29
|
+
aliased_name = "#{name}_without_hobo_type"
|
30
|
+
alias_method aliased_name, name
|
31
|
+
define_method name do
|
32
|
+
res = send(aliased_name)
|
33
|
+
if res.nil?
|
34
|
+
nil
|
35
|
+
elsif res.respond_to?(:hobo_undefined?) && res.hobo_undefined?
|
36
|
+
res
|
37
|
+
else
|
38
|
+
type_wrapper.new(res)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def set_field_type(types)
|
45
|
+
types.each_pair do |field, type|
|
46
|
+
|
47
|
+
# TODO: Make this extensible
|
48
|
+
type_class = case type
|
49
|
+
when :html; HtmlString
|
50
|
+
when :markdown; MarkdownString
|
51
|
+
when :textile; TextileString
|
52
|
+
when :password; PasswordString
|
53
|
+
else type
|
54
|
+
end
|
55
|
+
|
56
|
+
field_types[field] = type_class
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
def field_types
|
62
|
+
@hobo_field_types ||= superclass.respond_to?(:field_types) ? superclass.field_types : {}
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
def set_default_order(order)
|
67
|
+
@default_order = order
|
68
|
+
end
|
69
|
+
|
70
|
+
inheriting_attr_accessor :default_order, :id_name_options
|
71
|
+
|
72
|
+
|
73
|
+
def never_show(*fields)
|
74
|
+
@hobo_never_show ||= []
|
75
|
+
@hobo_never_show.concat(fields.omap{to_sym})
|
76
|
+
end
|
77
|
+
|
78
|
+
|
79
|
+
def never_show?(field)
|
80
|
+
@hobo_never_show and field.to_sym.in?(@hobo_never_show)
|
81
|
+
end
|
82
|
+
|
83
|
+
def set_creator_attr(attr)
|
84
|
+
class_eval %{
|
85
|
+
def creator
|
86
|
+
#{attr};
|
87
|
+
end
|
88
|
+
def creator=(x)
|
89
|
+
self.#{attr} = x;
|
90
|
+
end
|
91
|
+
}
|
92
|
+
end
|
93
|
+
|
94
|
+
def set_search_columns(*columns)
|
95
|
+
class_eval %{
|
96
|
+
def self.search_columns
|
97
|
+
%w{#{columns.omap{to_s} * ' '}}
|
98
|
+
end
|
99
|
+
}
|
100
|
+
end
|
101
|
+
|
102
|
+
|
103
|
+
def id_name(*args)
|
104
|
+
@id_name_options = [] + args
|
105
|
+
|
106
|
+
underscore = args.delete(:underscore)
|
107
|
+
insenstive = args.delete(:case_insensitive)
|
108
|
+
id_name_field = args.first || :name
|
109
|
+
@id_name_column = id_name_field.to_s
|
110
|
+
|
111
|
+
if underscore
|
112
|
+
class_eval %{
|
113
|
+
def id_name(underscore=false)
|
114
|
+
underscore ? #{id_name_field}.gsub(' ', '_') : #{id_name_field}
|
115
|
+
end
|
116
|
+
}
|
117
|
+
else
|
118
|
+
class_eval %{
|
119
|
+
def id_name(underscore=false)
|
120
|
+
#{id_name_field}
|
121
|
+
end
|
122
|
+
}
|
123
|
+
end
|
124
|
+
|
125
|
+
key = "id_name#{if underscore; ".gsub('_', ' ')"; end}"
|
126
|
+
finder = if insenstive
|
127
|
+
"find(:first, :conditions => ['lower(#{id_name_field}) = ?', #{key}.downcase])"
|
128
|
+
else
|
129
|
+
"find_by_#{id_name_field}(#{key})"
|
130
|
+
end
|
131
|
+
|
132
|
+
class_eval %{
|
133
|
+
def self.find_by_id_name(id_name)
|
134
|
+
#{finder}
|
135
|
+
end
|
136
|
+
}
|
137
|
+
|
138
|
+
model = self
|
139
|
+
validate do
|
140
|
+
erros.add id_name_field, "is taken" if model.find_by_id_name(name)
|
141
|
+
end
|
142
|
+
validates_format_of id_name_field, :with => /^[^_]+$/, :message => "cannot contain underscores" if
|
143
|
+
underscore
|
144
|
+
end
|
145
|
+
|
146
|
+
|
147
|
+
def id_name?
|
148
|
+
respond_to?(:find_by_id_name)
|
149
|
+
end
|
150
|
+
|
151
|
+
|
152
|
+
attr_reader :id_name_column
|
153
|
+
|
154
|
+
|
155
|
+
|
156
|
+
def field_type(name)
|
157
|
+
name = name.to_sym
|
158
|
+
field_types[name] or
|
159
|
+
reflections[name] or
|
160
|
+
((col = columns.find {|c| c.name == name.to_s}) and case col.type
|
161
|
+
when :boolean
|
162
|
+
TrueClass
|
163
|
+
when :text
|
164
|
+
Hobo::Text
|
165
|
+
else
|
166
|
+
col.klass
|
167
|
+
end)
|
168
|
+
end
|
169
|
+
|
170
|
+
|
171
|
+
def conditions(&b)
|
172
|
+
ModelQueries.new(self).instance_eval(&b).to_sql
|
173
|
+
end
|
174
|
+
|
175
|
+
|
176
|
+
def find(*args, &b)
|
177
|
+
if args.first.in?([:all, :first])
|
178
|
+
if args.last.is_a? Hash
|
179
|
+
options = args.last
|
180
|
+
args[-1] = options = options.merge(:order => default_order) if options[:order] == :default
|
181
|
+
else
|
182
|
+
options = {}
|
183
|
+
end
|
184
|
+
|
185
|
+
if b
|
186
|
+
super(args.first, options.merge(:conditions => conditions(&b)))
|
187
|
+
else
|
188
|
+
super(*args)
|
189
|
+
end
|
190
|
+
else
|
191
|
+
super(*args)
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
|
196
|
+
def count(*args, &b)
|
197
|
+
if b
|
198
|
+
sql = ModelQueries.new(self).instance_eval(&b).to_sql
|
199
|
+
options = extract_options_from_args!(args)
|
200
|
+
super(*args + [options.merge(:conditions => sql)])
|
201
|
+
else
|
202
|
+
super(*args)
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
|
207
|
+
def subclass_associations(association, *subclass_associations)
|
208
|
+
refl = reflections[association]
|
209
|
+
for assoc in subclass_associations
|
210
|
+
class_name = assoc.to_s.classify
|
211
|
+
options = { :class_name => class_name, :conditions => "type = '#{class_name}'" }
|
212
|
+
options[:source] = refl.source_reflection.name if refl.source_reflection
|
213
|
+
has_many(assoc, refl.options.merge(options))
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
def has_creator?
|
218
|
+
instance_methods.include?('creator=') and instance_methods.include?('creator')
|
219
|
+
end
|
220
|
+
|
221
|
+
def search_columns
|
222
|
+
cols = columns.omap{name}
|
223
|
+
%w{name title body content}.select{|c| c.in?(cols) }
|
224
|
+
end
|
225
|
+
|
226
|
+
# This should really be a method on AssociationReflection
|
227
|
+
def reverse_reflection(association_name)
|
228
|
+
refl = reflections[association_name]
|
229
|
+
return nil if refl.options[:conditions]
|
230
|
+
|
231
|
+
reverse_macro = if refl.macro == :has_many
|
232
|
+
:belongs_to
|
233
|
+
elsif refl.macro == :belongs_to
|
234
|
+
:has_many
|
235
|
+
end
|
236
|
+
refl.klass.reflections.values.find do |r|
|
237
|
+
r.macro == reverse_macro and
|
238
|
+
r.klass == self and
|
239
|
+
!r.options[:conditions] and
|
240
|
+
r.primary_key_name == refl.primary_key_name
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
|
245
|
+
class ScopedProxy
|
246
|
+
def initialize(klass, scope={})
|
247
|
+
@klass, @scope = klass, scope
|
248
|
+
end
|
249
|
+
|
250
|
+
def method_missing(name, *args, &block)
|
251
|
+
klass.with_scope(@scope) do
|
252
|
+
@klass.send(name, *args, &block)
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
(Object.instance_methods +
|
257
|
+
Object.private_instance_methods +
|
258
|
+
Object.protected_instance_methods).each do |m|
|
259
|
+
ScopedProxy.send(:undef_method, m) unless
|
260
|
+
m.in?(%w{initialize method_missing}) || m.starts_with?('_')
|
261
|
+
end
|
262
|
+
|
263
|
+
attr_accessor :defined_scopes
|
264
|
+
|
265
|
+
|
266
|
+
def def_scope(name, scope=nil, &block)
|
267
|
+
@defined_scopes ||= {}
|
268
|
+
@defined_scopes[name.to_sym] = block || scope
|
269
|
+
|
270
|
+
meta_def(name) do |*args|
|
271
|
+
ScopedProxy.new(self, block ? block.call(*args) : scope)
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
|
276
|
+
module DefinedScopeProxyExtender
|
277
|
+
|
278
|
+
attr_accessor :reflections
|
279
|
+
|
280
|
+
def method_missing(name, *args, &block)
|
281
|
+
scopes = proxy_reflection.klass.defined_scopes
|
282
|
+
scope = scopes && scopes[name.to_sym]
|
283
|
+
if scope
|
284
|
+
scope = scope.call(*args) if scope.is_a?(Proc)
|
285
|
+
|
286
|
+
# Calling directly causes self to get loaded
|
287
|
+
assoc = Kernel.instance_method(:instance_variable_get).bind(self).call("@#{name}")
|
288
|
+
unless assoc
|
289
|
+
options = proxy_reflection.options
|
290
|
+
has_many_conditions = options.has_key?(:condition)
|
291
|
+
scope_conditions = scope.delete(:conditions)
|
292
|
+
conditions = if has_many_conditions && scope_conditions
|
293
|
+
"(#{scope_conditions}) AND (#{has_many_conditions})"
|
294
|
+
else
|
295
|
+
scope_conditions || has_many_conditions
|
296
|
+
end
|
297
|
+
|
298
|
+
options = options.merge(scope).update(:conditions => conditions,
|
299
|
+
:class_name => proxy_reflection.klass.name,
|
300
|
+
:foreign_key => proxy_reflection.primary_key_name)
|
301
|
+
r = ActiveRecord::Reflection::AssociationReflection.new(:has_many,
|
302
|
+
name,
|
303
|
+
options,
|
304
|
+
proxy_reflection.klass)
|
305
|
+
@reflections ||= {}
|
306
|
+
@reflections[name] = r
|
307
|
+
|
308
|
+
assoc = if options.has_key?(:through)
|
309
|
+
ActiveRecord::Associations::HasManyThroughAssociation
|
310
|
+
else
|
311
|
+
ActiveRecord::Associations::HasManyAssociation
|
312
|
+
end.new(self.proxy_owner, r)
|
313
|
+
|
314
|
+
# Calling directly causes self to get loaded
|
315
|
+
Kernel.instance_method(:instance_variable_set).bind(self).call("@#{name}", assoc)
|
316
|
+
end
|
317
|
+
assoc
|
318
|
+
else
|
319
|
+
super
|
320
|
+
end
|
321
|
+
end
|
322
|
+
|
323
|
+
end
|
324
|
+
|
325
|
+
|
326
|
+
def has_many_with_defined_scopes(name, *args, &block)
|
327
|
+
options = extract_options_from_args!(args)
|
328
|
+
if options.has_key?(:extend) || block
|
329
|
+
# Normal has_many
|
330
|
+
has_many_without_defined_scopes(name, *args + [options], &block)
|
331
|
+
else
|
332
|
+
options[:extend] = DefinedScopeProxyExtender
|
333
|
+
has_many_without_defined_scopes(name, *args + [options], &block)
|
334
|
+
end
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
|
339
|
+
def method_missing(name, *args, &b)
|
340
|
+
val = super
|
341
|
+
if val.nil?
|
342
|
+
nil
|
343
|
+
else
|
344
|
+
type_wrapper = self.class.field_type(name)
|
345
|
+
(type_wrapper && type_wrapper.is_a?(Class) && type_wrapper < String) ? type_wrapper.new(val) : val
|
346
|
+
end
|
347
|
+
end
|
348
|
+
|
349
|
+
|
350
|
+
def created_by(user)
|
351
|
+
self.creator ||= user if self.class.has_creator? and not user.guest?
|
352
|
+
end
|
353
|
+
|
354
|
+
|
355
|
+
def duplicate
|
356
|
+
res = self.class.new
|
357
|
+
res.instance_variable_set("@attributes", @attributes.dup)
|
358
|
+
res.instance_variable_set("@new_record", nil) unless new_record?
|
359
|
+
|
360
|
+
# Shallow copy of belongs_to associations
|
361
|
+
for refl in self.class.reflections.values
|
362
|
+
if refl.macro == :belongs_to and (target = self.send(refl.name))
|
363
|
+
bta = ActiveRecord::Associations::BelongsToAssociation.new(res, refl)
|
364
|
+
bta.replace(target)
|
365
|
+
res.instance_variable_set("@#{refl.name}", bta)
|
366
|
+
end
|
367
|
+
end
|
368
|
+
res
|
369
|
+
end
|
370
|
+
|
371
|
+
|
372
|
+
def same_fields?(other, *fields)
|
373
|
+
fields.all?{|f| self.send(f) == other.send(f)}
|
374
|
+
end
|
375
|
+
|
376
|
+
def changed_fields?(other, *fields)
|
377
|
+
fields.all?{|f| self.send(f) != other.send(f)}
|
378
|
+
end
|
379
|
+
|
380
|
+
def compose_with(object, use=nil)
|
381
|
+
CompositeModel.new_for([self, object])
|
382
|
+
end
|
383
|
+
|
384
|
+
|
385
|
+
def typed_id
|
386
|
+
id ? "#{self.class.name.underscore}_#{self.id}" : nil
|
387
|
+
end
|
388
|
+
|
389
|
+
end
|
390
|
+
end
|
391
|
+
|
@@ -0,0 +1,676 @@
|
|
1
|
+
module Hobo
|
2
|
+
|
3
|
+
module ModelController
|
4
|
+
|
5
|
+
include Hobo::Controller
|
6
|
+
|
7
|
+
class PermissionDeniedError < RuntimeError; end
|
8
|
+
|
9
|
+
VIEWLIB_DIR = "hobolib"
|
10
|
+
|
11
|
+
GENERIC_PAGE_TAGS = [:index, :show, :new, :edit, :show_collection, :new_in_collection]
|
12
|
+
|
13
|
+
class << self
|
14
|
+
|
15
|
+
def included(base)
|
16
|
+
base.extend(ClassMethods)
|
17
|
+
base.helper_method(:find_partial, :model, :current_user)
|
18
|
+
|
19
|
+
Hobo::ControllerHelpers.public_instance_methods.each {|m| base.hide_action(m)}
|
20
|
+
|
21
|
+
for collection in base.collections
|
22
|
+
add_collection_actions(base, collection.to_sym)
|
23
|
+
end
|
24
|
+
|
25
|
+
base.before_filter :set_no_cache_headers
|
26
|
+
|
27
|
+
base.class_eval do
|
28
|
+
alias_method_chain :redirect_to, :object_url
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def find_partial(klass, as)
|
33
|
+
find_model_template(klass, as, true)
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
def template_path(dir, name, is_partial)
|
38
|
+
fileRx = is_partial ? /^_#{name}\.[^.]+/ : /^#{name}\.[^.]+/
|
39
|
+
unless Dir.entries("#{RAILS_ROOT}/app/views/#{dir}").grep(fileRx).empty?
|
40
|
+
return "#{dir}/#{name}"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
def find_model_template(klass, name, is_partial=false)
|
46
|
+
while klass and klass != ActiveRecord::Base
|
47
|
+
dir = klass.name.underscore.pluralize
|
48
|
+
path = template_path(dir, name, is_partial)
|
49
|
+
return path if path
|
50
|
+
|
51
|
+
klass = klass.superclass
|
52
|
+
end
|
53
|
+
nil
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
def add_collection_actions(controller_class, name)
|
58
|
+
defined_methods = controller_class.instance_methods
|
59
|
+
|
60
|
+
show_collection_method = "show_#{name}".to_sym
|
61
|
+
unless show_collection_method.in?(defined_methods)
|
62
|
+
controller_class.send(:define_method, show_collection_method) do
|
63
|
+
hobo_show_collection(name)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
if Hobo.simple_has_many_association?(controller_class.model.reflections[name])
|
68
|
+
new_method = "new_#{name.to_s.singularize}"
|
69
|
+
if new_method.not_in?(defined_methods)
|
70
|
+
controller_class.send(:define_method, new_method) do
|
71
|
+
hobo_new_in_collection(name)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
module ClassMethods
|
80
|
+
|
81
|
+
attr_writer :model
|
82
|
+
|
83
|
+
def web_methods
|
84
|
+
@web_methods ||= superclass.respond_to?(:web_methods) ? superclass.web_methods : []
|
85
|
+
end
|
86
|
+
|
87
|
+
def show_actions
|
88
|
+
@show_actions ||= superclass.respond_to?(:show_actions) ? superclass.show_actions : []
|
89
|
+
end
|
90
|
+
|
91
|
+
def collections
|
92
|
+
# By default, all has_many associations are published
|
93
|
+
@collections ||= if superclass.respond_to?(:collections)
|
94
|
+
superclass.collections
|
95
|
+
else
|
96
|
+
model.reflections.values.map {|r| r.name if r.macro == :has_many}.compact
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def model
|
101
|
+
@model ||= name.sub(/Controller$/, "").singularize.constantize
|
102
|
+
end
|
103
|
+
|
104
|
+
|
105
|
+
def autocomplete_for(attr, options={})
|
106
|
+
opts = { :limit => 15 }.update(options)
|
107
|
+
@completers ||= {}
|
108
|
+
@completers[attr.to_sym] = opts
|
109
|
+
end
|
110
|
+
|
111
|
+
|
112
|
+
def autocompleter(name)
|
113
|
+
(@completers && @completers[name]) ||
|
114
|
+
(superclass.respond_to?(:autocompleter) && superclass.autocompleter(name))
|
115
|
+
end
|
116
|
+
|
117
|
+
|
118
|
+
def def_data_filter(name, &b)
|
119
|
+
@data_filters ||= {}
|
120
|
+
@data_filters[name] = b
|
121
|
+
end
|
122
|
+
|
123
|
+
|
124
|
+
def web_method(web_name, method_name=nil)
|
125
|
+
method_name ||= web_name
|
126
|
+
web_methods << web_name.to_sym
|
127
|
+
before_filter(:only => [web_name]) {|controller| controller.send(:prepare_web_method, method_name)}
|
128
|
+
end
|
129
|
+
|
130
|
+
|
131
|
+
def show_action(*names)
|
132
|
+
show_actions.concat(names)
|
133
|
+
for name in names
|
134
|
+
class_eval "def #{name}; show; end"
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
|
139
|
+
def publish_collection(*names)
|
140
|
+
collections.concat(names)
|
141
|
+
names.each {|n| ModelController.add_collection_actions(self, n)}
|
142
|
+
end
|
143
|
+
|
144
|
+
|
145
|
+
def data_filter(name)
|
146
|
+
(@data_filters && @data_filters[name]) ||
|
147
|
+
(superclass.respond_to?(:data_filter) && superclass.data_filter(name))
|
148
|
+
end
|
149
|
+
|
150
|
+
|
151
|
+
def find_instance(id)
|
152
|
+
if model.id_name? and id !~ /^\d+$/
|
153
|
+
model.find_by_id_name(id)
|
154
|
+
else
|
155
|
+
model.find(id)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
end
|
160
|
+
|
161
|
+
# --- ACTIONS --- #
|
162
|
+
|
163
|
+
def index; hobo_index; end
|
164
|
+
def show; hobo_show; end
|
165
|
+
def new; hobo_new; end
|
166
|
+
def create; hobo_create; end
|
167
|
+
def edit; hobo_edit; end
|
168
|
+
def update; hobo_update; end
|
169
|
+
def destroy; hobo_destroy; end
|
170
|
+
|
171
|
+
def completions
|
172
|
+
attr = params[:for]
|
173
|
+
opts = attr && self.class.autocompleter(attr.to_sym)
|
174
|
+
if opts
|
175
|
+
q = params[:query]
|
176
|
+
items = find_with_data_filter(opts) { send("#{attr}_contains", q) }
|
177
|
+
|
178
|
+
render :text => "<ul>\n" + items.map {|i| "<li>#{i.send(attr)}</li>\n"}.join + "</ul>"
|
179
|
+
else
|
180
|
+
render :text => "No completer for #{attr}", :status => 404
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
|
185
|
+
###### END OF ACTIONS ######
|
186
|
+
|
187
|
+
protected
|
188
|
+
|
189
|
+
def overridable_response(options, key)
|
190
|
+
if options.has_key?(key)
|
191
|
+
options[key]
|
192
|
+
true
|
193
|
+
else
|
194
|
+
yield if block_given?
|
195
|
+
false
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
# --- action implementations --- #
|
200
|
+
|
201
|
+
def hobo_index(options = {})
|
202
|
+
options = LazyHash.new(options)
|
203
|
+
@model = model
|
204
|
+
@this = options[:collection] || paginated_find
|
205
|
+
|
206
|
+
instance_variable_set("@#{@model.name.pluralize.underscore}", @this)
|
207
|
+
if block_given?
|
208
|
+
yield
|
209
|
+
else
|
210
|
+
hobo_render
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
|
215
|
+
def paginated_find(*args, &b)
|
216
|
+
options = extract_options_from_args!(args)
|
217
|
+
|
218
|
+
total_number = options.delete(:total_number)
|
219
|
+
if args.empty?
|
220
|
+
total_number ||= count_with_data_filter
|
221
|
+
else
|
222
|
+
owner, collection_name = args
|
223
|
+
@association = collection_name.to_s.split(".").inject(owner) { |m, name| m.send(name) }
|
224
|
+
total_number ||= @association.size
|
225
|
+
@reflection = @association.proxy_reflection
|
226
|
+
end
|
227
|
+
|
228
|
+
page_size = options.delete(:page_size) || 20
|
229
|
+
page = options.delete(:page) || params[:page]
|
230
|
+
@pages = ::ActionController::Pagination::Paginator.new(self, total_number, page_size, page)
|
231
|
+
|
232
|
+
options = {
|
233
|
+
:limit => @pages.items_per_page,
|
234
|
+
:offset => @pages.current.offset,
|
235
|
+
}.merge(options)
|
236
|
+
|
237
|
+
if @association
|
238
|
+
@association.find(:all, options, &b)
|
239
|
+
else
|
240
|
+
options[:order] = :default
|
241
|
+
find_with_data_filter(options, &b)
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
|
246
|
+
def find_instance_or_not_found(options, this_option)
|
247
|
+
x = begin
|
248
|
+
options[this_option] || find_instance
|
249
|
+
rescue ActiveRecord::RecordNotFound
|
250
|
+
nil
|
251
|
+
end
|
252
|
+
|
253
|
+
not_found unless x
|
254
|
+
x
|
255
|
+
end
|
256
|
+
|
257
|
+
|
258
|
+
def hobo_show(options={})
|
259
|
+
options = LazyHash.new(options)
|
260
|
+
|
261
|
+
@this = find_instance_or_not_found(options, :this)
|
262
|
+
if @this
|
263
|
+
if Hobo.can_view?(current_user, @this)
|
264
|
+
set_named_this!
|
265
|
+
block_given? ? yield : hobo_render
|
266
|
+
else
|
267
|
+
permission_denied(options)
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
|
273
|
+
def hobo_new(options={})
|
274
|
+
options = LazyHash.new(options)
|
275
|
+
@this = options[:this] || model.new
|
276
|
+
@this.created_by(current_user) unless options.has_key?(:set_creator) && !options[:set_creator]
|
277
|
+
|
278
|
+
if Hobo.can_create?(current_user, @this)
|
279
|
+
set_named_this!
|
280
|
+
block_given? ? yield : hobo_render
|
281
|
+
else
|
282
|
+
permission_denied(options)
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
|
287
|
+
def hobo_create(options={})
|
288
|
+
options = LazyHash.new(options)
|
289
|
+
|
290
|
+
@this = (options[:this] ||
|
291
|
+
begin
|
292
|
+
attributes = params[model.name.underscore]
|
293
|
+
type_attr = params['type']
|
294
|
+
create_model = if 'type'.in?(model.column_names) and
|
295
|
+
type_attr and type_attr.in?(model.send(:subclasses).omap{name})
|
296
|
+
type_attr.constantize
|
297
|
+
else
|
298
|
+
model
|
299
|
+
end
|
300
|
+
this = create_model.new
|
301
|
+
@check_create_permission = [this]
|
302
|
+
initialize_from_params(this, attributes)
|
303
|
+
for obj in @check_create_permission
|
304
|
+
permission_denied(options) and return unless Hobo.can_create?(current_user, obj)
|
305
|
+
end
|
306
|
+
@check_create_permission = nil
|
307
|
+
this
|
308
|
+
end)
|
309
|
+
|
310
|
+
set_named_this!
|
311
|
+
if @this.save
|
312
|
+
if block_given?
|
313
|
+
yield
|
314
|
+
else
|
315
|
+
respond_to do |wants|
|
316
|
+
wants.html { overridable_response(options, :html_response) || redirect_to(object_url(@this)) }
|
317
|
+
wants.js { overridable_response(options, :js_response) || hobo_ajax_response || render(:text => "") }
|
318
|
+
end
|
319
|
+
end
|
320
|
+
else
|
321
|
+
# Validation errors
|
322
|
+
unless options[:invalid_response]
|
323
|
+
respond_to do |wants|
|
324
|
+
wants.html { overridable_response(options, :invalid_html_response) || hobo_render(:new) }
|
325
|
+
wants.js do
|
326
|
+
(overridable_response(options, :invalid_js_response) ||
|
327
|
+
render(:status => 500,
|
328
|
+
:text => ("There was a problem creating that #{create_model.name}.\n" +
|
329
|
+
@this.errors.full_messages.join("\n"))))
|
330
|
+
end
|
331
|
+
end
|
332
|
+
end
|
333
|
+
end
|
334
|
+
end
|
335
|
+
|
336
|
+
def hobo_edit(options={})
|
337
|
+
hobo_show(options)
|
338
|
+
end
|
339
|
+
|
340
|
+
|
341
|
+
def hobo_update(options={})
|
342
|
+
options = LazyHash.new(options)
|
343
|
+
|
344
|
+
@this = find_instance_or_not_found(options, :this)
|
345
|
+
return unless @this
|
346
|
+
|
347
|
+
original = @this.duplicate
|
348
|
+
|
349
|
+
changes = params[model.name.underscore]
|
350
|
+
|
351
|
+
if changes
|
352
|
+
# The 'duplicate' call above can set these, but they can
|
353
|
+
# conflict with the changes so we clear them
|
354
|
+
@this.send(:clear_aggregation_cache)
|
355
|
+
@this.send(:clear_association_cache)
|
356
|
+
|
357
|
+
update_with_params(@this, changes)
|
358
|
+
permission_denied(options) and return unless Hobo.can_update?(current_user, original, @this)
|
359
|
+
end
|
360
|
+
|
361
|
+
set_named_this!
|
362
|
+
if changes.nil? || @this.save
|
363
|
+
# Ensure current_user isn't out of date
|
364
|
+
@current_user = @this if @this == current_user
|
365
|
+
|
366
|
+
if block_given?
|
367
|
+
yield
|
368
|
+
else
|
369
|
+
respond_to do |wants|
|
370
|
+
wants.html do
|
371
|
+
overridable_response(options, :html_response) || redirect_to(object_url(@this))
|
372
|
+
end
|
373
|
+
|
374
|
+
wants.js do
|
375
|
+
overridable_response(options, :js_response) do
|
376
|
+
if changes.size == 1
|
377
|
+
# Decreasingly hacky support for the scriptaculous in-place-editor
|
378
|
+
new_val = Hobo::Dryml.render_tag(@template, "show",
|
379
|
+
:obj => @this, :attr => changes.keys.first, :no_span => true)
|
380
|
+
hobo_ajax_response(@this, :new_field_value => new_val)
|
381
|
+
else
|
382
|
+
hobo_ajax_response(@this)
|
383
|
+
end
|
384
|
+
|
385
|
+
# Maybe no ajax requests were made
|
386
|
+
render :nothing => true unless performed?
|
387
|
+
end
|
388
|
+
end
|
389
|
+
end
|
390
|
+
end
|
391
|
+
|
392
|
+
else
|
393
|
+
# Validation errors
|
394
|
+
respond_to do |wants|
|
395
|
+
wants.html do
|
396
|
+
overridable_response(options, :invalid_html_response) || render(:action => :edit)
|
397
|
+
end
|
398
|
+
|
399
|
+
wants.js do
|
400
|
+
overridable_response(options, :invalid_js_response) do
|
401
|
+
render(:status => 500,
|
402
|
+
:text => ("There was a problem with that change.\n" +
|
403
|
+
@this.errors.full_messages.join("\n")))
|
404
|
+
end
|
405
|
+
end
|
406
|
+
end
|
407
|
+
end
|
408
|
+
end
|
409
|
+
|
410
|
+
|
411
|
+
def hobo_destroy(options={})
|
412
|
+
options = LazyHash.new(options)
|
413
|
+
@this = find_instance_or_not_found(options, :this)
|
414
|
+
return unless @this
|
415
|
+
|
416
|
+
set_named_this!
|
417
|
+
permission_denied(options) and return unless Hobo.can_delete?(current_user, @this)
|
418
|
+
|
419
|
+
@this.destroy
|
420
|
+
|
421
|
+
if block_given?
|
422
|
+
yield
|
423
|
+
else
|
424
|
+
respond_to do |wants|
|
425
|
+
wants.html { overridable_response(options, :html_response) || redirect_to(:action => "index") }
|
426
|
+
wants.js { overridable_response(options, :js_response) || hobo_ajax_response || render(:text => "") }
|
427
|
+
end
|
428
|
+
end
|
429
|
+
end
|
430
|
+
|
431
|
+
def hobo_show_collection(collection, options={})
|
432
|
+
options = LazyHash.new(options)
|
433
|
+
|
434
|
+
@owner = find_instance_or_not_found(options, :owner)
|
435
|
+
return unless @owner
|
436
|
+
|
437
|
+
toplevel_collection = collection.to_s.split(".").first
|
438
|
+
if Hobo.can_view?(current_user, @owner, toplevel_collection)
|
439
|
+
@this = options[:collection] || @this = paginated_find(@owner, collection, options)
|
440
|
+
|
441
|
+
if block_given?
|
442
|
+
yield
|
443
|
+
else
|
444
|
+
hobo_render(params[:action]) or hobo_render(:show_collection, @reflection.klass)
|
445
|
+
end
|
446
|
+
else
|
447
|
+
permission_denied(options)
|
448
|
+
end
|
449
|
+
end
|
450
|
+
|
451
|
+
|
452
|
+
def hobo_new_in_collection(collection, options={})
|
453
|
+
options = LazyHash.new(options)
|
454
|
+
@owner = find_instance_or_not_found(options, :owner)
|
455
|
+
return unless @owner
|
456
|
+
|
457
|
+
permission_denied(options) and return unless Hobo.can_view?(current_user, @owner, collection)
|
458
|
+
|
459
|
+
@association = options[:collection] || @owner.send(collection)
|
460
|
+
@this = options[:this] || @association.new_without_appending
|
461
|
+
@this.created_by(current_user) unless options.has_key?(:set_creator) && !options[:set_creator]
|
462
|
+
|
463
|
+
permission_denied(options) and return unless Hobo.can_create?(current_user, @this)
|
464
|
+
|
465
|
+
if block_given?
|
466
|
+
yield
|
467
|
+
else
|
468
|
+
hobo_render("new_#{collection.to_s.singularize}") or hobo_render(:new_in_collection, @this.class)
|
469
|
+
end
|
470
|
+
end
|
471
|
+
|
472
|
+
|
473
|
+
# --- end action implementations --- #
|
474
|
+
|
475
|
+
# --- filters --- #
|
476
|
+
|
477
|
+
def prepare_web_method(method)
|
478
|
+
@this = find_instance
|
479
|
+
permission_denied unless Hobo.can_call?(current_user, @this, method)
|
480
|
+
end
|
481
|
+
|
482
|
+
# --- end filters --- #
|
483
|
+
|
484
|
+
|
485
|
+
def set_no_cache_headers
|
486
|
+
headers["Pragma"] = "no-cache"
|
487
|
+
#headers["Cache-Control"] = ["must-revalidate", "no-cache", "no-store"]
|
488
|
+
#headers["Cache-Control"] = "no-cache"
|
489
|
+
headers["Cache-Control"] = "no-store"
|
490
|
+
headers["Expires"] ='0'
|
491
|
+
end
|
492
|
+
|
493
|
+
|
494
|
+
def permission_denied(options=nil)
|
495
|
+
if options and options[:permission_denied_response]
|
496
|
+
# do nothing (callback handled by LazyHash)
|
497
|
+
elsif respond_to? :permission_denied_response
|
498
|
+
permission_denied_response
|
499
|
+
else
|
500
|
+
render :text => "Permission Denied", :status => 403
|
501
|
+
end
|
502
|
+
end
|
503
|
+
|
504
|
+
def not_found(options=nil)
|
505
|
+
if options && options[:not_found_response]
|
506
|
+
# do nothing (callback handled by LazyHash)
|
507
|
+
elsif respond_to? :not_found_response
|
508
|
+
not_found_response
|
509
|
+
else
|
510
|
+
render(:text => "Can't find #{model.name.titleize}: #{params[:id]}", :status => 404)
|
511
|
+
end
|
512
|
+
end
|
513
|
+
|
514
|
+
def find_instance(id=nil)
|
515
|
+
res = self.class.find_instance(id || params[:id])
|
516
|
+
instance_variable_set("@#{model.name.underscore}", res)
|
517
|
+
res
|
518
|
+
end
|
519
|
+
|
520
|
+
def set_named_this!
|
521
|
+
instance_variable_set("@#{model.name.underscore}", @this)
|
522
|
+
end
|
523
|
+
|
524
|
+
|
525
|
+
def hobo_render(page_kind = nil, page_model=nil)
|
526
|
+
page_kind ||= params[:action].to_sym
|
527
|
+
page_model ||= model
|
528
|
+
|
529
|
+
template = Hobo::ModelController.find_model_template(page_model, page_kind)
|
530
|
+
|
531
|
+
if template
|
532
|
+
render :template => template
|
533
|
+
true
|
534
|
+
else
|
535
|
+
if page_kind.in? GENERIC_PAGE_TAGS
|
536
|
+
render_tag("#{page_kind}_page", :obj => @this)
|
537
|
+
true
|
538
|
+
else
|
539
|
+
false
|
540
|
+
end
|
541
|
+
end
|
542
|
+
end
|
543
|
+
|
544
|
+
|
545
|
+
def model
|
546
|
+
self.class.model
|
547
|
+
end
|
548
|
+
|
549
|
+
|
550
|
+
def find_template
|
551
|
+
Hobo::ModelController.find_model_template(model, params[:action])
|
552
|
+
end
|
553
|
+
|
554
|
+
|
555
|
+
def with_data_filter(operation, *args, &block)
|
556
|
+
filter_param = params.keys.ofind {starts_with? "where_"}
|
557
|
+
proc = filter_param && self.class.data_filter(filter_param[6..-1].to_sym)
|
558
|
+
if proc
|
559
|
+
filter_args = params[filter_param]
|
560
|
+
filter_args = [filter_args] unless filter_args.is_a? Array
|
561
|
+
model.send(operation, *args) do
|
562
|
+
if block
|
563
|
+
instance_eval(&block) & instance_exec(*filter_args, &proc)
|
564
|
+
else
|
565
|
+
instance_exec(*filter_args, &proc)
|
566
|
+
end
|
567
|
+
end
|
568
|
+
else
|
569
|
+
if block
|
570
|
+
model.send(operation, *args) { instance_eval(&block) }
|
571
|
+
else
|
572
|
+
model.send(operation, *args)
|
573
|
+
end
|
574
|
+
end
|
575
|
+
end
|
576
|
+
|
577
|
+
def find_with_data_filter(opts={}, &b)
|
578
|
+
with_data_filter(:find, :all, opts, &b)
|
579
|
+
end
|
580
|
+
|
581
|
+
|
582
|
+
def count_with_data_filter(opts={}, &b)
|
583
|
+
with_data_filter(:count, opts, &b)
|
584
|
+
end
|
585
|
+
|
586
|
+
|
587
|
+
def initialize_from_params(obj, params)
|
588
|
+
update_with_params(obj, params)
|
589
|
+
obj.created_by(current_user)
|
590
|
+
(@check_create_permission ||= []) << obj
|
591
|
+
obj
|
592
|
+
end
|
593
|
+
|
594
|
+
|
595
|
+
def update_with_params(object, params)
|
596
|
+
return unless params
|
597
|
+
|
598
|
+
params.each_pair do |field,value|
|
599
|
+
field = field.to_sym
|
600
|
+
refl = object.class.reflections[field]
|
601
|
+
ar_value = if refl
|
602
|
+
if refl.macro == :belongs_to
|
603
|
+
associated_record(object, refl, value)
|
604
|
+
|
605
|
+
elsif Hobo.simple_has_many_association?(refl) and object.new_record?
|
606
|
+
# only populate has_many relationships for new records. For existing
|
607
|
+
# records, AR updates the DB immediately, bypassing Hobo's permission check
|
608
|
+
if value.is_a? Array
|
609
|
+
value.map {|x| associated_record(object, refl, x) }
|
610
|
+
else
|
611
|
+
value.keys.every(:to_i).sort.map{|i| associated_record(object, refl, value[i.to_s]) }
|
612
|
+
end
|
613
|
+
else
|
614
|
+
raise HoboError.new("association #{refl.name} is not settable via parameters")
|
615
|
+
end
|
616
|
+
else
|
617
|
+
param_to_value(object.class.field_type(field), value)
|
618
|
+
end
|
619
|
+
object.send("#{field}=".to_sym, ar_value)
|
620
|
+
end
|
621
|
+
end
|
622
|
+
|
623
|
+
|
624
|
+
def parse_datetime(s)
|
625
|
+
defined?(Chronic) ? Chronic.parse(s) : Time.parse(s)
|
626
|
+
end
|
627
|
+
|
628
|
+
|
629
|
+
def param_to_value(field_type, value)
|
630
|
+
if field_type <= Date
|
631
|
+
if value.is_a? Hash
|
632
|
+
Date.new(*(%w{year month day}.map{|s| value[s].to_i}))
|
633
|
+
elsif value.is_a? String
|
634
|
+
dt = parse_datetime(value)
|
635
|
+
dt && dt.to_date
|
636
|
+
end
|
637
|
+
elsif field_type <= Time
|
638
|
+
if value.is_a? Hash
|
639
|
+
Time.local(*(%w{year month day hour minute}.map{|s| value[s].to_i}))
|
640
|
+
elsif value.is_a? String
|
641
|
+
parse_datetime(value)
|
642
|
+
end
|
643
|
+
else
|
644
|
+
# primitive field
|
645
|
+
value
|
646
|
+
end
|
647
|
+
end
|
648
|
+
|
649
|
+
def associated_record(owner, refl, value)
|
650
|
+
if value.is_a? String
|
651
|
+
if value.starts_with?('@')
|
652
|
+
Hobo.object_from_dom_id(value[1..-1])
|
653
|
+
elsif refl.klass.id_name?
|
654
|
+
refl.klass.find_by_id_name(value)
|
655
|
+
else
|
656
|
+
nil
|
657
|
+
end
|
658
|
+
else
|
659
|
+
if refl.macro == :belongs_to
|
660
|
+
new_from_params(refl.klass, value)
|
661
|
+
else
|
662
|
+
obj = owner.send(refl.name).new
|
663
|
+
initialize_from_params(obj, value)
|
664
|
+
obj
|
665
|
+
end
|
666
|
+
end
|
667
|
+
end
|
668
|
+
|
669
|
+
|
670
|
+
def object_from_param(param)
|
671
|
+
Hobo.object_from_dom_id(param)
|
672
|
+
end
|
673
|
+
|
674
|
+
end
|
675
|
+
|
676
|
+
end
|