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,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,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
|