mjs 0.0.6

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