hobo 0.5.3

Sign up to get free protection for your applications and to get access to all the features.
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