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.
@@ -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,16 @@
1
+ module Mjs
2
+ module PageObject
3
+ private
4
+ def page
5
+ @page ||= Mjs::JavaScriptContext.new
6
+ end
7
+
8
+ def render(*args)
9
+ if args[0].is_a?(Mjs::JavaScriptContext)
10
+ args[0].to_s
11
+ else
12
+ super
13
+ end
14
+ end
15
+ end
16
+ 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