hobo 1.0.3 → 1.1.0.pre0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. data/CHANGES.txt +0 -66
  2. data/README +3 -0
  3. data/Rakefile +7 -7
  4. data/doctest/model.rdoctest +0 -2
  5. data/doctest/multi_model_forms.rdoctest +0 -2
  6. data/doctest/scopes.rdoctest +13 -21
  7. data/lib/active_record/association_collection.rb +7 -16
  8. data/lib/hobo.rb +10 -65
  9. data/lib/hobo/accessible_associations.rb +1 -5
  10. data/lib/hobo/authentication_support.rb +1 -1
  11. data/lib/hobo/controller.rb +5 -5
  12. data/lib/hobo/hobo_helper.rb +0 -84
  13. data/lib/hobo/lifecycles/lifecycle.rb +37 -31
  14. data/lib/hobo/lifecycles/transition.rb +1 -2
  15. data/lib/hobo/model.rb +13 -21
  16. data/lib/hobo/model_controller.rb +8 -8
  17. data/lib/hobo/rapid_helper.rb +12 -1
  18. data/lib/hobo/scopes/automatic_scopes.rb +26 -13
  19. data/lib/hobo/scopes/named_scope_extensions.rb +16 -28
  20. data/lib/hobo/user_controller.rb +1 -0
  21. data/lib/hobo/view_hints.rb +1 -5
  22. data/rails_generators/hobo/templates/initializer.rb +1 -1
  23. data/rails_generators/hobo_front_controller/templates/summary.dryml +4 -2
  24. data/rails_generators/hobo_model/hobo_model_generator.rb +12 -0
  25. data/rails_generators/hobo_model/templates/model.rb +9 -2
  26. data/rails_generators/hobo_rapid/templates/hobo-rapid.js +98 -23
  27. data/rails_generators/hobo_rapid/templates/themes/clean/public/stylesheets/clean.css +1 -1
  28. data/rails_generators/hobo_rapid/templates/themes/clean/public/stylesheets/rapid-ui.css +3 -1
  29. data/{dryml_generators → rapid_generators}/rapid/cards.dryml.erb +0 -0
  30. data/{dryml_generators → rapid_generators}/rapid/forms.dryml.erb +0 -0
  31. data/{dryml_generators → rapid_generators}/rapid/pages.dryml.erb +1 -1
  32. data/taglibs/rapid.dryml +2 -0
  33. data/taglibs/rapid_core.dryml +10 -9
  34. data/taglibs/rapid_forms.dryml +70 -35
  35. data/taglibs/rapid_lifecycles.dryml +17 -4
  36. data/taglibs/rapid_plus.dryml +3 -3
  37. data/taglibs/rapid_summary.dryml +11 -0
  38. data/taglibs/rapid_user_pages.dryml +39 -28
  39. data/tasks/hobo_tasks.rake +1 -1
  40. metadata +45 -61
  41. data/hobo.gemspec +0 -226
  42. data/lib/hobo/dryml.rb +0 -188
  43. data/lib/hobo/dryml/dryml_builder.rb +0 -140
  44. data/lib/hobo/dryml/dryml_doc.rb +0 -159
  45. data/lib/hobo/dryml/dryml_generator.rb +0 -263
  46. data/lib/hobo/dryml/dryml_support_controller.rb +0 -13
  47. data/lib/hobo/dryml/parser.rb +0 -3
  48. data/lib/hobo/dryml/parser/attribute.rb +0 -41
  49. data/lib/hobo/dryml/parser/base_parser.rb +0 -254
  50. data/lib/hobo/dryml/parser/document.rb +0 -57
  51. data/lib/hobo/dryml/parser/element.rb +0 -27
  52. data/lib/hobo/dryml/parser/elements.rb +0 -45
  53. data/lib/hobo/dryml/parser/source.rb +0 -58
  54. data/lib/hobo/dryml/parser/text.rb +0 -13
  55. data/lib/hobo/dryml/parser/tree_parser.rb +0 -67
  56. data/lib/hobo/dryml/part_context.rb +0 -137
  57. data/lib/hobo/dryml/scoped_variables.rb +0 -42
  58. data/lib/hobo/dryml/tag_parameters.rb +0 -36
  59. data/lib/hobo/dryml/taglib.rb +0 -123
  60. data/lib/hobo/dryml/template.rb +0 -1019
  61. data/lib/hobo/dryml/template_environment.rb +0 -613
  62. data/lib/hobo/dryml/template_handler.rb +0 -187
  63. data/lib/hobo/static_tags +0 -98
  64. data/taglibs/core.dryml +0 -104
@@ -1,613 +0,0 @@
1
- module Hobo::Dryml
2
-
3
- class TemplateEnvironment
4
-
5
- class << self
6
- def inherited(subclass)
7
- subclass.compiled_local_names = []
8
- end
9
- attr_accessor :load_time, :compiled_local_names
10
-
11
-
12
- def _register_tag_attrs(tag_name, attrs)
13
- @tag_attrs ||= {}
14
- @tag_attrs[tag_name] = attrs
15
- end
16
-
17
-
18
- def tag_attrs
19
- @tag_attrs ||= {}
20
- end
21
-
22
- alias_method :delayed_alias_method_chain, :alias_method_chain
23
-
24
- end
25
-
26
- include ActionView::Helpers
27
-
28
-
29
- def initialize(view_name=nil, view=nil)
30
- unless view_name.nil? && view.nil?
31
- @view = view
32
- @_view_name = view_name
33
- @_erb_binding = binding
34
- @_part_contexts = {}
35
- @_scoped_variables = ScopedVariables.new
36
- @_polymorphic_tag_cache = {}
37
-
38
- # Make sure the "assigns" from the controller are available (instance variables)
39
- if view
40
- view.assigns.each do |key, value|
41
- instance_variable_set("@#{key}", value)
42
- end
43
-
44
- # copy view instance variables over
45
- view.instance_variables.each do |iv|
46
- instance_variable_set(iv, view.instance_variable_get(iv))
47
- end
48
- end
49
- end
50
- end
51
-
52
- for attr in [:erb_binding, :part_contexts, :view_name,
53
- :this, :this_parent, :this_field, :this_key,
54
- :form_this, :form_field_path, :form_field_names]
55
- class_eval "def #{attr}; @_#{attr}; end"
56
- end
57
-
58
- def path_for_form_field
59
- @_form_field_path.nil? and raise Hobo::Dryml::DrymlException,
60
- "DRYML cannot provide the correct form-field name here (this_field = #{this_field.inspect}, this = #{this.inspect})"
61
- @_form_field_path
62
- end
63
-
64
-
65
- def this_key=(key)
66
- @_this_key = key
67
- end
68
-
69
-
70
- # The type of this, or when this is nil, the type that would be expected in the current field
71
- def this_type
72
- @_this_type ||= if this == false || this == true
73
- Hobo::Boolean
74
- elsif this
75
- this.class
76
- elsif this_parent && this_field && (parent_class = this_parent.class).respond_to?(:attr_type)
77
- type = parent_class.attr_type(this_field)
78
- if type.is_a?(ActiveRecord::Reflection::AssociationReflection)
79
- reflection = type
80
- if reflection.macro == :has_many
81
- Array
82
- elsif reflection.options[:polymorphic]
83
- # All we know is that it will be some active-record type
84
- ActiveRecord::Base
85
- else
86
- reflection.klass
87
- end
88
- else
89
- type
90
- end
91
- else
92
- # Nothing to go on at all
93
- Object
94
- end
95
- end
96
-
97
-
98
- def this_field_reflection
99
- this.try.proxy_reflection ||
100
- (this_parent && this_field && this_parent.class.respond_to?(:reflections) && this_parent.class.reflections[this_field.to_sym])
101
- end
102
-
103
-
104
- def attrs_for(name)
105
- self.class.tag_attrs[name.to_sym]
106
- end
107
-
108
-
109
- def add_classes!(attributes, *classes)
110
- classes = classes.flatten.select{|x|x}
111
- current = attributes[:class]
112
- attributes[:class] = (current ? current.split + classes : classes).uniq.join(' ')
113
- attributes
114
- end
115
-
116
-
117
- def add_classes(attributes, *classes)
118
- add_classes!(HashWithIndifferentAccess.new(attributes), classes)
119
- end
120
-
121
-
122
- def merge_attrs(attrs, overriding_attrs)
123
- return {}.update(attrs) if overriding_attrs.nil?
124
- attrs = attrs.with_indifferent_access unless attrs.is_a?(HashWithIndifferentAccess)
125
- classes = overriding_attrs[:class]
126
- attrs = add_classes(attrs, *classes.split) if classes
127
- attrs.update(overriding_attrs - [:class])
128
- end
129
-
130
-
131
- def scope
132
- @_scoped_variables
133
- end
134
-
135
-
136
- def typed_id(object=nil, attribute=nil)
137
- if object.nil?
138
- # nothing passed -- use context
139
- if this_parent && this_field && !this_parent.respond_to?(:member_class)
140
- object, attribute = this_parent, this_field
141
- else
142
- object = this
143
- end
144
- end
145
-
146
- id = if (typed_id = object.try.typed_id)
147
- typed_id
148
- elsif object == @this
149
- "this"
150
- end
151
- attribute ? "#{id}:#{attribute}" : id
152
- end
153
-
154
-
155
- def call_part(part_node_id, part_name, *locals)
156
- res = ''
157
- new_context do
158
- @_part_contexts[part_node_id] = PartContext.for_call(part_name, self, locals)
159
- res = send("#{part_name}_part", *locals)
160
- end
161
- res
162
- end
163
-
164
-
165
- def refresh_part(encoded_context, session, dom_id)
166
- context = Hobo::Dryml::PartContext.for_refresh(encoded_context, @this, session)
167
-
168
- with_part_context(context) do
169
- send("#{context.part_name}_part", *context.locals)
170
- end
171
- end
172
-
173
-
174
- def with_part_context(context, &block)
175
- this, this_field = context.this, context.this_field
176
-
177
- b = if context.form_field_path
178
- proc { with_form_context(:unknown, context.form_field_path, &block) }
179
- else
180
- block
181
- end
182
-
183
- if this && this_field
184
- new_object_context(this) { new_field_context(this_field, &b) }
185
- elsif this
186
- new_object_context(this, &b)
187
- else
188
- new_context(&b)
189
- end
190
- end
191
-
192
-
193
- def call_polymorphic_tag(name, *args)
194
- name = name.to_s.gsub('-', '_')
195
- type = args.first.is_a?(Class) ? args.shift : nil
196
- attributes, parameters = args
197
-
198
- tag = find_polymorphic_tag(name, type)
199
- if tag != name
200
- send(tag, attributes || {}, parameters || {})
201
- else
202
- block_given? ? yield : nil
203
- end
204
- end
205
-
206
-
207
- def find_polymorphic_tag(name, call_type=nil)
208
- call_type ||= (this.respond_to?(:member_class) && this.member_class) || this_type
209
-
210
- begin
211
- found = nil
212
- while true
213
- if respond_to?(poly_name = "#{name}__for_#{call_type.name.to_s.underscore.gsub('/', '__')}")
214
- found = poly_name
215
- break
216
- else
217
- if call_type == ActiveRecord::Base || call_type == Object
218
- found = name
219
- break
220
- else
221
- call_type = call_type.superclass
222
- end
223
- end
224
- end
225
- found
226
- end
227
- end
228
-
229
-
230
- def repeat_attribute(string_or_array)
231
- res = nil
232
- if string_or_array.instance_of?(String)
233
- new_field_context(string_or_array) do
234
- res = context_map { yield }
235
- end
236
- else
237
- res = context_map(string_or_array) { yield }
238
- end
239
- res.join
240
- end
241
-
242
-
243
- def new_context
244
- ctx = [ @_this, @_this_parent, @_this_field, @_this_type,
245
- @_form_field_path, @_form_field_paths_by_object ]
246
- @_this_type = nil
247
- res = nil
248
- @view.with_output_buffer { res = yield }
249
- @_this, @_this_parent, @_this_field, @_this_type, @_form_field_path, @_form_field_paths_by_object = ctx
250
- res.to_s
251
- end
252
-
253
-
254
- def new_object_context(new_this)
255
- new_context do
256
- if new_this.respond_to?(:origin)
257
- @_this_parent, @_this_field = new_this.origin, new_this.origin_attribute
258
- else
259
- @_this_parent, @_this_field = [nil, nil]
260
- end
261
- @_this = new_this
262
-
263
- # We might have lost track of where 'this' is relative to the form_this
264
- # check if this or this_parent are objects we've seen before in this form
265
- @_form_field_path = find_form_field_path(new_this) if @_form_field_path
266
-
267
- yield
268
- end
269
- end
270
-
271
-
272
- def new_field_context(field_path, new_this=nil)
273
- new_context do
274
- path = if field_path.is_a? String
275
- field_path.split('.')
276
- else
277
- Array(field_path)
278
- end
279
- if new_this
280
- raise ArgumentError, "invlaid context change" unless path.length == 1
281
- @_this_parent, @_this_field, @_this = this, path.first, new_this
282
- else
283
- parent, field, obj = Hobo.get_field_path(this, path)
284
- @_this, @_this_parent, @_this_field = obj, parent, field
285
- end
286
-
287
- if @_form_field_path
288
- @_form_field_path += path
289
- @_form_field_paths_by_object[@_this] = @_form_field_path
290
- end
291
-
292
- yield
293
- end
294
- end
295
-
296
-
297
- def find_form_field_path(object)
298
- back = []
299
- while object
300
- path = @_form_field_paths_by_object[object]
301
- if path
302
- path = path + back unless back.empty?
303
- return path
304
- end
305
- if object.respond_to? :origin
306
- back.unshift object.origin_attribute
307
- object = object.origin
308
- else
309
- return nil
310
- end
311
- end
312
- end
313
-
314
-
315
-
316
-
317
- def _tag_context(attributes)
318
- with = attributes[:with]
319
- field = attributes[:field]
320
-
321
- if with && field
322
- new_object_context(with) { new_field_context(field) { yield } }
323
- elsif field
324
- new_field_context(field) { yield }
325
- elsif attributes.has_key?(:with)
326
- new_object_context(with) { yield }
327
- else
328
- new_context { yield }
329
- end
330
- end
331
-
332
-
333
- def with_form_context(form_this=this, form_field_path=[form_this.class.name.underscore])
334
- @_form_this = form_this
335
- @_form_field_path = form_field_path
336
- @_form_field_paths_by_object = { form_this => form_field_path }
337
- res = scope.new_scope :in_form => true, :form_field_names => [] do
338
- yield
339
- end
340
- @_form_this = @_form_field_path = @_form_field_paths_by_object = nil
341
- res
342
- end
343
-
344
-
345
- def register_form_field(name)
346
- scope.form_field_names << name
347
- end
348
-
349
-
350
- def _tag_locals(attributes, locals)
351
- attributes.symbolize_keys!
352
- #ensure with and field are not in attributes
353
- attributes.delete(:with)
354
- attributes.delete(:field)
355
-
356
- # declared attributes don't appear in the attributes hash
357
- stripped_attributes = HashWithIndifferentAccess.new.update(attributes)
358
- locals.each {|a| stripped_attributes.delete(a.to_sym) }
359
-
360
- # Return locals declared as local variables (attrs="...")
361
- locals.map {|a| attributes[a.to_sym]} + [stripped_attributes]
362
- end
363
-
364
-
365
- def call_tag_parameter_with_default_content(the_tag, attributes, default_content, overriding_content_proc)
366
- if the_tag.is_one_of?(String, Symbol) && the_tag.to_s.in?(Hobo.static_tags)
367
- body = if overriding_content_proc
368
- new_context { overriding_content_proc.call(proc { default_content._?.call(nil) }) }
369
- elsif default_content
370
- new_context { default_content.call(nil) }
371
- else
372
- nil
373
- end
374
- element(the_tag, attributes, body)
375
- else
376
- d = if overriding_content_proc
377
- proc { |default| overriding_content_proc.call(proc { default_content._?.call(default) }) }
378
- else
379
- proc { |default| default_content._?.call(default) }
380
- end
381
- send(the_tag, attributes, { :default => d })
382
- end
383
- end
384
-
385
-
386
- def call_tag_parameter(the_tag, attributes, parameters, caller_parameters, param_name)
387
- overriding_proc = caller_parameters[param_name]
388
- replacing_proc = caller_parameters[:"#{param_name}_replacement"]
389
-
390
- unless param_name == the_tag || param_name == :default
391
- classes = attributes[:class]
392
- param_class = param_name.to_s.gsub('_', '-')
393
- attributes[:class] = if classes
394
- classes =~ /\b#{param_class}\b/ ? classes : "#{classes} #{param_class}"
395
- else
396
- param_class
397
- end
398
- end
399
-
400
- if param_name == :default && overriding_proc && overriding_proc.arity>0
401
- # :default content is handled specially
402
-
403
- call_tag_parameter_with_default_content(the_tag, attributes, parameters[:default], overriding_proc)
404
-
405
- elsif replacing_proc
406
- # The caller is replacing this parameter. Don't call the tag
407
- # at all, just the overriding proc, but pass the restorable
408
- # tag as a parameter to the overriding proc
409
-
410
- tag_restore = proc do |restore_attrs, restore_params|
411
- # Call the replaced tag with the attributes and parameters
412
- # as given in the original tag definition, and with the
413
- # specialisation given on the 'restore' call
414
-
415
- if overriding_proc
416
- overriding_attributes, overriding_parameters = overriding_proc.call
417
- restore_attrs = overriding_attributes.merge(restore_attrs)
418
- restore_params = overriding_parameters.merge(restore_params)
419
- end
420
-
421
- override_and_call_tag(the_tag, attributes, parameters, restore_attrs, restore_params)
422
- end
423
- replacing_proc.call(tag_restore)
424
-
425
- else
426
- overriding_attributes, overriding_parameters = overriding_proc._?.call
427
- override_and_call_tag(the_tag, attributes, parameters, overriding_attributes, overriding_parameters)
428
- end
429
- end
430
-
431
-
432
- def override_and_call_tag(the_tag, general_attributes, general_parameters, overriding_attributes, overriding_parameters)
433
- attributes = overriding_attributes ? merge_attrs(general_attributes, overriding_attributes) : general_attributes
434
- if overriding_parameters
435
- overriding_default_content = overriding_parameters.delete(:default)
436
- parameters = general_parameters.merge(overriding_parameters)
437
- else
438
- parameters = general_parameters
439
- end
440
-
441
- default_content = parameters[:default]
442
-
443
- if the_tag.is_one_of?(String, Symbol) && the_tag.to_s.in?(Hobo.static_tags)
444
- body = if overriding_default_content
445
- new_context { overriding_default_content.call(proc { default_content.call(nil) if default_content }) }
446
- elsif default_content
447
- new_context { default_content.call(nil) }
448
- else
449
- nil
450
- end
451
- element(the_tag, attributes, body)
452
- else
453
- if default_content || overriding_default_content
454
- d = if overriding_default_content
455
- proc { |default| overriding_default_content.call(proc { default_content.call(default) if default_content }) }
456
- else
457
- proc { |default| default_content.call(default) if default_content }
458
- end
459
- parameters = parameters.merge(:default => d)
460
- end
461
-
462
- if the_tag.is_one_of?(String, Symbol)
463
- # It's a defined DRYML tag
464
- send(the_tag, attributes, parameters)
465
- else
466
- # It's a proc - restoring a replaced parameter
467
- the_tag.call(attributes, parameters)
468
- end
469
- end
470
- end
471
-
472
-
473
- # This method is used where 'param' is declared on a tag that is
474
- # itself a parameter tag. Takes two procs that each return a pair
475
- # of hashes (attributes and parameters). Returns a single proc
476
- # that also returns a pair of hashes - the merged atributes and
477
- # parameters.
478
- def merge_tag_parameter(general_proc, overriding_proc)
479
- if overriding_proc.nil?
480
- general_proc
481
- else
482
- if overriding_proc.arity == 1
483
- # The override is a replace parameter - just pass it on
484
- overriding_proc
485
- else
486
- proc do
487
- overriding_attrs, overriding_parameters = overriding_proc.call
488
- general_attrs, general_parameters = general_proc.call
489
-
490
- attrs = merge_attrs(general_attrs, overriding_attrs)
491
- overriding_default = overriding_parameters.delete(:default)
492
- params = merge_parameter_hashes(general_parameters, overriding_parameters)
493
-
494
- # The overrider should provide its :default as the new
495
- # 'default_content'
496
- if overriding_default
497
- params[:default] =
498
- if general_parameters[:default]
499
- proc do |default|
500
- overriding_default.call(proc { new_context { concat(general_parameters[:default].call(default)) } } )
501
- end
502
- else
503
- proc do |default|
504
- overriding_default.call(default)
505
- end
506
- end
507
- end
508
-
509
- [attrs, params]
510
- end
511
- end
512
- end
513
- end
514
-
515
-
516
- def merge_parameter_hashes(given_parameters, overriding_parameters)
517
- to_merge = given_parameters.keys & overriding_parameters.keys
518
- no_merge = overriding_parameters.keys - to_merge
519
- result = given_parameters.dup
520
-
521
- no_merge.each { |k| result[k] = overriding_parameters[k] }
522
- to_merge.each { |k| result[k] = merge_tag_parameter(given_parameters[k], overriding_parameters[k])}
523
-
524
- result
525
- end
526
-
527
-
528
-
529
-
530
- def part_contexts_javascripts
531
- storage = part_contexts_storage
532
- storage.blank? ? "" : "<script type=\"text/javascript\">\n#{storage}</script>\n"
533
- end
534
-
535
-
536
- def part_contexts_storage
537
- PartContext.client_side_storage(@_part_contexts, session)
538
- end
539
-
540
-
541
- def render_tag(tag_name, attributes)
542
- method_name = tag_name.to_s.gsub('-', '_')
543
- if respond_to?(method_name)
544
- res = send(method_name, attributes).strip
545
-
546
- # TODO: Temporary hack to get the dryml metadata comments in the right place
547
- if false && RAILS_ENV == "development"
548
- res.gsub(/^(.*?)(<!DOCTYPE.*?>).*?(<html.*?>)/m, "\\2\\3\\1")
549
- else
550
- res
551
- end
552
- else
553
- false
554
- end
555
- end
556
-
557
-
558
- def element(name, attributes, content=nil, escape = true, empty = false, &block)
559
- unless attributes.blank?
560
- attrs = []
561
- if escape
562
- attributes.each do |key, value|
563
- next unless value
564
- key = key.to_s.gsub("_", "-")
565
-
566
- value = if ActionView::Helpers::TagHelper::BOOLEAN_ATTRIBUTES.include?(key)
567
- key
568
- else
569
- # escape once
570
- value.to_s.gsub(/[\"><]|&(?!([a-zA-Z]+|(#\d+));)/) { |special| ERB::Util::HTML_ESCAPE[special] }
571
- end
572
- attrs << %(#{key}="#{value}")
573
- end
574
-
575
- else
576
- attrs = options.map do |key, value|
577
- key = key.to_s.gsub("_", "-")
578
- %(#{key}="#{value}")
579
- end
580
- end
581
- attr_string = " #{attrs.sort * ' '}" unless attrs.empty?
582
- end
583
-
584
- content = new_context { yield } if block_given?
585
- res = if empty
586
- "<#{name}#{attr_string}#{scope.xmldoctype ? ' /' : ''}>"
587
- else
588
- "<#{name}#{attr_string}>#{content}</#{name}>"
589
- end
590
- if block_called_from_erb? block
591
- concat res
592
- else
593
- res
594
- end
595
- end
596
-
597
-
598
- def session
599
- @view ? @view.session : {}
600
- end
601
-
602
-
603
- def method_missing(name, *args, &b)
604
- if @view
605
- @view.send(name, *args, &b)
606
- else
607
- raise NoMethodError, name.to_s
608
- end
609
- end
610
-
611
- end
612
-
613
- end