mjs 0.0.6
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.
- data/LICENSE +20 -0
- data/README +89 -0
- data/Rakefile +55 -0
- data/TODO +15 -0
- data/app/controllers/application.rb +5 -0
- data/app/controllers/main.rb +7 -0
- data/app/helpers/application_helper.rb +63 -0
- data/app/views/layout/mjs.html.erb +16 -0
- data/app/views/main/index.html.erb +1 -0
- data/lib/mjs/helper.rb +165 -0
- data/lib/mjs/java_script_context.rb +486 -0
- data/lib/mjs/merbtasks.rb +103 -0
- data/lib/mjs/page_object.rb +16 -0
- data/lib/mjs/slicetasks.rb +20 -0
- data/lib/mjs/spectasks.rb +53 -0
- data/lib/mjs/utils.rb +13 -0
- data/lib/mjs.rb +86 -0
- data/public/javascripts/master.js +0 -0
- data/public/stylesheets/master.css +2 -0
- data/spec/mjs_spec.rb +20 -0
- data/spec/requests/main_spec.rb +30 -0
- data/spec/spec_helper.rb +58 -0
- data/stubs/app/controllers/application.rb +2 -0
- data/stubs/app/controllers/main.rb +2 -0
- metadata +89 -0
@@ -0,0 +1,486 @@
|
|
1
|
+
require 'mjs/utils'
|
2
|
+
|
3
|
+
#
|
4
|
+
# This file is almost derived from prototype_helper.rb of RoR
|
5
|
+
#
|
6
|
+
module Mjs
|
7
|
+
class JavaScriptContext #:nodoc:
|
8
|
+
|
9
|
+
######################################################################
|
10
|
+
### define :each method for Rack::Response
|
11
|
+
### because Merb::Rack::StreamWrapper can't create response body correctly
|
12
|
+
|
13
|
+
def each(&callback)
|
14
|
+
callback.call(to_s)
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize
|
18
|
+
@lines = []
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_s
|
22
|
+
javascript = @lines * $/
|
23
|
+
end
|
24
|
+
|
25
|
+
# Returns a element reference by finding it through +id+ in the DOM. This element can then be
|
26
|
+
# used for further method calls. Examples:
|
27
|
+
#
|
28
|
+
# page['blank_slate'] # => $('blank_slate');
|
29
|
+
# page['blank_slate'].show # => $('blank_slate').show();
|
30
|
+
# page['blank_slate'].show('first').up # => $('blank_slate').show('first').up();
|
31
|
+
#
|
32
|
+
# You can also pass in a record, which will use ActionController::RecordIdentifier.dom_id to lookup
|
33
|
+
# the correct id:
|
34
|
+
#
|
35
|
+
# page[@post] # => $('post_45')
|
36
|
+
# page[Post.new] # => $('new_post')
|
37
|
+
def [](id)
|
38
|
+
case id
|
39
|
+
when Symbol
|
40
|
+
JavaScriptElementProxy.new(self, "##{id}")
|
41
|
+
when String, NilClass
|
42
|
+
JavaScriptElementProxy.new(self, id)
|
43
|
+
else
|
44
|
+
raise NotImplementedError, "[MJS] RecordIdentifier.dom_id(id)"
|
45
|
+
JavaScriptElementProxy.new(self, ActionController::RecordIdentifier.dom_id(id))
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Returns an object whose <tt>to_json</tt> evaluates to +code+. Use this to pass a literal JavaScript
|
50
|
+
# expression as an argument to another JavaScriptGenerator method.
|
51
|
+
def literal(code)
|
52
|
+
raise NotImplementedError, "[MJS] ActiveSupport::JSON::Variable.new(code.to_s)"
|
53
|
+
ActiveSupport::JSON::Variable.new(code.to_s)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Returns a collection reference by finding it through a CSS +pattern+ in the DOM. This collection can then be
|
57
|
+
# used for further method calls. Examples:
|
58
|
+
#
|
59
|
+
# page.select('p') # => $$('p');
|
60
|
+
# page.select('p.welcome b').first # => $$('p.welcome b').first();
|
61
|
+
# page.select('p.welcome b').first.hide # => $$('p.welcome b').first().hide();
|
62
|
+
#
|
63
|
+
# You can also use prototype enumerations with the collection. Observe:
|
64
|
+
#
|
65
|
+
# # Generates: $$('#items li').each(function(value) { value.hide(); });
|
66
|
+
# page.select('#items li').each do |value|
|
67
|
+
# value.hide
|
68
|
+
# end
|
69
|
+
#
|
70
|
+
# Though you can call the block param anything you want, they are always rendered in the
|
71
|
+
# javascript as 'value, index.' Other enumerations, like collect() return the last statement:
|
72
|
+
#
|
73
|
+
# # Generates: var hidden = $$('#items li').collect(function(value, index) { return value.hide(); });
|
74
|
+
# page.select('#items li').collect('hidden') do |item|
|
75
|
+
# item.hide
|
76
|
+
# end
|
77
|
+
#
|
78
|
+
def select(pattern)
|
79
|
+
JavaScriptElementCollectionProxy.new(self, pattern)
|
80
|
+
end
|
81
|
+
|
82
|
+
# Inserts HTML at the specified +position+ relative to the DOM element
|
83
|
+
# identified by the given +id+.
|
84
|
+
#
|
85
|
+
# +position+ may be one of:
|
86
|
+
#
|
87
|
+
# <tt>:top</tt>:: HTML is inserted inside the element, before the
|
88
|
+
# element's existing content.
|
89
|
+
# <tt>:bottom</tt>:: HTML is inserted inside the element, after the
|
90
|
+
# element's existing content.
|
91
|
+
# <tt>:before</tt>:: HTML is inserted immediately preceding the element.
|
92
|
+
# <tt>:after</tt>:: HTML is inserted immediately following the element.
|
93
|
+
#
|
94
|
+
# +options_for_render+ may be either a string of HTML to insert, or a hash
|
95
|
+
# of options to be passed to ActionView::Base#render. For example:
|
96
|
+
#
|
97
|
+
# # Insert the rendered 'navigation' partial just before the DOM
|
98
|
+
# # element with ID 'content'.
|
99
|
+
# # Generates: Element.insert("content", { before: "-- Contents of 'navigation' partial --" });
|
100
|
+
# page.insert_html :before, 'content', :partial => 'navigation'
|
101
|
+
#
|
102
|
+
# # Add a list item to the bottom of the <ul> with ID 'list'.
|
103
|
+
# # Generates: Element.insert("list", { bottom: "<li>Last item</li>" });
|
104
|
+
# page.insert_html :bottom, 'list', '<li>Last item</li>'
|
105
|
+
#
|
106
|
+
def insert_html(position, id, *options_for_render)
|
107
|
+
content = javascript_object_for(render(*options_for_render))
|
108
|
+
record "Element.insert(\"#{id}\", { #{position.to_s.downcase}: #{content} });"
|
109
|
+
end
|
110
|
+
|
111
|
+
# Replaces the inner HTML of the DOM element with the given +id+.
|
112
|
+
#
|
113
|
+
# +options_for_render+ may be either a string of HTML to insert, or a hash
|
114
|
+
# of options to be passed to ActionView::Base#render. For example:
|
115
|
+
#
|
116
|
+
# # Replace the HTML of the DOM element having ID 'person-45' with the
|
117
|
+
# # 'person' partial for the appropriate object.
|
118
|
+
# # Generates: Element.update("person-45", "-- Contents of 'person' partial --");
|
119
|
+
# page.replace_html 'person-45', :partial => 'person', :object => @person
|
120
|
+
#
|
121
|
+
def replace_html(id, *options_for_render)
|
122
|
+
call 'Element.update', id, render(*options_for_render)
|
123
|
+
end
|
124
|
+
|
125
|
+
# Replaces the "outer HTML" (i.e., the entire element, not just its
|
126
|
+
# contents) of the DOM element with the given +id+.
|
127
|
+
#
|
128
|
+
# +options_for_render+ may be either a string of HTML to insert, or a hash
|
129
|
+
# of options to be passed to ActionView::Base#render. For example:
|
130
|
+
#
|
131
|
+
# # Replace the DOM element having ID 'person-45' with the
|
132
|
+
# # 'person' partial for the appropriate object.
|
133
|
+
# page.replace 'person-45', :partial => 'person', :object => @person
|
134
|
+
#
|
135
|
+
# This allows the same partial that is used for the +insert_html+ to
|
136
|
+
# be also used for the input to +replace+ without resorting to
|
137
|
+
# the use of wrapper elements.
|
138
|
+
#
|
139
|
+
# Examples:
|
140
|
+
#
|
141
|
+
# <div id="people">
|
142
|
+
# <%= render :partial => 'person', :collection => @people %>
|
143
|
+
# </div>
|
144
|
+
#
|
145
|
+
# # Insert a new person
|
146
|
+
# #
|
147
|
+
# # Generates: new Insertion.Bottom({object: "Matz", partial: "person"}, "");
|
148
|
+
# page.insert_html :bottom, :partial => 'person', :object => @person
|
149
|
+
#
|
150
|
+
# # Replace an existing person
|
151
|
+
#
|
152
|
+
# # Generates: Element.replace("person_45", "-- Contents of partial --");
|
153
|
+
# page.replace 'person_45', :partial => 'person', :object => @person
|
154
|
+
#
|
155
|
+
def replace(id, *options_for_render)
|
156
|
+
call 'Element.replace', id, render(*options_for_render)
|
157
|
+
end
|
158
|
+
|
159
|
+
# Removes the DOM elements with the given +ids+ from the page.
|
160
|
+
#
|
161
|
+
# Example:
|
162
|
+
#
|
163
|
+
# # Remove a few people
|
164
|
+
# # Generates: ["person_23", "person_9", "person_2"].each(Element.remove);
|
165
|
+
# page.remove 'person_23', 'person_9', 'person_2'
|
166
|
+
#
|
167
|
+
def remove(*ids)
|
168
|
+
loop_on_multiple_args 'Element.remove', ids
|
169
|
+
end
|
170
|
+
|
171
|
+
# Shows hidden DOM elements with the given +ids+.
|
172
|
+
#
|
173
|
+
# Example:
|
174
|
+
#
|
175
|
+
# # Show a few people
|
176
|
+
# # Generates: ["person_6", "person_13", "person_223"].each(Element.show);
|
177
|
+
# page.show 'person_6', 'person_13', 'person_223'
|
178
|
+
#
|
179
|
+
def show(*ids)
|
180
|
+
loop_on_multiple_args 'Element.show', ids
|
181
|
+
end
|
182
|
+
|
183
|
+
# Hides the visible DOM elements with the given +ids+.
|
184
|
+
#
|
185
|
+
# Example:
|
186
|
+
#
|
187
|
+
# # Hide a few people
|
188
|
+
# # Generates: ["person_29", "person_9", "person_0"].each(Element.hide);
|
189
|
+
# page.hide 'person_29', 'person_9', 'person_0'
|
190
|
+
#
|
191
|
+
def hide(*ids)
|
192
|
+
loop_on_multiple_args 'Element.hide', ids
|
193
|
+
end
|
194
|
+
|
195
|
+
# Toggles the visibility of the DOM elements with the given +ids+.
|
196
|
+
# Example:
|
197
|
+
#
|
198
|
+
# # Show a few people
|
199
|
+
# # Generates: ["person_14", "person_12", "person_23"].each(Element.toggle);
|
200
|
+
# page.toggle 'person_14', 'person_12', 'person_23' # Hides the elements
|
201
|
+
# page.toggle 'person_14', 'person_12', 'person_23' # Shows the previously hidden elements
|
202
|
+
#
|
203
|
+
def toggle(*ids)
|
204
|
+
loop_on_multiple_args 'Element.toggle', ids
|
205
|
+
end
|
206
|
+
|
207
|
+
# Displays an alert dialog with the given +message+.
|
208
|
+
#
|
209
|
+
# Example:
|
210
|
+
#
|
211
|
+
# # Generates: alert('This message is from Rails!')
|
212
|
+
# page.alert('This message is from Rails!')
|
213
|
+
def alert(message)
|
214
|
+
call 'alert', message
|
215
|
+
end
|
216
|
+
|
217
|
+
# Redirects the browser to the given +location+ using JavaScript, in the same form as +url_for+.
|
218
|
+
#
|
219
|
+
# Examples:
|
220
|
+
#
|
221
|
+
# # Generates: window.location.href = "/mycontroller";
|
222
|
+
# page.redirect_to(:action => 'index')
|
223
|
+
#
|
224
|
+
# # Generates: window.location.href = "/account/signup";
|
225
|
+
# page.redirect_to(:controller => 'account', :action => 'signup')
|
226
|
+
def redirect_to(location)
|
227
|
+
url = location.is_a?(String) ? location : @context.url_for(location)
|
228
|
+
record "window.location.href = #{url.inspect}"
|
229
|
+
end
|
230
|
+
|
231
|
+
# Reloads the browser's current +location+ using JavaScript
|
232
|
+
#
|
233
|
+
# Examples:
|
234
|
+
#
|
235
|
+
# # Generates: window.location.reload();
|
236
|
+
# page.reload
|
237
|
+
def reload
|
238
|
+
record 'window.location.reload()'
|
239
|
+
end
|
240
|
+
|
241
|
+
# Calls the JavaScript +function+, optionally with the given +arguments+.
|
242
|
+
#
|
243
|
+
# If a block is given, the block will be passed to a new JavaScriptGenerator;
|
244
|
+
# the resulting JavaScript code will then be wrapped inside <tt>function() { ... }</tt>
|
245
|
+
# and passed as the called function's final argument.
|
246
|
+
#
|
247
|
+
# Examples:
|
248
|
+
#
|
249
|
+
# # Generates: Element.replace(my_element, "My content to replace with.")
|
250
|
+
# page.call 'Element.replace', 'my_element', "My content to replace with."
|
251
|
+
#
|
252
|
+
# # Generates: alert('My message!')
|
253
|
+
# page.call 'alert', 'My message!'
|
254
|
+
#
|
255
|
+
# # Generates:
|
256
|
+
# # my_method(function() {
|
257
|
+
# # $("one").show();
|
258
|
+
# # $("two").hide();
|
259
|
+
# # });
|
260
|
+
# page.call(:my_method) do |p|
|
261
|
+
# p[:one].show
|
262
|
+
# p[:two].hide
|
263
|
+
# end
|
264
|
+
def call(function, *arguments, &block)
|
265
|
+
record "#{function}(#{arguments_for_call(arguments, block)})"
|
266
|
+
end
|
267
|
+
|
268
|
+
# Assigns the JavaScript +variable+ the given +value+.
|
269
|
+
#
|
270
|
+
# Examples:
|
271
|
+
#
|
272
|
+
# # Generates: my_string = "This is mine!";
|
273
|
+
# page.assign 'my_string', 'This is mine!'
|
274
|
+
#
|
275
|
+
# # Generates: record_count = 33;
|
276
|
+
# page.assign 'record_count', 33
|
277
|
+
#
|
278
|
+
# # Generates: tabulated_total = 47
|
279
|
+
# page.assign 'tabulated_total', @total_from_cart
|
280
|
+
#
|
281
|
+
def assign(variable, value)
|
282
|
+
record "#{variable} = #{javascript_object_for(value)}"
|
283
|
+
end
|
284
|
+
|
285
|
+
# Writes raw JavaScript to the page.
|
286
|
+
#
|
287
|
+
# Example:
|
288
|
+
#
|
289
|
+
# page << "alert('JavaScript with Prototype.');"
|
290
|
+
def <<(javascript)
|
291
|
+
@lines << javascript
|
292
|
+
end
|
293
|
+
|
294
|
+
# Executes the content of the block after a delay of +seconds+. Example:
|
295
|
+
#
|
296
|
+
# # Generates:
|
297
|
+
# # setTimeout(function() {
|
298
|
+
# # ;
|
299
|
+
# # new Effect.Fade("notice",{});
|
300
|
+
# # }, 20000);
|
301
|
+
# page.delay(20) do
|
302
|
+
# page.visual_effect :fade, 'notice'
|
303
|
+
# end
|
304
|
+
def delay(seconds = 1)
|
305
|
+
record "setTimeout(function() {\n\n"
|
306
|
+
yield
|
307
|
+
record "}, #{(seconds * 1000).to_i})"
|
308
|
+
end
|
309
|
+
|
310
|
+
# Starts a script.aculo.us visual effect. See
|
311
|
+
# ActionView::Helpers::ScriptaculousHelper for more information.
|
312
|
+
def visual_effect(name, id = nil, options = {})
|
313
|
+
record @context.send(:visual_effect, name, id, options)
|
314
|
+
end
|
315
|
+
|
316
|
+
# Creates a script.aculo.us sortable element. Useful
|
317
|
+
# to recreate sortable elements after items get added
|
318
|
+
# or deleted.
|
319
|
+
# See ActionView::Helpers::ScriptaculousHelper for more information.
|
320
|
+
def sortable(id, options = {})
|
321
|
+
record @context.send(:sortable_element_js, id, options)
|
322
|
+
end
|
323
|
+
|
324
|
+
# Creates a script.aculo.us draggable element.
|
325
|
+
# See ActionView::Helpers::ScriptaculousHelper for more information.
|
326
|
+
def draggable(id, options = {})
|
327
|
+
record @context.send(:draggable_element_js, id, options)
|
328
|
+
end
|
329
|
+
|
330
|
+
# Creates a script.aculo.us drop receiving element.
|
331
|
+
# See ActionView::Helpers::ScriptaculousHelper for more information.
|
332
|
+
def drop_receiving(id, options = {})
|
333
|
+
record @context.send(:drop_receiving_element_js, id, options)
|
334
|
+
end
|
335
|
+
|
336
|
+
private
|
337
|
+
def loop_on_multiple_args(method, ids)
|
338
|
+
record(ids.size>1 ?
|
339
|
+
"#{javascript_object_for(ids)}.each(#{method})" :
|
340
|
+
"#{method}(#{ids.first.to_json})")
|
341
|
+
end
|
342
|
+
|
343
|
+
def page
|
344
|
+
self
|
345
|
+
end
|
346
|
+
|
347
|
+
def record(line)
|
348
|
+
returning line = "#{line.to_s.chomp.gsub(/\;\z/, '')};" do
|
349
|
+
self << line
|
350
|
+
end
|
351
|
+
end
|
352
|
+
|
353
|
+
def render(*options_for_render)
|
354
|
+
old_format = @context && @context.template_format
|
355
|
+
@context.template_format = :html if @context
|
356
|
+
Hash === options_for_render.first ?
|
357
|
+
@context.render(*options_for_render) :
|
358
|
+
options_for_render.first.to_s
|
359
|
+
ensure
|
360
|
+
@context.template_format = old_format if @context
|
361
|
+
end
|
362
|
+
|
363
|
+
def javascript_object_for(object)
|
364
|
+
object.respond_to?(:to_json) ? object.to_json : object.inspect
|
365
|
+
|
366
|
+
# TODO: to_json is too buggy!
|
367
|
+
rescue JSON::GeneratorError
|
368
|
+
if object.is_a?(String)
|
369
|
+
object.inspect
|
370
|
+
else
|
371
|
+
raise
|
372
|
+
end
|
373
|
+
end
|
374
|
+
|
375
|
+
def arguments_for_call(arguments, block = nil)
|
376
|
+
arguments << block_to_function(block) if block
|
377
|
+
arguments.map { |argument| javascript_object_for(argument) }.join ', '
|
378
|
+
end
|
379
|
+
|
380
|
+
def block_to_function(block)
|
381
|
+
generator = self.class.new(@context, &block)
|
382
|
+
literal("function() { #{generator.to_s} }")
|
383
|
+
end
|
384
|
+
|
385
|
+
def method_missing(method, *arguments)
|
386
|
+
JavaScriptProxy.new(self, Mjs::Utils.camelize(method))
|
387
|
+
end
|
388
|
+
end # class JavaScriptGenerator
|
389
|
+
|
390
|
+
# class JavaScriptProxy < ActiveSupport::BasicObject #:nodoc:
|
391
|
+
# [TODO] BlackSlate is not supported yet
|
392
|
+
class JavaScriptProxy
|
393
|
+
|
394
|
+
def initialize(generator, root = nil)
|
395
|
+
@generator = generator
|
396
|
+
@generator << root if root
|
397
|
+
end
|
398
|
+
|
399
|
+
private
|
400
|
+
def method_missing(method, *arguments, &block)
|
401
|
+
if method.to_s =~ /(.*)=$/
|
402
|
+
assign($1, arguments.first)
|
403
|
+
else
|
404
|
+
call("#{Mjs::Utils.camelize(method, :lower)}", *arguments, &block)
|
405
|
+
end
|
406
|
+
end
|
407
|
+
|
408
|
+
def call(function, *arguments, &block)
|
409
|
+
append_to_function_chain!("#{function}(#{@generator.send(:arguments_for_call, arguments, block)})")
|
410
|
+
self
|
411
|
+
end
|
412
|
+
|
413
|
+
def assign(variable, value)
|
414
|
+
append_to_function_chain!("#{variable} = #{@generator.send(:javascript_object_for, value)}")
|
415
|
+
end
|
416
|
+
|
417
|
+
def function_chain
|
418
|
+
@function_chain ||= @generator.instance_variable_get(:@lines)
|
419
|
+
end
|
420
|
+
|
421
|
+
def append_to_function_chain!(call)
|
422
|
+
function_chain[-1].chomp!(';')
|
423
|
+
function_chain[-1] += ".#{call};"
|
424
|
+
end
|
425
|
+
end
|
426
|
+
|
427
|
+
class JavaScriptElementProxy < JavaScriptProxy #:nodoc:
|
428
|
+
def initialize(generator, id)
|
429
|
+
@id = id
|
430
|
+
super(generator, "$(#{id.to_json})")
|
431
|
+
end
|
432
|
+
|
433
|
+
# Allows access of element attributes through +attribute+. Examples:
|
434
|
+
#
|
435
|
+
# page['foo']['style'] # => $('foo').style;
|
436
|
+
# page['foo']['style']['color'] # => $('blank_slate').style.color;
|
437
|
+
# page['foo']['style']['color'] = 'red' # => $('blank_slate').style.color = 'red';
|
438
|
+
# page['foo']['style'].color = 'red' # => $('blank_slate').style.color = 'red';
|
439
|
+
def [](attribute)
|
440
|
+
append_to_function_chain!(attribute)
|
441
|
+
self
|
442
|
+
end
|
443
|
+
|
444
|
+
def []=(variable, value)
|
445
|
+
assign(variable, value)
|
446
|
+
end
|
447
|
+
|
448
|
+
def replace_html(*options_for_render)
|
449
|
+
call 'update', @generator.send(:render, *options_for_render)
|
450
|
+
end
|
451
|
+
|
452
|
+
def replace(*options_for_render)
|
453
|
+
call 'replace', @generator.send(:render, *options_for_render)
|
454
|
+
end
|
455
|
+
|
456
|
+
def reload(options_for_replace = {})
|
457
|
+
replace(options_for_replace.merge({ :partial => @id.to_s }))
|
458
|
+
end
|
459
|
+
|
460
|
+
end
|
461
|
+
|
462
|
+
class JavaScriptVariableProxy < JavaScriptProxy #:nodoc:
|
463
|
+
def initialize(generator, variable)
|
464
|
+
@variable = variable
|
465
|
+
@empty = true # only record lines if we have to. gets rid of unnecessary linebreaks
|
466
|
+
super(generator)
|
467
|
+
end
|
468
|
+
|
469
|
+
# The JSON Encoder calls this to check for the +to_json+ method
|
470
|
+
# Since it's a blank slate object, I suppose it responds to anything.
|
471
|
+
def respond_to?(method)
|
472
|
+
true
|
473
|
+
end
|
474
|
+
|
475
|
+
def to_json(options = nil)
|
476
|
+
@variable
|
477
|
+
end
|
478
|
+
|
479
|
+
private
|
480
|
+
def append_to_function_chain!(call)
|
481
|
+
@generator << @variable if @empty
|
482
|
+
@empty = false
|
483
|
+
super
|
484
|
+
end
|
485
|
+
end
|
486
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
namespace :slices do
|
2
|
+
namespace :mjs do
|
3
|
+
|
4
|
+
desc "Install Mjs"
|
5
|
+
task :install => [:preflight, :setup_directories, :copy_assets, :migrate]
|
6
|
+
|
7
|
+
desc "Test for any dependencies"
|
8
|
+
task :preflight do # see slicetasks.rb
|
9
|
+
end
|
10
|
+
|
11
|
+
desc "Setup directories"
|
12
|
+
task :setup_directories do
|
13
|
+
puts "Creating directories for host application"
|
14
|
+
Mjs.mirrored_components.each do |type|
|
15
|
+
if File.directory?(Mjs.dir_for(type))
|
16
|
+
if !File.directory?(dst_path = Mjs.app_dir_for(type))
|
17
|
+
relative_path = dst_path.relative_path_from(Merb.root)
|
18
|
+
puts "- creating directory :#{type} #{File.basename(Merb.root) / relative_path}"
|
19
|
+
mkdir_p(dst_path)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# desc "Copy stub files to host application"
|
26
|
+
# task :stubs do
|
27
|
+
# puts "Copying stubs for Mjs - resolves any collisions"
|
28
|
+
# copied, preserved = Mjs.mirror_stubs!
|
29
|
+
# puts "- no files to copy" if copied.empty? && preserved.empty?
|
30
|
+
# copied.each { |f| puts "- copied #{f}" }
|
31
|
+
# preserved.each { |f| puts "! preserved override as #{f}" }
|
32
|
+
# end
|
33
|
+
|
34
|
+
# desc "Copy stub files and views to host application"
|
35
|
+
# task :patch => [ "stubs", "freeze:views" ]
|
36
|
+
|
37
|
+
desc "Copy public assets to host application"
|
38
|
+
task :copy_assets do
|
39
|
+
puts "Copying assets for Mjs - resolves any collisions"
|
40
|
+
copied, preserved = Mjs.mirror_public!
|
41
|
+
puts "- no files to copy" if copied.empty? && preserved.empty?
|
42
|
+
copied.each { |f| puts "- copied #{f}" }
|
43
|
+
preserved.each { |f| puts "! preserved override as #{f}" }
|
44
|
+
end
|
45
|
+
|
46
|
+
desc "Migrate the database"
|
47
|
+
task :migrate do # see slicetasks.rb
|
48
|
+
end
|
49
|
+
|
50
|
+
desc "Freeze Mjs into your app (only mjs/app)"
|
51
|
+
task :freeze => [ "freeze:app" ]
|
52
|
+
|
53
|
+
namespace :freeze do
|
54
|
+
|
55
|
+
# desc "Freezes Mjs by installing the gem into application/gems"
|
56
|
+
# task :gem do
|
57
|
+
# ENV["GEM"] ||= "mjs"
|
58
|
+
# Rake::Task['slices:install_as_gem'].invoke
|
59
|
+
# end
|
60
|
+
|
61
|
+
desc "Freezes Mjs by copying all files from mjs/app to your application"
|
62
|
+
task :app do
|
63
|
+
puts "Copying all mjs/app files to your application - resolves any collisions"
|
64
|
+
copied, preserved = Mjs.mirror_app!
|
65
|
+
puts "- no files to copy" if copied.empty? && preserved.empty?
|
66
|
+
copied.each { |f| puts "- copied #{f}" }
|
67
|
+
preserved.each { |f| puts "! preserved override as #{f}" }
|
68
|
+
end
|
69
|
+
|
70
|
+
desc "Freeze all views into your application for easy modification"
|
71
|
+
task :views do
|
72
|
+
puts "Copying all view templates to your application - resolves any collisions"
|
73
|
+
copied, preserved = Mjs.mirror_files_for :view
|
74
|
+
puts "- no files to copy" if copied.empty? && preserved.empty?
|
75
|
+
copied.each { |f| puts "- copied #{f}" }
|
76
|
+
preserved.each { |f| puts "! preserved override as #{f}" }
|
77
|
+
end
|
78
|
+
|
79
|
+
desc "Freeze all models into your application for easy modification"
|
80
|
+
task :models do
|
81
|
+
puts "Copying all models to your application - resolves any collisions"
|
82
|
+
copied, preserved = Mjs.mirror_files_for :model
|
83
|
+
puts "- no files to copy" if copied.empty? && preserved.empty?
|
84
|
+
copied.each { |f| puts "- copied #{f}" }
|
85
|
+
preserved.each { |f| puts "! preserved override as #{f}" }
|
86
|
+
end
|
87
|
+
|
88
|
+
desc "Freezes Mjs as a gem and copies over mjs/app"
|
89
|
+
task :app_with_gem => [:gem, :app]
|
90
|
+
|
91
|
+
desc "Freezes Mjs by unpacking all files into your application"
|
92
|
+
task :unpack do
|
93
|
+
puts "Unpacking Mjs files to your application - resolves any collisions"
|
94
|
+
copied, preserved = Mjs.unpack_slice!
|
95
|
+
puts "- no files to copy" if copied.empty? && preserved.empty?
|
96
|
+
copied.each { |f| puts "- copied #{f}" }
|
97
|
+
preserved.each { |f| puts "! preserved override as #{f}" }
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
namespace :slices do
|
2
|
+
namespace :mjs do
|
3
|
+
|
4
|
+
# add your own mjs tasks here
|
5
|
+
|
6
|
+
# # Uncomment the following lines and edit the pre defined tasks
|
7
|
+
#
|
8
|
+
# # implement this to test for structural/code dependencies
|
9
|
+
# # like certain directories or availability of other files
|
10
|
+
# desc "Test for any dependencies"
|
11
|
+
# task :preflight do
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# # implement this to perform any database related setup steps
|
15
|
+
# desc "Migrate the database"
|
16
|
+
# task :migrate do
|
17
|
+
# end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
namespace :slices do
|
2
|
+
namespace :mjs do
|
3
|
+
|
4
|
+
desc "Run slice specs within the host application context"
|
5
|
+
task :spec => [ "spec:explain", "spec:default" ]
|
6
|
+
|
7
|
+
namespace :spec do
|
8
|
+
|
9
|
+
slice_root = File.expand_path(File.join(File.dirname(__FILE__), '..', '..'))
|
10
|
+
|
11
|
+
task :explain do
|
12
|
+
puts "\nNote: By running Mjs specs inside the application context any\n" +
|
13
|
+
"overrides could break existing specs. This isn't always a problem,\n" +
|
14
|
+
"especially in the case of views. Use these spec tasks to check how\n" +
|
15
|
+
"well your application conforms to the original slice implementation."
|
16
|
+
end
|
17
|
+
|
18
|
+
Spec::Rake::SpecTask.new('default') do |t|
|
19
|
+
t.spec_opts = ["--format", "specdoc", "--colour"]
|
20
|
+
t.spec_files = Dir["#{slice_root}/spec/**/*_spec.rb"].sort
|
21
|
+
end
|
22
|
+
|
23
|
+
desc "Run all model specs, run a spec for a specific Model with MODEL=MyModel"
|
24
|
+
Spec::Rake::SpecTask.new('model') do |t|
|
25
|
+
t.spec_opts = ["--format", "specdoc", "--colour"]
|
26
|
+
if(ENV['MODEL'])
|
27
|
+
t.spec_files = Dir["#{slice_root}/spec/models/**/#{ENV['MODEL']}_spec.rb"].sort
|
28
|
+
else
|
29
|
+
t.spec_files = Dir["#{slice_root}/spec/models/**/*_spec.rb"].sort
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
desc "Run all request specs, run a spec for a specific request with REQUEST=MyRequest"
|
34
|
+
Spec::Rake::SpecTask.new('request') do |t|
|
35
|
+
t.spec_opts = ["--format", "specdoc", "--colour"]
|
36
|
+
if(ENV['REQUEST'])
|
37
|
+
t.spec_files = Dir["#{slice_root}/spec/requests/**/#{ENV['REQUEST']}_spec.rb"].sort
|
38
|
+
else
|
39
|
+
t.spec_files = Dir["#{slice_root}/spec/requests/**/*_spec.rb"].sort
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
desc "Run all specs and output the result in html"
|
44
|
+
Spec::Rake::SpecTask.new('html') do |t|
|
45
|
+
t.spec_opts = ["--format", "html"]
|
46
|
+
t.libs = ['lib', 'server/lib' ]
|
47
|
+
t.spec_files = Dir["#{slice_root}/spec/**/*_spec.rb"].sort
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
data/lib/mjs/utils.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
module Mjs
|
2
|
+
module Utils
|
3
|
+
def self.camelize(lower_case_and_underscored_word, first_letter = :upper)
|
4
|
+
lower_case_and_underscored_word = lower_case_and_underscored_word.to_s
|
5
|
+
first_letter_in_uppercase = (first_letter == :upper)
|
6
|
+
if first_letter_in_uppercase
|
7
|
+
lower_case_and_underscored_word.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
|
8
|
+
else
|
9
|
+
lower_case_and_underscored_word[0,1].downcase + camelize(lower_case_and_underscored_word)[1..-1]
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|