hobo 0.5.3

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.
Files changed (125) hide show
  1. data/LICENSE.txt +22 -0
  2. data/README.txt +18 -0
  3. data/bin/hobo +81 -0
  4. data/hobo_files/plugin/CHANGES.txt +963 -0
  5. data/hobo_files/plugin/LICENSE.txt +22 -0
  6. data/hobo_files/plugin/README +4 -0
  7. data/hobo_files/plugin/Rakefile +11 -0
  8. data/hobo_files/plugin/generators/hobo/hobo_generator.rb +37 -0
  9. data/hobo_files/plugin/generators/hobo/templates/application.dryml +2 -0
  10. data/hobo_files/plugin/generators/hobo/templates/guest.rb +31 -0
  11. data/hobo_files/plugin/generators/hobo_front_controller/USAGE +11 -0
  12. data/hobo_files/plugin/generators/hobo_front_controller/hobo_front_controller_generator.rb +90 -0
  13. data/hobo_files/plugin/generators/hobo_front_controller/templates/controller.rb +51 -0
  14. data/hobo_files/plugin/generators/hobo_front_controller/templates/functional_test.rb +18 -0
  15. data/hobo_files/plugin/generators/hobo_front_controller/templates/helper.rb +2 -0
  16. data/hobo_files/plugin/generators/hobo_front_controller/templates/index.dryml +43 -0
  17. data/hobo_files/plugin/generators/hobo_front_controller/templates/login.dryml +44 -0
  18. data/hobo_files/plugin/generators/hobo_front_controller/templates/search.dryml +18 -0
  19. data/hobo_files/plugin/generators/hobo_front_controller/templates/signup.dryml +45 -0
  20. data/hobo_files/plugin/generators/hobo_model/USAGE +26 -0
  21. data/hobo_files/plugin/generators/hobo_model/hobo_model_generator.rb +38 -0
  22. data/hobo_files/plugin/generators/hobo_model/templates/fixtures.yml +11 -0
  23. data/hobo_files/plugin/generators/hobo_model/templates/migration.rb +13 -0
  24. data/hobo_files/plugin/generators/hobo_model/templates/model.rb +24 -0
  25. data/hobo_files/plugin/generators/hobo_model/templates/unit_test.rb +10 -0
  26. data/hobo_files/plugin/generators/hobo_model_controller/USAGE +30 -0
  27. data/hobo_files/plugin/generators/hobo_model_controller/hobo_model_controller_generator.rb +43 -0
  28. data/hobo_files/plugin/generators/hobo_model_controller/templates/controller.rb +5 -0
  29. data/hobo_files/plugin/generators/hobo_model_controller/templates/functional_test.rb +18 -0
  30. data/hobo_files/plugin/generators/hobo_model_controller/templates/helper.rb +2 -0
  31. data/hobo_files/plugin/generators/hobo_model_controller/templates/view.rhtml +2 -0
  32. data/hobo_files/plugin/generators/hobo_rapid/hobo_rapid_generator.rb +51 -0
  33. data/hobo_files/plugin/generators/hobo_rapid/templates/hobo_rapid.js +436 -0
  34. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/default_mapping.rb +11 -0
  35. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/banner.gif +0 -0
  36. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/bkg_bodytop.gif +0 -0
  37. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/bkg_corner_01.gif +0 -0
  38. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/bkg_corner_02.gif +0 -0
  39. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/bkg_corner_03.gif +0 -0
  40. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/bkg_corner_04.gif +0 -0
  41. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/bkg_shadow_bottom.gif +0 -0
  42. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/bkg_shadow_left.gif +0 -0
  43. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/bkg_shadow_right.gif +0 -0
  44. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/bkg_shadow_top.gif +0 -0
  45. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/header_blue.gif +0 -0
  46. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/header_dblue.gif +0 -0
  47. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/header_green.gif +0 -0
  48. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/header_purple.gif +0 -0
  49. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/header_red.gif +0 -0
  50. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/logo.gif +0 -0
  51. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/spinner.gif +0 -0
  52. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/txt_list_img_dblue.gif +0 -0
  53. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/txt_list_img_green.gif +0 -0
  54. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/txt_list_img_purple.gif +0 -0
  55. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/txt_list_img_red.gif +0 -0
  56. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/window_corner_01.gif +0 -0
  57. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/window_corner_02.gif +0 -0
  58. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/window_corner_03.gif +0 -0
  59. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/window_corner_04.gif +0 -0
  60. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/window_shadow_bottom.gif +0 -0
  61. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/window_shadow_left.gif +0 -0
  62. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/window_shadow_right.gif +0 -0
  63. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/window_shadow_top.gif +0 -0
  64. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/stylesheets/application.css +390 -0
  65. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/views/application.dryml +104 -0
  66. data/hobo_files/plugin/generators/hobo_user_model/USAGE +26 -0
  67. data/hobo_files/plugin/generators/hobo_user_model/hobo_user_model_generator.rb +38 -0
  68. data/hobo_files/plugin/generators/hobo_user_model/templates/fixtures.yml +11 -0
  69. data/hobo_files/plugin/generators/hobo_user_model/templates/migration.rb +15 -0
  70. data/hobo_files/plugin/generators/hobo_user_model/templates/model.rb +58 -0
  71. data/hobo_files/plugin/generators/hobo_user_model/templates/unit_test.rb +10 -0
  72. data/hobo_files/plugin/init.rb +44 -0
  73. data/hobo_files/plugin/lib/action_view_extensions/base.rb +14 -0
  74. data/hobo_files/plugin/lib/active_record/has_many_association.rb +54 -0
  75. data/hobo_files/plugin/lib/active_record/has_many_through_association.rb +22 -0
  76. data/hobo_files/plugin/lib/active_record/table_definition.rb +34 -0
  77. data/hobo_files/plugin/lib/extensions.rb +245 -0
  78. data/hobo_files/plugin/lib/extensions/test_case.rb +130 -0
  79. data/hobo_files/plugin/lib/hobo.rb +353 -0
  80. data/hobo_files/plugin/lib/hobo/HtmlString +3 -0
  81. data/hobo_files/plugin/lib/hobo/authenticated_user.rb +106 -0
  82. data/hobo_files/plugin/lib/hobo/authentication_support.rb +108 -0
  83. data/hobo_files/plugin/lib/hobo/composite_model.rb +66 -0
  84. data/hobo_files/plugin/lib/hobo/controller.rb +134 -0
  85. data/hobo_files/plugin/lib/hobo/controller_helpers.rb +135 -0
  86. data/hobo_files/plugin/lib/hobo/core.rb +475 -0
  87. data/hobo_files/plugin/lib/hobo/define_tags.rb +56 -0
  88. data/hobo_files/plugin/lib/hobo/dryml.rb +161 -0
  89. data/hobo_files/plugin/lib/hobo/dryml/dryml_builder.rb +126 -0
  90. data/hobo_files/plugin/lib/hobo/dryml/tag_module.rb +9 -0
  91. data/hobo_files/plugin/lib/hobo/dryml/taglib.rb +57 -0
  92. data/hobo_files/plugin/lib/hobo/dryml/template.rb +586 -0
  93. data/hobo_files/plugin/lib/hobo/dryml/template_environment.rb +302 -0
  94. data/hobo_files/plugin/lib/hobo/dryml/template_handler.rb +19 -0
  95. data/hobo_files/plugin/lib/hobo/generator.rb +25 -0
  96. data/hobo_files/plugin/lib/hobo/html_string.rb +3 -0
  97. data/hobo_files/plugin/lib/hobo/lazy_hash.rb +28 -0
  98. data/hobo_files/plugin/lib/hobo/mapping_tags.rb +262 -0
  99. data/hobo_files/plugin/lib/hobo/markdown_string.rb +7 -0
  100. data/hobo_files/plugin/lib/hobo/model.rb +391 -0
  101. data/hobo_files/plugin/lib/hobo/model_controller.rb +676 -0
  102. data/hobo_files/plugin/lib/hobo/model_queries.rb +92 -0
  103. data/hobo_files/plugin/lib/hobo/model_support.rb +44 -0
  104. data/hobo_files/plugin/lib/hobo/password_string.rb +3 -0
  105. data/hobo_files/plugin/lib/hobo/predicate_dispatch.rb +78 -0
  106. data/hobo_files/plugin/lib/hobo/proc_binding.rb +32 -0
  107. data/hobo_files/plugin/lib/hobo/rapid.rb +447 -0
  108. data/hobo_files/plugin/lib/hobo/static_tags +92 -0
  109. data/hobo_files/plugin/lib/hobo/text.rb +3 -0
  110. data/hobo_files/plugin/lib/hobo/textile_string.rb +13 -0
  111. data/hobo_files/plugin/lib/hobo/undefined.rb +41 -0
  112. data/hobo_files/plugin/lib/hobo/undefined_access_error.rb +5 -0
  113. data/hobo_files/plugin/lib/hobo/where_fragment.rb +23 -0
  114. data/hobo_files/plugin/lib/rexml.rb +345 -0
  115. data/hobo_files/plugin/tags/core.dryml +6 -0
  116. data/hobo_files/plugin/tags/rapid.dryml +177 -0
  117. data/hobo_files/plugin/tags/rapid_editing.dryml +168 -0
  118. data/hobo_files/plugin/tags/rapid_navigation.dryml +95 -0
  119. data/hobo_files/plugin/tags/rapid_pages.dryml +175 -0
  120. data/hobo_files/plugin/tasks/environments.rake +19 -0
  121. data/hobo_files/plugin/tasks/hobo_tasks.rake +4 -0
  122. data/hobo_files/plugin/test/hobo_dryml_template_test.rb +7 -0
  123. data/hobo_files/plugin/test/hobo_test.rb +7 -0
  124. data/hobo_files/plugin/uninstall.rb +1 -0
  125. metadata +206 -0
@@ -0,0 +1,302 @@
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
+ end
11
+
12
+ for mod in ActionView::Helpers.constants.grep(/Helper$/).map {|m| ActionView::Helpers.const_get(m)}
13
+ include mod
14
+ end
15
+
16
+ def initialize(view_name, view)
17
+ @view = view
18
+ @view_name = view_name
19
+ @erb_binding = binding
20
+ @part_contexts = {}
21
+ @stack = [nil]
22
+
23
+ # Make sure the "assigns" from the controller are available (instance variables)
24
+ if view
25
+ view.assigns.each do |key, value|
26
+ instance_variable_set("@#{key}", value)
27
+ end
28
+
29
+ # copy view instance variables over
30
+ view.instance_variables.each do |iv|
31
+ instance_variable_set(iv, @view.instance_variable_get(iv))
32
+ end
33
+ end
34
+ end
35
+
36
+ attr_accessor :erb_binding, :part_contexts, :view_name
37
+
38
+ attr_reader :this_parent, :this_field, :this_type, :form_field_path, :form_this, :form_field_names
39
+
40
+ def this; @_this; end
41
+
42
+ def attr_extension(s)
43
+ AttributeExtensionString.new(s)
44
+ end
45
+
46
+
47
+ def this_field_dom_id
48
+ Hobo.dom_id(this_parent, this_field)
49
+ end
50
+
51
+
52
+ def part_context_model_id
53
+ if this_parent and this_parent.is_a?(ActiveRecord::Base) and this_field
54
+ this_field_dom_id
55
+ elsif this.respond_to?(:typed_id)
56
+ this.typed_id
57
+ elsif this.is_a?(Array) and !this.respond_to?(:proxy_reflection)
58
+ "nil"
59
+ else
60
+ Hobo.dom_id(this)
61
+ end
62
+ end
63
+
64
+
65
+ def call_part(dom_id, part_id, part_this=nil)
66
+ res = ''
67
+ if part_this
68
+ new_object_context(part_this) do
69
+ @part_contexts[dom_id] = [part_id, part_context_model_id]
70
+ res = send("#{part_id}_part")
71
+ end
72
+ else
73
+ new_context do
74
+ @part_contexts[dom_id] = [part_id, part_context_model_id]
75
+ res = send("#{part_id}_part")
76
+ end
77
+ end
78
+ res
79
+ end
80
+
81
+
82
+ def _erbout
83
+ @output
84
+ end
85
+
86
+
87
+ def new_context
88
+ ctx = @output, @_this, @this_parent, @this_field, @this_type, @form_field_path
89
+ @output = ""
90
+ res = yield
91
+ @output, @_this, @this_parent, @this_field, @this_type, @form_field_path = ctx
92
+ res.to_s
93
+ end
94
+
95
+
96
+ def new_object_context(new_this)
97
+ new_context do
98
+ @this_parent,@this_field,@this_type = if new_this.respond_to?(:proxy_reflection)
99
+ refl = new_this.proxy_reflection
100
+ [new_this.proxy_owner, refl.name, refl]
101
+ else
102
+ [nil, nil, new_this.class]
103
+ end
104
+ @_this = new_this
105
+ yield
106
+ end
107
+ end
108
+
109
+
110
+ def new_field_context(field_path, tag_this=nil)
111
+ new_context do
112
+ path = if field_path.is_a? Array
113
+ field_path
114
+ elsif field_path.is_a? String
115
+ field_path.split('.')
116
+ else
117
+ [field_path]
118
+ end
119
+
120
+ obj = tag_this || this
121
+ for field in path
122
+ parent = obj
123
+ obj = Hobo.get_field(parent, field)
124
+ end
125
+
126
+ type = if (obj.nil? or obj.respond_to?(:proxy_reflection)) and
127
+ parent.class.respond_to?(:field_type) and field_type = parent.class.field_type(field)
128
+ field_type
129
+ else
130
+ obj.class
131
+ end
132
+
133
+
134
+ @_this, @this_parent, @this_field, @this_type = obj, parent, field, type
135
+ @form_field_path += path if @form_field_path
136
+ yield
137
+ end
138
+ end
139
+
140
+
141
+ def _tag_context(options, tagbody_proc)
142
+ tagbody = tagbody_proc && proc do |*args|
143
+ res = ''
144
+ block_options = args.length > 0 && args.first
145
+ if block_options and block_options.has_key?(:obj)
146
+ new_object_context(block_options[:obj]) { res = tagbody_proc.call }
147
+ elsif block_options and block_options.has_key?(:attr)
148
+ new_field_context(block_options[:attr]) { res = tagbody_proc.call }
149
+ else
150
+ new_context { res = tagbody_proc.call }
151
+ end
152
+ res
153
+ end
154
+
155
+ obj = options[:obj] == "page" ? @this : options[:obj]
156
+
157
+ if options.has_key?(:attr)
158
+ new_field_context(options[:attr], obj) { yield tagbody }
159
+ elsif options.has_key?(:obj)
160
+ new_object_context(obj) { yield tagbody }
161
+ else
162
+ new_context { yield tagbody }
163
+ end
164
+ end
165
+
166
+
167
+ def with_form_context
168
+ @form_this = this
169
+ @form_field_path = []
170
+ @form_field_names = []
171
+ res = yield
172
+ field_names = @form_field_names
173
+ @form_this = @form_field_path = @form_field_names = nil
174
+ [res, field_names]
175
+ end
176
+
177
+
178
+ def register_form_field(name)
179
+ @form_field_names << name
180
+ end
181
+
182
+
183
+ def part_contexts_js
184
+ return "" if part_contexts.empty?
185
+
186
+ assigns = part_contexts.map do |dom_id, p|
187
+ part_id, model_id = p
188
+ "hoboParts.#{dom_id} = ['#{part_id}', '#{model_id}']\n"
189
+ end
190
+
191
+ "<script>\nvar hoboParts = {}\n" + assigns.join + "</script>\n"
192
+ end
193
+
194
+
195
+ def _tag_locals(options, attrs, inner_tag_names)
196
+ options = Hobo::Dryml.hashify_options(options)
197
+ options.symbolize_keys!
198
+ #ensure obj and attr are not in options
199
+ options.delete(:obj)
200
+ options.delete(:attr)
201
+
202
+ inner_tag_options, options = options.partition_hash(inner_tag_names.omap{to_sym})
203
+
204
+ # positional arguments never appear in the options hash
205
+ stripped_options = {}.update(options)
206
+ attrs.each {|a| stripped_options.delete(a.to_sym) }
207
+ attrs.map {|a| options[a.to_sym]} + [stripped_options, inner_tag_options]
208
+ end
209
+
210
+
211
+ def call_replaceable_tag(name, options, external_param, &b)
212
+ options.delete(:replace_option)
213
+
214
+ if external_param.is_a? Hash
215
+ before = external_param.delete(:before_content)
216
+ after = external_param.delete(:after_content)
217
+ top = external_param.delete(:top_content)
218
+ bottom = external_param.delete(:bottom_content)
219
+ content = external_param.delete(:content)
220
+ options = Hobo::Dryml.merge_tag_options(options, external_param)
221
+ elsif !external_param.nil?
222
+ return external_param.to_s
223
+ end
224
+
225
+ tag = if respond_to?(name)
226
+ body = if content
227
+ proc { content }
228
+ elsif b && (top || bottom)
229
+ proc { top.to_s + b.call + bottom.to_s }
230
+ else
231
+ b
232
+ end
233
+ send(name, options, &body)
234
+ else
235
+ body = if content
236
+ content
237
+ elsif b
238
+ top.to_s + new_context { b.call } + bottom.to_s
239
+ else
240
+ top.to_s + bottom.to_s
241
+ end
242
+ content_tag(name, body, options)
243
+ end
244
+ before.to_s + tag.to_s + after.to_s
245
+ end
246
+
247
+
248
+ def call_replaceable_content_tag(name, options, external_param, &b)
249
+ options.delete(:content_option)
250
+
251
+ if external_param.is_a? Hash
252
+ content = external_param.delete(:content)
253
+ top = external_param.delete(:top_content)
254
+ bottom = external_param.delete(:bottom_content)
255
+ external_param.delete(:before_content)
256
+ external_param.delete(:after_content)
257
+ options = Hobo::Dryml.merge_tag_options(options, external_param)
258
+ elsif !external_param.nil?
259
+ content = external_param.to_s
260
+ end
261
+
262
+ # If there's no body, and no content provided externally, remove
263
+ # the tag altogether
264
+ return if b.nil? and content.nil?
265
+
266
+ tag = if respond_to?(name)
267
+ body = if content
268
+ proc { content }
269
+ elsif b && (top || bottom)
270
+ proc { top.to_s + b.call + bottom.to_s }
271
+ else
272
+ b
273
+ end
274
+ send(name, options, &body)
275
+ else
276
+ body = if content
277
+ content
278
+ elsif b
279
+ top.to_s + new_context { b.call } + bottom.to_s
280
+ else
281
+ top.to_s + bottom.to_s
282
+ end
283
+ content_tag(name, body, options)
284
+ end
285
+ tag.to_s
286
+ end
287
+
288
+
289
+ def render_tag(tag_name, options)
290
+ (send(tag_name, options) + part_contexts_js).strip
291
+ end
292
+
293
+
294
+ def method_missing(name, *args)
295
+ @view.send(name, *args)
296
+ end
297
+
298
+
299
+ end
300
+
301
+
302
+ end
@@ -0,0 +1,19 @@
1
+ module Hobo::Dryml
2
+
3
+ class TemplateHandler
4
+
5
+ def initialize(view)
6
+ @view = view
7
+ end
8
+
9
+ def render(src, local_assigns)
10
+ renderer = Hobo::Dryml.page_renderer(@view, local_assigns.keys)
11
+ s = renderer.render_page(@view.assigns["this"] || local_assigns[:this], local_assigns)
12
+ # Important to strip whitespace, or the browser hangs around for ages (FF2)
13
+
14
+ s.strip
15
+ end
16
+
17
+ end
18
+
19
+ end
@@ -0,0 +1,25 @@
1
+ require 'find'
2
+
3
+ class Hobo::Generator < Rails::Generator::Base
4
+
5
+ def with_source_in(path)
6
+ root = source_path(path)
7
+ Find.find(root) do |f|
8
+ Find.prune if File.basename(f) == ".svn"
9
+ full_path = f[(source_root.length)..-1]
10
+ rel_path = f[(root.length)..-1]
11
+ yield full_path, rel_path
12
+ end
13
+ end
14
+
15
+ def create_all(m, src, dest)
16
+ with_source_in(src) do |full, rel|
17
+ if File.directory?(source_path(full))
18
+ m.directory File.join(dest, rel)
19
+ else
20
+ m.file full, File.join(dest, rel)
21
+ end
22
+ end
23
+ end
24
+
25
+ end
@@ -0,0 +1,3 @@
1
+ class Hobo::HtmlString < String
2
+
3
+ end
@@ -0,0 +1,28 @@
1
+ class Hobo::LazyHash < Hash
2
+
3
+ def initialize(hash=nil)
4
+ if hash.is_a?(Hash)
5
+ update(hash)
6
+ else
7
+ super
8
+ end
9
+ end
10
+
11
+ def [](key)
12
+ val = super
13
+ if val.is_a?(Proc)
14
+ self[val] = val.call
15
+ else
16
+ val
17
+ end
18
+ end
19
+
20
+ def inspect
21
+ "#<LazyHash:#{object_id} #{keys.inspect}>"
22
+ end
23
+
24
+ def to_s
25
+ inspect
26
+ end
27
+
28
+ end
@@ -0,0 +1,262 @@
1
+ module Hobo
2
+ module MappingTags
3
+
4
+ class InvalidMappingError < RuntimeError; end
5
+
6
+ class TagOrSelector
7
+
8
+ def initialize(name, all_mappings)
9
+ @_name = name
10
+ @_dot_names = []
11
+ @_selectors = []
12
+
13
+ # Keep a reference to the set of mappings for the whole
14
+ # mapping_tags do ... end, so we can remove self if it turns out
15
+ # to be an AttributeValueSelector
16
+ @all_mappings = all_mappings
17
+ end
18
+
19
+ attr_accessor :_name, :_selectors, :_dot_names
20
+
21
+ def method_missing(name)
22
+ raise NoMethodError.new("undefined method: #{name}") if name.to_s =~ /^_|[!?=]$/
23
+ _dot_names << name
24
+ self
25
+ end
26
+
27
+
28
+ def [](*selectors)
29
+ raise InvalidMappingError.new unless _selectors.empty?
30
+
31
+ @_selectors = selectors.omap {_to_selector}
32
+ self
33
+ end
34
+
35
+
36
+ def _to_selector
37
+ raise InvalidMappingError.new unless _dot_names.empty? and _selectors.empty?
38
+ AttributeSelector.new(_name)
39
+ end
40
+
41
+
42
+ def ==(rhs)
43
+ raise InvalidMappingError.new unless _selectors.empty? and _dot_names.empty?
44
+ AttributeValueSelector.new(_name, rhs)
45
+ end
46
+
47
+
48
+ def >>(rhs)
49
+ @all_mappings << Mapping.new(self, rhs)
50
+ nil
51
+ end
52
+
53
+ end
54
+
55
+ class AttributeSelector
56
+
57
+ def initialize(attribute)
58
+ @attribute = attribute
59
+ end
60
+
61
+
62
+ def compile
63
+ "options[:#{@attribute}]"
64
+ end
65
+ end
66
+
67
+ class AttributeValueSelector
68
+
69
+ def initialize(name, value)
70
+ @name = name
71
+ @value = value
72
+ end
73
+
74
+ attr_reader :name, :value
75
+
76
+ def compile
77
+ "(options[:#{@name}] == #{@value.inspect})"
78
+ end
79
+
80
+ def _to_selector
81
+ self
82
+ end
83
+
84
+ end
85
+
86
+
87
+ class Mapping
88
+
89
+ def initialize(pattern, result)
90
+ raise InvalidMappingError.new unless result._selectors.empty? and pattern._dot_names.length <= 1
91
+ @pattern = pattern
92
+ @result = result
93
+ end
94
+
95
+ attr_reader :pattern, :result
96
+
97
+
98
+ def pattern_tag_name
99
+ pattern._name
100
+ end
101
+
102
+
103
+ def pattern_predicate
104
+ clauses = (pattern._selectors || []).omap{compile}
105
+ type_name = pattern._dot_names.first
106
+ clauses << "selector_type && (#{type_name.to_s.camelize} <= selector_type)" if type_name
107
+ !clauses.empty? && "proc {|options| #{clauses.join(' and ')}}"
108
+ end
109
+
110
+
111
+ def result_tag_name
112
+ result._name
113
+ end
114
+
115
+
116
+ def result_css_classes
117
+ result._dot_names
118
+ end
119
+
120
+
121
+ end
122
+
123
+
124
+ class MappingDSL
125
+
126
+ def initialize
127
+ @_mappings = []
128
+ end
129
+
130
+ attr_reader :_mappings
131
+
132
+ def standard_mappings
133
+ instance_eval do
134
+ intro >> div.intro
135
+ maincol >> div.maincol
136
+ sidecol >> div.sidecol
137
+ panel >> div.panel
138
+ section >> div.section.foo
139
+
140
+ fields >> table.fields
141
+ field >> tr.field
142
+ fl >> td.field_label
143
+ fd >> td.field_data
144
+ end
145
+ end
146
+
147
+ def method_missing(name, &b)
148
+ raise NoMethodError.new("undefined method: #{name}") if name.to_s =~ /^_|[!?=]$/
149
+ TagOrSelector.new(name, @_mappings)
150
+ end
151
+
152
+ end
153
+
154
+ (Object.instance_methods +
155
+ Object.private_instance_methods +
156
+ Object.protected_instance_methods).each do |m|
157
+ MappingDSL.send(:undef_method, m) unless
158
+ %w{initialize method_missing instance_eval standard_mappings}.include?(m) || m.starts_with?('_')
159
+ end
160
+
161
+
162
+ def self.define_mapping_tag(mapping, mod)
163
+ def_name = mapping.pattern_tag_name
164
+ pred = mapping.pattern_predicate
165
+ result_tag_name = mapping.result_tag_name
166
+ result_css_classes = mapping.result_css_classes
167
+
168
+ static_tag_name = result_tag_name if result_tag_name.to_s.in?(Hobo.static_tags)
169
+
170
+ if def_name == result_tag_name
171
+ result_tag_name = "#{def_name}_unmapped"
172
+ if def_name.to_s.in?(mod.instance_methods) and result_tag_name.not_in?(mod.instance_methods)
173
+ mod.send(:alias_method, result_tag_name, def_name)
174
+ end
175
+
176
+ if pred and !mod.predicate_has_default?(def_name)
177
+ # Make sure the old tag method is called when the predicate is false
178
+ mod.module_eval "defp(:#{def_name}) { |options, block| #{result_tag_name}(options, &block) }"
179
+ end
180
+ end
181
+
182
+ # puts "map: #{def_name}[#{pred}] -> #{result_tag_name} (#{result_css_classes * ' '}) #{static_tag_name}"
183
+
184
+ def_line = if !pred.blank?
185
+ "defp :#{def_name}, (#{pred}) do |options, block|"
186
+ elsif mod.predicate_method?(def_name)
187
+ # be sure not to overwrite the predicate dispatch method
188
+ "defp :#{def_name} do |options, block|"
189
+ else
190
+ "def #{def_name}(options={}, &block)"
191
+ end
192
+
193
+ add_classes = if result_css_classes.empty?
194
+ ""
195
+ else
196
+ "options = add_classes(options, *#{result_css_classes.inspect})"
197
+ end
198
+
199
+ no_method_fallback = if static_tag_name
200
+ "content_tag('#{static_tag_name}', tagbody && tagbody.call, options)"
201
+ else
202
+ "raise NoMethodError.new('undefined method: #{result_tag_name}')"
203
+ end
204
+
205
+ mod.module_eval(x = <<-END, __FILE__, __LINE__+1)
206
+ #{def_line}
207
+ _tag_context(options, block) do |tagbody|
208
+ #{add_classes}
209
+ if respond_to?('#{result_tag_name}')
210
+ #{result_tag_name}(options, &block)
211
+ else
212
+ #{no_method_fallback}
213
+ end
214
+ end
215
+ end
216
+ END
217
+ end
218
+
219
+
220
+ def self.make_mappings(&b)
221
+ d = MappingDSL.new
222
+ d.instance_eval(&b)
223
+ d._mappings
224
+ end
225
+
226
+
227
+ def self.map(&b)
228
+ @mappings = make_mappings(&b)
229
+ end
230
+
231
+
232
+ def self.apply_mappings(mod)
233
+ mod.send(:include, PredicateDispatch) unless PredicateDispatch.in?(mod.included_modules)
234
+ @mappings.each {|m| define_mapping_tag(m, mod) }
235
+ end
236
+
237
+
238
+ def self.apply_standard_mappings(mod)
239
+ mod.send(:include, PredicateDispatch) unless PredicateDispatch.in?(mod.included_modules)
240
+ standard_mappings = make_mappings do
241
+ intro >> div.intro
242
+ main >> div.main
243
+ maincol >> div.maincol
244
+ sidecol >> div.sidecol
245
+ panel >> div.panel
246
+ section >> div.section
247
+
248
+ fields >> table.fields
249
+ field >> tr.field
250
+ fl >> td.field_label
251
+ fd >> td.field_data
252
+ end
253
+
254
+ for mapping in standard_mappings
255
+ define_mapping_tag(mapping, mod) unless mapping.pattern_tag_name.to_s.in?(mod.instance_methods)
256
+ end
257
+
258
+ end
259
+
260
+ end
261
+
262
+ end