From 2799ba7ba00d215b1feee27325406b4b95ce63eb Mon Sep 17 00:00:00 2001 From: Kristian Mandrup Date: Wed, 5 Sep 2012 15:03:47 +0200 Subject: [PATCH 01/33] some nice updates to make it better to work with for Rails 3.2 --- lib/apotomo/javascript_generator.rb | 94 ++++++++++++++++++++ lib/generators/apotomo/widget_generator.rb | 34 +++++-- lib/generators/erb/widget_generator.rb | 1 + lib/generators/haml/widget_generator.rb | 1 + lib/generators/slim/widget_generator.rb | 1 + lib/generators/templates/view.slim | 7 +- lib/generators/templates/widget.coffee | 1 + lib/generators/templates/widget.css | 1 + lib/generators/test_unit/widget_generator.rb | 8 +- test/rails/widget_generator_test.rb | 69 ++++++++------ 10 files changed, 179 insertions(+), 38 deletions(-) create mode 100644 lib/generators/templates/widget.coffee create mode 100644 lib/generators/templates/widget.css diff --git a/lib/apotomo/javascript_generator.rb b/lib/apotomo/javascript_generator.rb index 6474586..ff3d8e9 100644 --- a/lib/apotomo/javascript_generator.rb +++ b/lib/apotomo/javascript_generator.rb @@ -47,6 +47,100 @@ def update(id, markup); element(id) + '.html("'+escape(markup)+'");'; end def replace(id, markup); element(id) + '.replaceWith("'+escape(markup)+'");'; end def update_id(id, markup); update("##{id}", markup); end def replace_id(id, markup); replace("##{id}", markup); end + + def update_text(id, selector, markup) + element(id) + ".find(#{selector}).text('#{escape(markup)}');" + end + + def append(id, selector, markup) + element(id) + ".find(#{selector}).append('#{escape(markup)}');" + end + + def prepend(id, selector, markup) + element(id) + ".find(#{selector}).prepend('#{escape(markup)}');" + end + + def append_to(selector, markup) + "$(#{escape(markup)}').appendTo('#{selector}');" + end + + def prepend_to(selector, markup) + "$(#{escape(markup)}').prependTo('#{selector}');" + end + + def after selector, markup + "$('#{selector}').after('#{escape(markup)}');" + end + + def before selector, markup + "$('#{selector}').before('#{escape(markup)}');" + end + + def replace_all(selector, markup) + "$(#{escape(markup)}').replaceAll('#{selector}');" + end + + def unwrap (selector) + "$('#{selector}').unwrap();" + end + + def wrap (selector, markup) + "$('#{selector}').wrap('#{escape(markup)}');" + end + + def wrap_innet (selector, markup) + "$('#{selector}').wrapInner('#{escape(markup)}');" + end + + def wrap_all (selector, markup) + "$('#{selector}').wrap_all('#{escape(markup)}');" + end + + def remove(selector) + "$('#{selector}').remove();" + end + + def remove_class(selector, *classes) + classes = classes.flatten.join(' ') + "$('#{selector}').removeClass('#{classes}');" + end + alias_method :remove_classes, :remove_class + + def add_class(selector, *classes) + classes = classes.flatten.join(' ') + "$('#{selector}').addClass('#{classes}');" + end + alias_method :add_classes, :add_class + + def toggle_class(selector, *classes) + classes = classes.flatten.join(' ') + "$('#{selector}').toggleClass('#{classes}');" + end + alias_method :toggle_classes, :toggle_class + + def toggle_class_fun(selector, fun) + "$('#{selector}').toggleClass(function() {#{fun}});" + end + + def get_attr(selector, name) + "$('#{selector}').attr('#{name}');" + end + + def get_prop(selector, name) + "$('#{selector}').prop('#{name}');" + end + + def get_val selector + "$('#{selector}').val();" + end + + def get_html(selector) + "$('#{selector}').html();" + end + + def empty(selector) + "$('#{selector}').empty();" + end end end end diff --git a/lib/generators/apotomo/widget_generator.rb b/lib/generators/apotomo/widget_generator.rb index 21475a8..56c97ae 100644 --- a/lib/generators/apotomo/widget_generator.rb +++ b/lib/generators/apotomo/widget_generator.rb @@ -3,12 +3,32 @@ module Apotomo module Generators module BasePathMethods - private + private + def base_path File.join('app/widgets', class_path, file_name) end + + + def js_path + File.join('app/assets/javascripts/widgets', class_path, file_name) + end + + def css_path + File.join('app/assets/stylesheets/widgets', class_path, file_name) + end end + module Views + def create_views + for state in actions do + @state = state + @path = File.join(base_path, 'views', "#{state}.html.#{handler}") #base_path defined in Cells::Generators::Base. + template "view.#{handler}", @path + end + end + end + class WidgetGenerator < ::Cells::Generators::Base include BasePathMethods @@ -18,11 +38,15 @@ class WidgetGenerator < ::Cells::Generators::Base hook_for(:test_framework) # TODO: implement rspec-apotomo. check_class_collision :suffix => "Widget" - - + def create_cell_file - template 'widget.rb', "#{base_path}_widget.rb" + template 'widget.rb', File.join(base_path, "#{file_name}_widget.rb") end + + def create_assets_files + template 'widget.coffee', "#{js_path}_widget.coffee" + template 'widget.css', "#{css_path}_widget.css" + end end end -end +end \ No newline at end of file diff --git a/lib/generators/erb/widget_generator.rb b/lib/generators/erb/widget_generator.rb index f9915c7..a16c28a 100644 --- a/lib/generators/erb/widget_generator.rb +++ b/lib/generators/erb/widget_generator.rb @@ -5,6 +5,7 @@ module Erb module Generators class WidgetGenerator < CellGenerator include ::Apotomo::Generators::BasePathMethods + include ::Apotomo::Generators::Views source_root File.expand_path('../../templates', __FILE__) end end diff --git a/lib/generators/haml/widget_generator.rb b/lib/generators/haml/widget_generator.rb index 2c5d807..7709bdd 100644 --- a/lib/generators/haml/widget_generator.rb +++ b/lib/generators/haml/widget_generator.rb @@ -5,6 +5,7 @@ module Haml module Generators class WidgetGenerator < CellGenerator include ::Apotomo::Generators::BasePathMethods + include ::Apotomo::Generators::Views source_root File.expand_path('../../templates', __FILE__) end end diff --git a/lib/generators/slim/widget_generator.rb b/lib/generators/slim/widget_generator.rb index 3824ea6..23966e1 100644 --- a/lib/generators/slim/widget_generator.rb +++ b/lib/generators/slim/widget_generator.rb @@ -5,6 +5,7 @@ module Slim module Generators class WidgetGenerator < CellGenerator include ::Apotomo::Generators::BasePathMethods + include ::Apotomo::Generators::Views source_root File.expand_path('../../templates', __FILE__) end end diff --git a/lib/generators/templates/view.slim b/lib/generators/templates/view.slim index c2a56f5..8a7c543 100644 --- a/lib/generators/templates/view.slim +++ b/lib/generators/templates/view.slim @@ -1,4 +1,3 @@ -h1 - <%= class_name %>Widget#<%= @state %> -p - Find me in <%= @path %> += widget_div do + h1 <%= class_name %>Widget#<%= @state %> + p Find me in <%= @path %> \ No newline at end of file diff --git a/lib/generators/templates/widget.coffee b/lib/generators/templates/widget.coffee new file mode 100644 index 0000000..9168ed7 --- /dev/null +++ b/lib/generators/templates/widget.coffee @@ -0,0 +1 @@ +# Define your coffeescript code for the <%= class_name %> widget diff --git a/lib/generators/templates/widget.css b/lib/generators/templates/widget.css new file mode 100644 index 0000000..90ce1a6 --- /dev/null +++ b/lib/generators/templates/widget.css @@ -0,0 +1 @@ +/* Define your css code for the <%= class_name %> widget */ \ No newline at end of file diff --git a/lib/generators/test_unit/widget_generator.rb b/lib/generators/test_unit/widget_generator.rb index e2dce04..131b357 100644 --- a/lib/generators/test_unit/widget_generator.rb +++ b/lib/generators/test_unit/widget_generator.rb @@ -7,7 +7,13 @@ class WidgetGenerator < ::Cells::Generators::Base def create_test @states = actions - template 'widget_test.rb', File.join('test/widgets/', class_path, "#{file_name}_widget_test.rb") + template 'widget_test.rb', File.join(test_path, "#{file_name}_widget_test.rb") + end + + protected + + def test_path + File.join('test/widgets/', class_path, file_name) end end end diff --git a/test/rails/widget_generator_test.rb b/test/rails/widget_generator_test.rb index 9e3f13b..77eb3b6 100644 --- a/test/rails/widget_generator_test.rb +++ b/test/rails/widget_generator_test.rb @@ -5,57 +5,70 @@ class WidgetGeneratorTest < Rails::Generators::TestCase destination File.join(Rails.root, "tmp") setup :prepare_destination tests ::Apotomo::Generators::WidgetGenerator - + context "Running rails g apotomo::widget" do context "Gerbil squeak snuggle" do should "create the standard assets" do - + run_generator %w(Gerbil squeak snuggle -t test_unit) + + assert_file "app/widgets/gerbil/gerbil_widget.rb", /class GerbilWidget < Apotomo::Widget/ + assert_file "app/widgets/gerbil/gerbil_widget.rb", /def snuggle/ + assert_file "app/widgets/gerbil/gerbil_widget.rb", /def squeak/ - assert_file "app/widgets/gerbil_widget.rb", /class GerbilWidget < Apotomo::Widget/ - assert_file "app/widgets/gerbil_widget.rb", /def snuggle/ - assert_file "app/widgets/gerbil_widget.rb", /def squeak/ - assert_file "app/widgets/gerbil/snuggle.html.erb", %r(app/widgets/gerbil/snuggle\.html\.erb) - assert_file "app/widgets/gerbil/snuggle.html.erb", %r(

) - assert_file "app/widgets/gerbil/squeak.html.erb", %r(app/widgets/gerbil/squeak\.html\.erb) + assert_file "app/widgets/gerbil/views/snuggle.html.erb", %r(app/widgets/gerbil/views/snuggle\.html\.erb) + assert_file "app/widgets/gerbil/views/snuggle.html.erb", %r(

) + assert_file "app/widgets/gerbil/views/squeak.html.erb", %r(app/widgets/gerbil/views/squeak\.html\.erb) assert_file "test/widgets/gerbil_widget_test.rb", %r(class GerbilWidgetTest < Apotomo::TestCase) assert_file "test/widgets/gerbil_widget_test.rb", %r(widget\(:gerbil\)) end - + + should "create javascript and css assets" do + run_generator %w(Gerbil squeak snuggle -t test_unit) + + assert_file "app/assets/javascripts/widgets/gerbil_widget.coffee", /Define your coffeescript code for the Gerbil widget*/ + assert_file "app/assets/stylesheets/widgets/gerbil_widget.css", /Define your css code for the Gerbil widget*/ + end + should "create haml assets with -e haml" do run_generator %w(Gerbil squeak snuggle -e haml -t test_unit) + + assert_file "app/widgets/gerbil/gerbil_widget.rb", /class GerbilWidget < Apotomo::Widget/ + assert_file "app/widgets/gerbil/gerbil_widget.rb", /def snuggle/ + assert_file "app/widgets/gerbil/gerbil_widget.rb", /def squeak/ + + assert_file "app/widgets/gerbil/views/snuggle.html.haml", %r(app/widgets/gerbil/views/snuggle\.html\.haml) + assert_file "app/widgets/gerbil/views/snuggle.html.haml", %r(%p) + assert_file "app/widgets/gerbil/views/squeak.html.haml", %r(app/widgets/gerbil/views/squeak\.html\.haml) - assert_file "app/widgets/gerbil_widget.rb", /class GerbilWidget < Apotomo::Widget/ - assert_file "app/widgets/gerbil_widget.rb", /def snuggle/ - assert_file "app/widgets/gerbil_widget.rb", /def squeak/ - assert_file "app/widgets/gerbil/snuggle.html.haml", %r(app/widgets/gerbil/snuggle\.html\.haml) - assert_file "app/widgets/gerbil/snuggle.html.haml", %r(%p) - assert_file "app/widgets/gerbil/squeak.html.haml", %r(app/widgets/gerbil/squeak\.html\.haml) assert_file "test/widgets/gerbil_widget_test.rb" end should "create slim assets with -e slim" do run_generator %w(Gerbil squeak snuggle -e slim -t test_unit) + + assert_file "app/widgets/gerbil/gerbil_widget.rb", /class GerbilWidget < Apotomo::Widget/ + assert_file "app/widgets/gerbil/gerbil_widget.rb", /def snuggle/ + assert_file "app/widgets/gerbil/gerbil_widget.rb", /def squeak/ + + assert_file "app/widgets/gerbil/views/snuggle.html.slim", %r(app/widgets/gerbil/views/snuggle\.html\.slim) + assert_file "app/widgets/gerbil/views/snuggle.html.slim", %r(p) + assert_file "app/widgets/gerbil/views/squeak.html.slim", %r(app/widgets/gerbil/views/squeak\.html\.slim) - assert_file "app/widgets/gerbil_widget.rb", /class GerbilWidget < Apotomo::Widget/ - assert_file "app/widgets/gerbil_widget.rb", /def snuggle/ - assert_file "app/widgets/gerbil_widget.rb", /def squeak/ - assert_file "app/widgets/gerbil/snuggle.html.slim", %r(app/widgets/gerbil/snuggle\.html\.slim) - assert_file "app/widgets/gerbil/snuggle.html.slim", %r(p) - assert_file "app/widgets/gerbil/squeak.html.slim", %r(app/widgets/gerbil/squeak\.html\.slim) assert_file "test/widgets/gerbil_widget_test.rb" end - + should "work with namespaces" do run_generator %w(Gerbil::Mouse squeak -t test_unit) - assert_file "app/widgets/gerbil/mouse_widget.rb", /class Gerbil::MouseWidget < Apotomo::Widget/ - assert_file "app/widgets/gerbil/mouse_widget.rb", /def squeak/ - assert_file "app/widgets/gerbil/mouse/squeak.html.erb", %r(app/widgets/gerbil/mouse/squeak\.html\.erb) + assert_file "app/widgets/gerbil/mouse/mouse_widget.rb", /class Gerbil::MouseWidget < Apotomo::Widget/ + assert_file "app/widgets/gerbil/mouse/mouse_widget.rb", /def squeak/ + + assert_file "app/widgets/gerbil/mouse/views/squeak.html.erb", %r(app/widgets/gerbil/mouse/views/squeak\.html\.erb) + assert_file "test/widgets/gerbil/mouse_widget_test.rb" end - + end - end -end + end \ No newline at end of file From 40acbeb7f992ba4c454628a8b96ba41473f7c990 Mon Sep 17 00:00:00 2001 From: Kristian Mandrup Date: Wed, 5 Sep 2012 15:11:00 +0200 Subject: [PATCH 02/33] all test pass --- .gitignore | 1 + test/rails/widget_generator_test.rb | 13 +++++++------ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index f43a7a4..4a9cc1c 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ pkg/* .bundle test/dummy/log/* test/dummy/tmp/* +Gemfile.lock diff --git a/test/rails/widget_generator_test.rb b/test/rails/widget_generator_test.rb index 77eb3b6..d177f34 100644 --- a/test/rails/widget_generator_test.rb +++ b/test/rails/widget_generator_test.rb @@ -20,8 +20,8 @@ class WidgetGeneratorTest < Rails::Generators::TestCase assert_file "app/widgets/gerbil/views/snuggle.html.erb", %r(

) assert_file "app/widgets/gerbil/views/squeak.html.erb", %r(app/widgets/gerbil/views/squeak\.html\.erb) - assert_file "test/widgets/gerbil_widget_test.rb", %r(class GerbilWidgetTest < Apotomo::TestCase) - assert_file "test/widgets/gerbil_widget_test.rb", %r(widget\(:gerbil\)) + assert_file "test/widgets/gerbil/gerbil_widget_test.rb", %r(class GerbilWidgetTest < Apotomo::TestCase) + assert_file "test/widgets/gerbil/gerbil_widget_test.rb", %r(widget\(:gerbil\)) end should "create javascript and css assets" do @@ -42,7 +42,7 @@ class WidgetGeneratorTest < Rails::Generators::TestCase assert_file "app/widgets/gerbil/views/snuggle.html.haml", %r(%p) assert_file "app/widgets/gerbil/views/squeak.html.haml", %r(app/widgets/gerbil/views/squeak\.html\.haml) - assert_file "test/widgets/gerbil_widget_test.rb" + assert_file "test/widgets/gerbil/gerbil_widget_test.rb" end should "create slim assets with -e slim" do @@ -56,7 +56,7 @@ class WidgetGeneratorTest < Rails::Generators::TestCase assert_file "app/widgets/gerbil/views/snuggle.html.slim", %r(p) assert_file "app/widgets/gerbil/views/squeak.html.slim", %r(app/widgets/gerbil/views/squeak\.html\.slim) - assert_file "test/widgets/gerbil_widget_test.rb" + assert_file "test/widgets/gerbil/gerbil_widget_test.rb" end should "work with namespaces" do @@ -67,8 +67,9 @@ class WidgetGeneratorTest < Rails::Generators::TestCase assert_file "app/widgets/gerbil/mouse/views/squeak.html.erb", %r(app/widgets/gerbil/mouse/views/squeak\.html\.erb) - assert_file "test/widgets/gerbil/mouse_widget_test.rb" + assert_file "test/widgets/gerbil/mouse/mouse_widget_test.rb" end end - end \ No newline at end of file + end +end \ No newline at end of file From 2e8b36d628073bc9ad29669d47ed7402600d07db Mon Sep 17 00:00:00 2001 From: Kristian Mandrup Date: Wed, 5 Sep 2012 15:59:05 +0200 Subject: [PATCH 03/33] added api docs --- README.rdoc | 55 ++++++++++-- lib/apotomo/javascript_generator.rb | 127 ++++++++++++++++++++-------- 2 files changed, 140 insertions(+), 42 deletions(-) diff --git a/README.rdoc b/README.rdoc index c0c2d6e..17aa395 100644 --- a/README.rdoc +++ b/README.rdoc @@ -46,11 +46,11 @@ Go and generate a widget stub. $ rails generate apotomo:widget Comments display write -e haml create app/cells/ - create app/cells/comments_widget - create app/cells/comments_widget.rb - create app/cells/comments_widget/display.html.haml - create app/cells/comments_widget/write.html.haml - create test/widgets/comments_widget_test.rb + create app/cells/comments + create app/cells/comments/comments_widget.rb + create app/cells/comments/views/display.html.haml + create app/cells/comments/views/write.html.haml + create test/widgets/comments/comments_widget_test.rb Nothing special. @@ -160,6 +160,51 @@ If that's not what you want, do Apotomo doesn't depend on _any_ JS framework - you choose! +== jQuery helpers + +* element(id) +* update(id, markup) +* replace(id, markup) +* update_id(id, markup) +* replace_id(id, markup) + +Extras (jQuery only): + +* selector_for(var, id, selector) + +Example usage: + +```ruby +top_item = selector_for(:top_item, widget_id, '.item:first') +render js: top_item + append_to(:_top_item, markup) + +Will select `.item:first` under the widget container element as a variable `_apo_top_item` and then append the markup to the DOM element(s) pointed to by that variable. +``` + +* update_text(id, selector, markup) +* append(id, selector, markup) +* prepend(id, selector, markup) +* append_to(selector, markup) +* prepend_to(selector, markup) +* after(selector, markup) +* before(selector, markup) +* replace_all(selector, markup) +* unwrap(selector) +* wrap(selector, markup) +* wrap_inner(selector, markup) +* wrap_all(selector, markup) +* remove(selector) +* remove_class(selector, *classes) +* add_class(selector, *classes) +* toggle_class(selector, *classes) +* toggle_class_fun(selector, fun) +* get_attr(selector, name) +* get_prop(selector, name) +* get_val selector +* get_html(selector) +* empty(selector) + + == Testing Apotomo comes with its own test case and assertions to build rock-solid web components. diff --git a/lib/apotomo/javascript_generator.rb b/lib/apotomo/javascript_generator.rb index ff3d8e9..6a90e55 100644 --- a/lib/apotomo/javascript_generator.rb +++ b/lib/apotomo/javascript_generator.rb @@ -24,27 +24,34 @@ def escape(javascript) module Prototype def prototype; end - def element(id); "$(\"#{id}\")"; end - def update(id, markup); element(id) + '.update("'+escape(markup)+'");'; end - def replace(id, markup); element(id) + '.replace("'+escape(markup)+'");'; end - def update_id(id, markup); update(id, markup); end - def replace_id(id, markup); replace(id, markup); end + def element(selector); "$(\"#{selector}\")"; end + def update(selector, markup); element(selector) + '.update("'+escape(markup)+'");'; end + def replace(selector, markup); element(selector) + '.replace("'+escape(markup)+'");'; end + + alias_method :update_id, :update + alias_method :replace_id, :replace end module Right def right; end - def element(id); "$(\"#{id}\")"; end - def update(id, markup); element(id) + '.update("'+escape(markup)+'");'; end - def replace(id, markup); element(id) + '.replace("'+escape(markup)+'");'; end - def update_id(id, markup); update(id, markup); end - def replace_id(id, markup); replace(id, markup); end + def element(selector); "$(\"#{selector}\")"; end + def update(selector, markup); element(selector) + '.update("'+escape(markup)+'");'; end + def replace(selector, markup); element(selector) + '.replace("'+escape(markup)+'");'; end + + alias_method :update_id, :update + alias_method :replace_id, :replace end module Jquery def jquery; end - def element(id); "$(\"#{id}\")"; end + def element(selector) + selector = selector =~ /^_apo_/ ? selector : "'#{selector}'" + "$(#{selector})" + end + def update(id, markup); element(id) + '.html("'+escape(markup)+'");'; end def replace(id, markup); element(id) + '.replaceWith("'+escape(markup)+'");'; end + def update_id(id, markup); update("##{id}", markup); end def replace_id(id, markup); replace("##{id}", markup); end @@ -52,94 +59,140 @@ def update_text(id, selector, markup) element(id) + ".find(#{selector}).text('#{escape(markup)}');" end + def selector_for var, id, selector + raise ArgumentError, "Must not be an _apo_ selector here: #{selector}" if apo_match?(selector) + "var _apo_#{var} = " + element(id) + ".find('#{selector}');" + end + + def find_element id, selector + element(id) + ".find('#{selector}')" + end + def append(id, selector, markup) - element(id) + ".find(#{selector}).append('#{escape(markup)}');" + find_element(id, selector) + ".append(#{escaped(markup)});" end def prepend(id, selector, markup) - element(id) + ".find(#{selector}).prepend('#{escape(markup)}');" + find_element(id, selector) + ".prepend(#{escaped(markup)});" end def append_to(selector, markup) - "$(#{escape(markup)}').appendTo('#{selector}');" + selector = calc_selector selector + "$(#{escaped(markup)}).appendTo(#{selector});" end def prepend_to(selector, markup) - "$(#{escape(markup)}').prependTo('#{selector}');" + selector = calc_selector selector + "$(#{escaped(markup)}).prependTo(#{selector});" end def after selector, markup - "$('#{selector}').after('#{escape(markup)}');" + selector = calc_selector selector + element(selector) + ".after(#{escaped(markup)});" end def before selector, markup - "$('#{selector}').before('#{escape(markup)}');" + selector = calc_selector selector + element(selector) + ".before(#{escaped(markup)});" end def replace_all(selector, markup) - "$(#{escape(markup)}').replaceAll('#{selector}');" + selector = calc_selector selector + "$(#{escaped(markup)}).replaceAll(#{selector});" end - def unwrap (selector) - "$('#{selector}').unwrap();" + def unwrap(selector) + selector = calc_selector selector + element(selector) + ".unwrap();" end - def wrap (selector, markup) - "$('#{selector}').wrap('#{escape(markup)}');" + def wrap(selector, markup) + selector = calc_selector selector + element(selector) + ".wrap(#{escaped(markup)});" end - def wrap_innet (selector, markup) - "$('#{selector}').wrapInner('#{escape(markup)}');" + def wrap_inner(selector, markup) + selector = calc_selector selector + element(selector) + ".wrapInner(#{escaped(markup)});" end - def wrap_all (selector, markup) - "$('#{selector}').wrap_all('#{escape(markup)}');" + def wrap_all(selector, markup) + selector = calc_selector selector + element(selector) + ".wrap_all(#{escaped(markup)});" end def remove(selector) - "$('#{selector}').remove();" + selector = calc_selector selector + element(selector) + ".remove();" end def remove_class(selector, *classes) classes = classes.flatten.join(' ') - "$('#{selector}').removeClass('#{classes}');" + selector = calc_selector selector + element(selector) + ".removeClass('#{classes}');" end alias_method :remove_classes, :remove_class def add_class(selector, *classes) classes = classes.flatten.join(' ') - "$('#{selector}').addClass('#{classes}');" + selector = calc_selector selector + element(selector) + ".addClass('#{classes}');" end alias_method :add_classes, :add_class def toggle_class(selector, *classes) classes = classes.flatten.join(' ') - "$('#{selector}').toggleClass('#{classes}');" + selector = calc_selector selector + element(selector) + ".toggleClass('#{classes}');" end alias_method :toggle_classes, :toggle_class - def toggle_class_fun(selector, fun) - "$('#{selector}').toggleClass(function() {#{fun}});" + def toggle_class_fun(selector, fun) + selector = calc_selector selector + element(selector) + ".toggleClass(function() {#{fun}});" end def get_attr(selector, name) - "$('#{selector}').attr('#{name}');" + selector = calc_selector selector + element(selector) + ".attr('#{name}');" end def get_prop(selector, name) - "$('#{selector}').prop('#{name}');" + selector = calc_selector selector + element(selector) + ".prop('#{name}');" end def get_val selector - "$('#{selector}').val();" + selector = calc_selector selector + element(selector) + ".val();" end def get_html(selector) - "$('#{selector}').html();" + selector = calc_selector selector + element(selector) + ".html();" end def empty(selector) - "$('#{selector}').empty();" + selector = calc_selector selector + element(selector) + ".empty();" + end + + private + + def escaped markup + "'#{escape(markup)}'" + end + + def apo_match? selector + selector.to_s =~ /^_apo_/ + end + + def calc_selector selector + selector = apo_selector?(selector) ? "_apo_#{selector}" : "'#{selector}'" + end + + def apo_selector? selector + selector.kind_of?(Symbol) && selector.to_s[0..1] == '_' end end end From 492dc03de1c480f5b2d6172378e48ecdcb5d9ade Mon Sep 17 00:00:00 2001 From: Kristian Mandrup Date: Wed, 5 Sep 2012 16:57:52 +0200 Subject: [PATCH 04/33] much cleaner and flexible --- README.rdoc | 44 ++++--- lib/apotomo/javascript_generator.rb | 179 +++++++++++++++------------- 2 files changed, 122 insertions(+), 101 deletions(-) diff --git a/README.rdoc b/README.rdoc index 17aa395..7f54055 100644 --- a/README.rdoc +++ b/README.rdoc @@ -181,29 +181,35 @@ render js: top_item + append_to(:_top_item, markup) Will select `.item:first` under the widget container element as a variable `_apo_top_item` and then append the markup to the DOM element(s) pointed to by that variable. ``` -* update_text(id, selector, markup) -* append(id, selector, markup) -* prepend(id, selector, markup) +Inverse jQuery actions + * append_to(selector, markup) * prepend_to(selector, markup) -* after(selector, markup) -* before(selector, markup) * replace_all(selector, markup) -* unwrap(selector) -* wrap(selector, markup) -* wrap_inner(selector, markup) -* wrap_all(selector, markup) -* remove(selector) -* remove_class(selector, *classes) -* add_class(selector, *classes) -* toggle_class(selector, *classes) -* toggle_class_fun(selector, fun) -* get_attr(selector, name) -* get_prop(selector, name) -* get_val selector -* get_html(selector) -* empty(selector) +Normal jQuery action + +* update_text(id, selector, markup) +* append(id, selector, markup) +* prepend(id, selector, markup) +* after(id, selector, markup) +* before(id, selector, markup) +* unwrap(id, selector) +* wrap(id, selector, markup) +* wrap_inner(id, selector, markup) +* wrap_all(id, selector, markup) +* remove(id, selector) +* remove_class(id, selector, *classes) +* add_class(id, selector, *classes) +* toggle_class(id, selector, *classes) +* toggle_class_fun(id, selector, fun) +* get_attr(id, selector, name) +* get_prop(id, selector, name) +* get_val(id, selector) +* get_html(id, selector) +* empty(id, selector) + +Note: The first argument id is always optional == Testing diff --git a/lib/apotomo/javascript_generator.rb b/lib/apotomo/javascript_generator.rb index 6a90e55..c333734 100644 --- a/lib/apotomo/javascript_generator.rb +++ b/lib/apotomo/javascript_generator.rb @@ -61,137 +61,152 @@ def update_text(id, selector, markup) def selector_for var, id, selector raise ArgumentError, "Must not be an _apo_ selector here: #{selector}" if apo_match?(selector) - "var _apo_#{var} = " + element(id) + ".find('#{selector}');" + "var _apo_#{var} = " + element("##{id}") + ".find('#{selector}');" end - def find_element id, selector - element(id) + ".find('#{selector}')" + def find_element id, selector + if id == nil || apo_selector?(selector) + return element(calc_selector selector) + end + element("##{id}") + ".find('#{selector}')" end - def append(id, selector, markup) - find_element(id, selector) + ".append(#{escaped(markup)});" + [:replace_all, :prepend_to, :append_to].each do |name| + define_method name do |selector, markup| + _jq_inverse_action(selector, markup, name.to_sym) + end end - def prepend(id, selector, markup) - find_element(id, selector) + ".prepend(#{escaped(markup)});" + [:append, :prepend, :after, :before, :wrap, :wrap_inner, :wrap_all].each do |name| + define_method name do |args| + _jquery_action *args, name + end end - def append_to(selector, markup) - selector = calc_selector selector - "$(#{escaped(markup)}).appendTo(#{selector});" - end - - def prepend_to(selector, markup) - selector = calc_selector selector - "$(#{escaped(markup)}).prependTo(#{selector});" - end - - def after selector, markup - selector = calc_selector selector - element(selector) + ".after(#{escaped(markup)});" - end - - def before selector, markup - selector = calc_selector selector - element(selector) + ".before(#{escaped(markup)});" - end - - def replace_all(selector, markup) - selector = calc_selector selector - "$(#{escaped(markup)}).replaceAll(#{selector});" - end - - def unwrap(selector) - selector = calc_selector selector - element(selector) + ".unwrap();" - end - - def wrap(selector, markup) - selector = calc_selector selector - element(selector) + ".wrap(#{escaped(markup)});" - end - - def wrap_inner(selector, markup) - selector = calc_selector selector - element(selector) + ".wrapInner(#{escaped(markup)});" - end - - def wrap_all(selector, markup) - selector = calc_selector selector - element(selector) + ".wrap_all(#{escaped(markup)});" + def unwrap *args + jquery_action *args, 'unwrap()' end - def remove(selector) - selector = calc_selector selector - element(selector) + ".remove();" + def remove *args + _jquery_action *args, 'remove()' end - def remove_class(selector, *classes) + def remove_class(id, selector, *classes) classes = classes.flatten.join(' ') - selector = calc_selector selector - element(selector) + ".removeClass('#{classes}');" + find_element(id, selector) + _js_action("removeClass('#{classes}')") end alias_method :remove_classes, :remove_class - def add_class(selector, *classes) + def add_class(id, selector, *classes) classes = classes.flatten.join(' ') - selector = calc_selector selector - element(selector) + ".addClass('#{classes}');" + find_element(id, selector) + _js_action("addClass('#{classes}')" end alias_method :add_classes, :add_class - def toggle_class(selector, *classes) + def toggle_class(id, selector, *classes) classes = classes.flatten.join(' ') - selector = calc_selector selector - element(selector) + ".toggleClass('#{classes}');" + find_element(id, selector) + _js_action("toggleClass('#{classes}')") end alias_method :toggle_classes, :toggle_class - def toggle_class_fun(selector, fun) - selector = calc_selector selector - element(selector) + ".toggleClass(function() {#{fun}});" + def toggle_class_fun(id, selector, fun) + find_element(id, selector) + _js_action("toggleClass(function() {#{fun}})") end - def get_attr(selector, name) - selector = calc_selector selector - element(selector) + ".attr('#{name}');" + def get_attr(id. selector, name) + find_element(id, selector) + _js_action("attr('#{name}')") end - def get_prop(selector, name) - selector = calc_selector selector - element(selector) + ".prop('#{name}');" + def get_prop(selector, name) + find_element(id, selector) + _js_action("prop('#{name}')") end - def get_val selector - selector = calc_selector selector - element(selector) + ".val();" + def get_val selector + find_element(id, selector) + _js_action("val()") end def get_html(selector) - selector = calc_selector selector - element(selector) + ".html();" + find_element(id, selector) + _js_action("html()") end def empty(selector) - selector = calc_selector selector - element(selector) + ".empty();" + find_element(id, selector) + _js_action("empty()") end private + def _jq_inverse_action(selector, markup, action) + selector = calc_selector selector + action = _js_camelize(action) + "$(#{escaped(markup)}).#{action}(#{selector});" + end + + def _jquery_action *args, &block + args = args.flatten + [id, selector, markup, action] = _extract_args(*args) + action ||= yield if block_given? + raise ArgumentError, "Must take action block or Symbol as last argument" unless action + elem_action = case action + when String + action = _js_camelize(action) + ".#{action};" + else + action = _js_camelize(action) + _action(action, markup) + end + find_element(id, selector) + elem_action + end + def escaped markup "'#{escape(markup)}'" + end + + def _action name, markup + _js_action "#{name}(#{_escaped(markup)})" + end + + def _js_action action + ".#{action};" + end + + def _js_camelize str + str.to_s.camelize.sub(/^\w/, s[0].downcase) + end + + # id, selector, markup, action + def _extract_args *args + [_extract_id(*args), _extract_selector(*args), _extract_markup(*args), _extract_action(*args)] + end + + def _extract_id *args + args = args.flatten + args.size == 4 ? args.first : nil + end + + def _extract_selector *args + args = args.flatten + args.size == 4 ? args[1] : args[0] + end + + def _extract_markup(*args) + args = args.flatten + args.size == 4 ? args[2] : args.last + end + + def _extract_action(*args) + args = args.flatten + args.size == 4 ? args.last : nil end - def apo_match? selector + def _apo_match? selector selector.to_s =~ /^_apo_/ end - def calc_selector selector - selector = apo_selector?(selector) ? "_apo_#{selector}" : "'#{selector}'" + def _calc_selector selector + selector = _apo_selector?(selector) ? "_apo_#{selector}" : "'#{selector}'" end - def apo_selector? selector + def _apo_selector? selector selector.kind_of?(Symbol) && selector.to_s[0..1] == '_' end end From cb80a00e647ef55a4d456418d65ad8110dc4522a Mon Sep 17 00:00:00 2001 From: Kristian Mandrup Date: Wed, 5 Sep 2012 21:26:37 +0200 Subject: [PATCH 05/33] jquery helper tests wip --- README.rdoc | 1 + Rakefile | 2 +- lib/apotomo/javascript_generator.rb | 125 ++++-------------- .../javascript_generator/jquery_helper.rb | 104 +++++++++++++++ .../jquery_helper_test.rb | 37 ++++++ 5 files changed, 168 insertions(+), 101 deletions(-) create mode 100644 lib/apotomo/javascript_generator/jquery_helper.rb create mode 100644 test/javascript_generator/jquery_helper_test.rb diff --git a/README.rdoc b/README.rdoc index 7f54055..be232ea 100644 --- a/README.rdoc +++ b/README.rdoc @@ -170,6 +170,7 @@ Apotomo doesn't depend on _any_ JS framework - you choose! Extras (jQuery only): +* find_element(id, selector) * selector_for(var, id, selector) Example usage: diff --git a/Rakefile b/Rakefile index efa5f0a..9b82f69 100644 --- a/Rakefile +++ b/Rakefile @@ -8,6 +8,6 @@ task :default => :test Rake::TestTask.new(:test) do |test| test.libs << 'test' - test.test_files = FileList['test/*_test.rb', 'test/rails/*_test.rb'] + test.test_files = FileList['test/javascript_generator/*_test.rb'] # FileList['test/*_test.rb', 'test/rails/*_test.rb'] test.verbose = true end diff --git a/lib/apotomo/javascript_generator.rb b/lib/apotomo/javascript_generator.rb index c333734..541503a 100644 --- a/lib/apotomo/javascript_generator.rb +++ b/lib/apotomo/javascript_generator.rb @@ -1,7 +1,10 @@ require 'action_view/helpers/javascript_helper' +require 'apotomo/javascript_generator/jquery_helper' module Apotomo class JavascriptGenerator + autoload :JqueryHelper, 'apotomo/javascript_generator/jquery_helper' + def initialize(framework) raise "No JS framework specified" if framework.blank? extend "apotomo/javascript_generator/#{framework}".camelize.constantize @@ -45,7 +48,7 @@ def replace(selector, markup); element(selector) + '.replace("'+escape(markup module Jquery def jquery; end def element(selector) - selector = selector =~ /^_apo_/ ? selector : "'#{selector}'" + selector = jq_helper.calc_selector selector "$(#{selector})" end @@ -60,154 +63,76 @@ def update_text(id, selector, markup) end def selector_for var, id, selector - raise ArgumentError, "Must not be an _apo_ selector here: #{selector}" if apo_match?(selector) + raise ArgumentError, "Must not be an _apo_ selector here: #{selector}" if jq_helper.apo_match?(selector) "var _apo_#{var} = " + element("##{id}") + ".find('#{selector}');" end - def find_element id, selector - if id == nil || apo_selector?(selector) - return element(calc_selector selector) - end - element("##{id}") + ".find('#{selector}')" - end - [:replace_all, :prepend_to, :append_to].each do |name| define_method name do |selector, markup| - _jq_inverse_action(selector, markup, name.to_sym) + jq_helper.inv_markup_action selector, markup, name.to_sym end end [:append, :prepend, :after, :before, :wrap, :wrap_inner, :wrap_all].each do |name| define_method name do |args| - _jquery_action *args, name + jq_helper.markup_action *args, name end end def unwrap *args - jquery_action *args, 'unwrap()' + jq_helper.markup_action *args, 'unwrap()' end def remove *args - _jquery_action *args, 'remove()' + jq_helper.markup_action *args, 'remove()' end def remove_class(id, selector, *classes) classes = classes.flatten.join(' ') - find_element(id, selector) + _js_action("removeClass('#{classes}')") + jq_helper.jq_action id, selector, "removeClass('#{classes}')" end alias_method :remove_classes, :remove_class def add_class(id, selector, *classes) classes = classes.flatten.join(' ') - find_element(id, selector) + _js_action("addClass('#{classes}')" + jq_helper.jq_action id, selector, "addClass('#{classes}')" end alias_method :add_classes, :add_class def toggle_class(id, selector, *classes) classes = classes.flatten.join(' ') - find_element(id, selector) + _js_action("toggleClass('#{classes}')") + jq_helper.jq_action id, selector, "toggleClass('#{classes}')" end alias_method :toggle_classes, :toggle_class def toggle_class_fun(id, selector, fun) - find_element(id, selector) + _js_action("toggleClass(function() {#{fun}})") + jq_helper.jq_action id, selector, "toggleClass(function() {#{fun}})" end - def get_attr(id. selector, name) - find_element(id, selector) + _js_action("attr('#{name}')") + def get_attr(id, selector, name) + jq_helper.jq_action id, selector, "attr('#{name}')" end - def get_prop(selector, name) - find_element(id, selector) + _js_action("prop('#{name}')") + def get_prop(id, selector, name) + jq_helper.jq_action id, selector, "prop('#{name}')" end - def get_val selector - find_element(id, selector) + _js_action("val()") + def get_val id, selector + jq_helper.jq_action id, selector, "val()" end - def get_html(selector) - find_element(id, selector) + _js_action("html()") + def get_html(id, selector) + jq_helper.jq_action id, selector, "html()" end - def empty(selector) - find_element(id, selector) + _js_action("empty()") + def empty(id, selector) + jq_helper.jq_action id, selector, "empty()" end private - def _jq_inverse_action(selector, markup, action) - selector = calc_selector selector - action = _js_camelize(action) - "$(#{escaped(markup)}).#{action}(#{selector});" - end - - def _jquery_action *args, &block - args = args.flatten - [id, selector, markup, action] = _extract_args(*args) - action ||= yield if block_given? - raise ArgumentError, "Must take action block or Symbol as last argument" unless action - elem_action = case action - when String - action = _js_camelize(action) - ".#{action};" - else - action = _js_camelize(action) - _action(action, markup) - end - find_element(id, selector) + elem_action - end - - def escaped markup - "'#{escape(markup)}'" - end - - def _action name, markup - _js_action "#{name}(#{_escaped(markup)})" - end - - def _js_action action - ".#{action};" - end - - def _js_camelize str - str.to_s.camelize.sub(/^\w/, s[0].downcase) - end - - # id, selector, markup, action - def _extract_args *args - [_extract_id(*args), _extract_selector(*args), _extract_markup(*args), _extract_action(*args)] - end - - def _extract_id *args - args = args.flatten - args.size == 4 ? args.first : nil - end - - def _extract_selector *args - args = args.flatten - args.size == 4 ? args[1] : args[0] - end - - def _extract_markup(*args) - args = args.flatten - args.size == 4 ? args[2] : args.last - end - - def _extract_action(*args) - args = args.flatten - args.size == 4 ? args.last : nil - end - - def _apo_match? selector - selector.to_s =~ /^_apo_/ - end - - def _calc_selector selector - selector = _apo_selector?(selector) ? "_apo_#{selector}" : "'#{selector}'" - end - - def _apo_selector? selector - selector.kind_of?(Symbol) && selector.to_s[0..1] == '_' + def jq_helper + JqueryHelper end end end diff --git a/lib/apotomo/javascript_generator/jquery_helper.rb b/lib/apotomo/javascript_generator/jquery_helper.rb new file mode 100644 index 0000000..86587c1 --- /dev/null +++ b/lib/apotomo/javascript_generator/jquery_helper.rb @@ -0,0 +1,104 @@ +module Apotomo + class JavascriptGenerator + module JqueryHelper + def find_element id, selector + if id == nil || apo_selector?(selector) + return element(selector) + end + element("##{id}") + ".find('#{selector}')" + end + + # - id, selector action + # - selector action + def jq_action *args, action + args = *args, nil, action + id, selector, action = [extract_id(*args), extract_selector(*args), args.last] + find_element(id, selector) + js_action(action) + end + + def inv_markup_action(selector, markup, action) + selector = calc_selector selector + action = js_camelize(action) + "$(#{escaped(markup)}).#{action}(#{selector});" + end + + def markup_action *args, &block + args = args.flatten + id, selector, markup, action = extract_args(*args) + action ||= yield if block_given? + raise ArgumentError, "Must take action block or Symbol as last argument" unless action + elem_action = case action + when String + action = js_camelize(action) + ".#{action};" + else + action = js_camelize(action) + mk_action(action, markup) + end + find_element(id, selector) + elem_action + end + + def escaped markup + "'#{escape(markup)}'" + end + + def mk_action name, markup + js_action "#{name}(#{escaped(markup)})" + end + + def js_action action + ".#{action};" + end + + def js_camelize str + str.to_s.camelize.sub(/^\w/, s[0].downcase) + end + + def calc_selector selector + selector = apo_selector?(selector) ? "_apo_#{selector}" : "'#{selector}'" + end + + def apo_selector? selector + selector.kind_of?(Symbol) && selector.to_s[0..1] == '_' + end + + def apo_match? selector + selector.to_s =~ /^_apo_/ + end + + protected + + def element(selector) + selector = calc_selector selector + "$(#{selector})" + end + + # id, selector, markup, action + def extract_args *args + [extract_id(*args), extract_selector(*args), extract_markup(*args), extract_action(*args)] + end + + def extract_id *args + args = args.flatten + args.size == 4 ? args.first : nil + end + + def extract_selector *args + args = args.flatten + args.size == 4 ? args[1] : args[0] + end + + def extract_markup(*args) + args = args.flatten + args.size == 4 ? args[2] : args.last + end + + def extract_action(*args) + args = args.flatten + args.size == 4 ? args.last : nil + end + + extend self + end + end +end \ No newline at end of file diff --git a/test/javascript_generator/jquery_helper_test.rb b/test/javascript_generator/jquery_helper_test.rb new file mode 100644 index 0000000..52336bb --- /dev/null +++ b/test/javascript_generator/jquery_helper_test.rb @@ -0,0 +1,37 @@ +require 'test_helper' + +class JQueryHelperTest < Test::Unit::TestCase + context "The JQueryHelper" do + setup do + @helper = Apotomo::JavascriptGenerator::JqueryHelper + end + + context '#find_element' do + context 'args: id, selector' do + should "generate full action" do + assert_equal "$('#my_widget').find('.item')", @helper.find_element('my_widget', '.item') + end + end + + context 'args: selector' do + should "generate selector only" do + assert_equal "$('.item')", @helper.find_element(nil, '.item') + end + end + end + + context '#jq_action' do + context 'args: id, selector, action' do + should "generate full action" do + assert_equal "$('#my_widget').find('.item').empty();", @helper.jq_action('my_widget', '.item', 'empty()') + end + end + + context 'args: selector, action' do + should "generate selector only" do + assert_equal "$('.item').empty();", @helper.jq_action('.item', 'empty()') + end + end + end + end +end \ No newline at end of file From 22263c57e19131059dbccd82a5997b892b1a1126 Mon Sep 17 00:00:00 2001 From: Kristian Mandrup Date: Wed, 5 Sep 2012 23:11:15 +0200 Subject: [PATCH 06/33] all tests pass using new jquery helpers --- Rakefile | 2 +- lib/apotomo/javascript_generator.rb | 17 ++++---- .../javascript_generator/jquery_helper.rb | 40 ++++++++++++------- .../jquery_helper_test.rb | 30 ++++++++++++-- test/javascript_generator_test.rb | 24 ++++++++++- 5 files changed, 84 insertions(+), 29 deletions(-) diff --git a/Rakefile b/Rakefile index 9b82f69..0fd2cd1 100644 --- a/Rakefile +++ b/Rakefile @@ -8,6 +8,6 @@ task :default => :test Rake::TestTask.new(:test) do |test| test.libs << 'test' - test.test_files = FileList['test/javascript_generator/*_test.rb'] # FileList['test/*_test.rb', 'test/rails/*_test.rb'] + test.test_files = FileList['test/*_test.rb', 'test/rails/*_test.rb', 'test/javascript_generator/*_test.rb'] test.verbose = true end diff --git a/lib/apotomo/javascript_generator.rb b/lib/apotomo/javascript_generator.rb index 541503a..e103134 100644 --- a/lib/apotomo/javascript_generator.rb +++ b/lib/apotomo/javascript_generator.rb @@ -52,19 +52,20 @@ def element(selector) "$(#{selector})" end - def update(id, markup); element(id) + '.html("'+escape(markup)+'");'; end - def replace(id, markup); element(id) + '.replaceWith("'+escape(markup)+'");'; end + def update(*args, &block) + jq_helper.markup_act :html, *args, &block + end + + def replace(*args, &block) + jq_helper.markup_act :replaceWith, *args, &block + end def update_id(id, markup); update("##{id}", markup); end def replace_id(id, markup); replace("##{id}", markup); end - def update_text(id, selector, markup) - element(id) + ".find(#{selector}).text('#{escape(markup)}');" - end - def selector_for var, id, selector raise ArgumentError, "Must not be an _apo_ selector here: #{selector}" if jq_helper.apo_match?(selector) - "var _apo_#{var} = " + element("##{id}") + ".find('#{selector}');" + "var _apo_#{var} = " + element("##{id}") + ".find(\"#{selector}\");" end [:replace_all, :prepend_to, :append_to].each do |name| @@ -73,7 +74,7 @@ def selector_for var, id, selector end end - [:append, :prepend, :after, :before, :wrap, :wrap_inner, :wrap_all].each do |name| + [:update_text, :append, :prepend, :after, :before, :wrap, :wrap_inner, :wrap_all].each do |name| define_method name do |args| jq_helper.markup_action *args, name end diff --git a/lib/apotomo/javascript_generator/jquery_helper.rb b/lib/apotomo/javascript_generator/jquery_helper.rb index 86587c1..d9c114b 100644 --- a/lib/apotomo/javascript_generator/jquery_helper.rb +++ b/lib/apotomo/javascript_generator/jquery_helper.rb @@ -5,7 +5,7 @@ def find_element id, selector if id == nil || apo_selector?(selector) return element(selector) end - element("##{id}") + ".find('#{selector}')" + element("##{id}") + ".find(\"#{selector}\")" end # - id, selector action @@ -22,9 +22,18 @@ def inv_markup_action(selector, markup, action) "$(#{escaped(markup)}).#{action}(#{selector});" end + def markup_act name, *args, &block + id, selector = extract_args *args, &block + markup = block_given? ? yield : args.last + + find_element(id, selector) + mk_action(name, markup) + end + + # - id, selector, markup, action + # - selector, markup, action def markup_action *args, &block args = args.flatten - id, selector, markup, action = extract_args(*args) + id, selector, markup, action = extract_args(*args, &block) action ||= yield if block_given? raise ArgumentError, "Must take action block or Symbol as last argument" unless action elem_action = case action @@ -37,12 +46,14 @@ def markup_action *args, &block end find_element(id, selector) + elem_action end - + + include ::ActionView::Helpers::JavaScriptHelper + def escaped markup - "'#{escape(markup)}'" + "\"#{escape_javascript(markup)}\"" end - def mk_action name, markup + def mk_action name, markup js_action "#{name}(#{escaped(markup)})" end @@ -51,11 +62,12 @@ def js_action action end def js_camelize str - str.to_s.camelize.sub(/^\w/, s[0].downcase) + str = str.to_s + str.camelize.sub(/^\w/, str[0].downcase) end def calc_selector selector - selector = apo_selector?(selector) ? "_apo_#{selector}" : "'#{selector}'" + selector = apo_selector?(selector) ? "_apo_#{selector}" : "\"#{selector}\"" end def apo_selector? selector @@ -74,8 +86,8 @@ def element(selector) end # id, selector, markup, action - def extract_args *args - [extract_id(*args), extract_selector(*args), extract_markup(*args), extract_action(*args)] + def extract_args *args, &block + [extract_id(*args), extract_selector(*args), extract_markup(*args, &block), extract_action(*args, &block)] end def extract_id *args @@ -88,14 +100,12 @@ def extract_selector *args args.size == 4 ? args[1] : args[0] end - def extract_markup(*args) - args = args.flatten - args.size == 4 ? args[2] : args.last + def extract_markup(*args, &block) + block_given? ? args.last : args[-2] end - def extract_action(*args) - args = args.flatten - args.size == 4 ? args.last : nil + def extract_action(*args, &block) + args.last unless block_given? end extend self diff --git a/test/javascript_generator/jquery_helper_test.rb b/test/javascript_generator/jquery_helper_test.rb index 52336bb..2c27e32 100644 --- a/test/javascript_generator/jquery_helper_test.rb +++ b/test/javascript_generator/jquery_helper_test.rb @@ -9,13 +9,13 @@ class JQueryHelperTest < Test::Unit::TestCase context '#find_element' do context 'args: id, selector' do should "generate full action" do - assert_equal "$('#my_widget').find('.item')", @helper.find_element('my_widget', '.item') + assert_equal "$(\"#my_widget\").find(\".item\")", @helper.find_element('my_widget', '.item') end end context 'args: selector' do should "generate selector only" do - assert_equal "$('.item')", @helper.find_element(nil, '.item') + assert_equal "$(\".item\")", @helper.find_element(nil, '.item') end end end @@ -23,15 +23,37 @@ class JQueryHelperTest < Test::Unit::TestCase context '#jq_action' do context 'args: id, selector, action' do should "generate full action" do - assert_equal "$('#my_widget').find('.item').empty();", @helper.jq_action('my_widget', '.item', 'empty()') + assert_equal "$(\"#my_widget\").find(\".item\").empty();", @helper.jq_action('my_widget', '.item', 'empty()') end end context 'args: selector, action' do should "generate selector only" do - assert_equal "$('.item').empty();", @helper.jq_action('.item', 'empty()') + assert_equal "$(\".item\").empty();", @helper.jq_action('.item', 'empty()') end end end + + context '#inv_markup_action' do + context 'args: selector, markup, action' do + should "generate full action" do + assert_equal "$(\"hello > world<\\/b>\").replaceAll(\"#my_widget\");", @helper.inv_markup_action('#my_widget', 'hello > world', :replace_all) + end + end + end + + context '#markup_action' do + context 'args: id, selector, markup, action' do + should "generate full action" do + assert_equal "$(\"#my_widget\").find(\".item\").append(\"hello > world<\\/b>\");", @helper.markup_action('my_widget', '.item', 'hello > world', :append) + end + end + + context 'args: selector, markup, action' do + should "generate selector action" do + assert_equal "$(\".item\").append(\"hello > world<\\/b>\");", @helper.markup_action('.item', 'hello > world', :append) + end + end + end end end \ No newline at end of file diff --git a/test/javascript_generator_test.rb b/test/javascript_generator_test.rb index a188126..78a5a01 100644 --- a/test/javascript_generator_test.rb +++ b/test/javascript_generator_test.rb @@ -64,6 +64,10 @@ class JavascriptGeneratorTest < Test::Unit::TestCase setup do @gen = Apotomo::JavascriptGenerator.new(:jquery) end + + should 'create _apo_xyz javascript variable for jQuery element' do + assert_equal "var _apo_xyz = $(\"#my_id\").find(\".item\");", @gen.selector_for(:xyz, 'my_id', '.item') + end should "respond to jquery" do assert_respond_to @gen, :jquery @@ -72,14 +76,32 @@ class JavascriptGeneratorTest < Test::Unit::TestCase should "respond to replace" do assert_equal "$(\"#drinks\").replaceWith(\"EMPTY!\");", @gen.replace("#drinks", 'EMPTY!') end + + should "escape" do + assert_equal "

  • <\\/li>", @gen.escape('
  • ') + end + + should "respond to replace and escape" do + assert_equal "$(\"#drinks\").replaceWith(\"
  • <\\/li>\");", @gen.replace("#drinks", '
  • ') + end + + should "respond to replace with block" do + txt = @gen.replace("#drinks") { '
  • '} + assert_equal "$(\"#drinks\").replaceWith(\"
  • <\\/li>\");", txt + end should "respond to replace_id" do assert_equal "$(\"#drinks\").replaceWith(\"EMPTY!\");", @gen.replace_id("drinks", 'EMPTY!') end - should "respond to update" do + should "respond to update and escape" do assert_equal "$(\"#drinks\").html(\"
  • <\\/li>\");", @gen.update("#drinks", '
  • ') end + + should "respond to update with block" do + txt = @gen.update("#drinks") { '
  • '} + assert_equal "$(\"#drinks\").html(\"
  • <\\/li>\");", txt + end should "respond to update_id" do assert_equal "$(\"#drinks\").html(\"EMPTY!\");", @gen.update_id("drinks", 'EMPTY!') From 27c26bffb41f00d058638cbc2def5a7d20a13343 Mon Sep 17 00:00:00 2001 From: Kristian Mandrup Date: Wed, 5 Sep 2012 23:11:46 +0200 Subject: [PATCH 07/33] bump version --- lib/apotomo/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/apotomo/version.rb b/lib/apotomo/version.rb index 26a5111..97cca6b 100644 --- a/lib/apotomo/version.rb +++ b/lib/apotomo/version.rb @@ -1,3 +1,3 @@ module Apotomo - VERSION = '1.2.3' + VERSION = '1.2.4' end From 25e1d5fead79e4b9cb7a4f596611cf612372fe38 Mon Sep 17 00:00:00 2001 From: Kristian Mandrup Date: Thu, 6 Sep 2012 08:44:47 +0200 Subject: [PATCH 08/33] added more view helpers for js --- lib/apotomo/javascript_generator.rb | 1 - lib/apotomo/rails/view_helper.rb | 7 ++++++- lib/apotomo/rails/view_helper/ajax.rb | 9 +++++++++ lib/apotomo/rails/view_helper/dragn_drop.rb | 19 +++++++++++++++++++ 4 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 lib/apotomo/rails/view_helper/ajax.rb create mode 100644 lib/apotomo/rails/view_helper/dragn_drop.rb diff --git a/lib/apotomo/javascript_generator.rb b/lib/apotomo/javascript_generator.rb index e103134..3d8ae10 100644 --- a/lib/apotomo/javascript_generator.rb +++ b/lib/apotomo/javascript_generator.rb @@ -1,5 +1,4 @@ require 'action_view/helpers/javascript_helper' -require 'apotomo/javascript_generator/jquery_helper' module Apotomo class JavascriptGenerator diff --git a/lib/apotomo/rails/view_helper.rb b/lib/apotomo/rails/view_helper.rb index 872e604..c73b8dc 100644 --- a/lib/apotomo/rails/view_helper.rb +++ b/lib/apotomo/rails/view_helper.rb @@ -15,8 +15,13 @@ module Rails # - children.each do |kid| # = render_widget kid module ViewHelper + autoload :DragnDrop, 'apotomo/rails/view_helper/dragn_drop' + autoload :Ajax, 'apotomo/rails/view_helper/ajax' + delegate :children, :url_for_event, :widget_id, :to => :controller + include DragnDrop, Ajax + # Returns the app JavaScript generator. def js_generator Apotomo.js_generator @@ -50,6 +55,6 @@ def widget_div(options={}, &block) options.reverse_merge!(:id => widget_id) content_tag(:div, options, &block) end - end + end end end diff --git a/lib/apotomo/rails/view_helper/ajax.rb b/lib/apotomo/rails/view_helper/ajax.rb new file mode 100644 index 0000000..a1357c0 --- /dev/null +++ b/lib/apotomo/rails/view_helper/ajax.rb @@ -0,0 +1,9 @@ +module Apotomo::Rails::ViewHelper + module Ajax + # fx + # ajax_url "#trashbin", "&id=" + ui.draggable.attr("data-id") + def ajax_url selector, params + %Q{$.ajax({url: $("#{selector}").attr("data-event-url") + #{params};})} + end + end +end diff --git a/lib/apotomo/rails/view_helper/dragn_drop.rb b/lib/apotomo/rails/view_helper/dragn_drop.rb new file mode 100644 index 0000000..4d214ef --- /dev/null +++ b/lib/apotomo/rails/view_helper/dragn_drop.rb @@ -0,0 +1,19 @@ +module Apotomo::Rails::ViewHelper + module DragnDrop + # fx + # droppable "#trashbin", ajax_url("#trashbin", "&id=" + ui.draggable.attr("data-id")) + def droppable selector, &block + %Q{$("#{selector}").droppable({ + drop: function(event, ui) { + #{yield} + } +}} + end + + # fx + # draggable "##{widget_id} li", {revert: "invalid"} + def draggable selector, options + %Q{$(\"#{selector}\").draggable(#{options.to_json});} + end + end +end From b17ec0890101f9cd1b25bfbfc32c29d8b4010ce2 Mon Sep 17 00:00:00 2001 From: Kristian Mandrup Date: Thu, 6 Sep 2012 09:36:19 +0200 Subject: [PATCH 09/33] patch cell rendering to use views folder --- lib/apotomo.rb | 1 + lib/apotomo/cell/rendering.rb | 8 ++++++++ 2 files changed, 9 insertions(+) create mode 100644 lib/apotomo/cell/rendering.rb diff --git a/lib/apotomo.rb b/lib/apotomo.rb index 82db695..3102211 100644 --- a/lib/apotomo.rb +++ b/lib/apotomo.rb @@ -22,6 +22,7 @@ def setup require 'apotomo/widget' require 'apotomo/railtie' +require 'apotomo/cell/rendering' require 'apotomo/widget_shortcuts' require 'apotomo/rails/controller_methods' require 'apotomo/javascript_generator' diff --git a/lib/apotomo/cell/rendering.rb b/lib/apotomo/cell/rendering.rb new file mode 100644 index 0000000..d3e7c24 --- /dev/null +++ b/lib/apotomo/cell/rendering.rb @@ -0,0 +1,8 @@ +module Cell + module Rendering + def render(*args) + view_name = File.join('views', self.action_name) + render_view_for(view_name, *args) + end + end +end \ No newline at end of file From 54323193581bbf7d7267594b81f497a19842a491 Mon Sep 17 00:00:00 2001 From: Kristian Mandrup Date: Thu, 6 Sep 2012 11:56:23 +0200 Subject: [PATCH 10/33] all specs pass - much better coffe/js integration using js/coffee namespaces and classes for widgets :) --- .gitignore | 1 + README.rdoc | 172 ++++++- lib/apotomo/cell/rendering.rb | 7 +- lib/apotomo/javascript_generator.rb | 21 + lib/generators/apotomo/widget_generator.rb | 17 +- lib/generators/templates/widget.coffee | 3 + lib/generators/templates/widget.js | 9 + test/widgets/mouse/{ => views}/eat.erb | 0 .../widgets/mouse/{ => views}/eating.html.erb | 0 .../mouse/{ => views}/educate.html.erb | 0 test/widgets/mouse/{ => views}/feed.html.erb | 0 .../mouse/{ => views}/make_me_squeak.html.erb | 0 .../mouse/{ => views}/snuggle.html.erb | 0 vendor/assets/apotomo/Namespace.js | 484 ++++++++++++++++++ 14 files changed, 690 insertions(+), 24 deletions(-) create mode 100644 lib/generators/templates/widget.js rename test/widgets/mouse/{ => views}/eat.erb (100%) rename test/widgets/mouse/{ => views}/eating.html.erb (100%) rename test/widgets/mouse/{ => views}/educate.html.erb (100%) rename test/widgets/mouse/{ => views}/feed.html.erb (100%) rename test/widgets/mouse/{ => views}/make_me_squeak.html.erb (100%) rename test/widgets/mouse/{ => views}/snuggle.html.erb (100%) create mode 100644 vendor/assets/apotomo/Namespace.js diff --git a/.gitignore b/.gitignore index 4a9cc1c..66090d4 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ pkg/* test/dummy/log/* test/dummy/tmp/* Gemfile.lock +.DS_Store diff --git a/README.rdoc b/README.rdoc index be232ea..37e89a3 100644 --- a/README.rdoc +++ b/README.rdoc @@ -42,17 +42,36 @@ Let's wrap that comments block in a widget. == Generate -Go and generate a widget stub. - - $ rails generate apotomo:widget Comments display write -e haml - create app/cells/ - create app/cells/comments - create app/cells/comments/comments_widget.rb - create app/cells/comments/views/display.html.haml - create app/cells/comments/views/write.html.haml - create test/widgets/comments/comments_widget_test.rb - -Nothing special. + $ rails g apotomo:widget comments show -e haml + invoke haml + create app/widgets/comments/views/show.html.haml + invoke rspec + create spec/widgets/comments/comments_widget_spec.rb + create app/widgets/comments/comments_widget.rb + create app/assets/stylesheets/widgets/comments_widget.css + create app/assets/javascripts/widgets/comments_widget.coffee + +Go and generate a widget TopBar stub. + + $ rails g apotomo:widget TopBar show -e haml + invoke haml + create app/widgets/top_bar/views/show.html.haml + invoke rspec + create spec/widgets/top_bar/top_bar_widget_spec.rb + create app/widgets/top_bar/top_bar_widget.rb + create app/assets/stylesheets/widgets/top_bar_widget.css + create app/assets/javascripts/widgets/top_bar_widget.coffee + +And a Form widget within the TopBar namespace + + $ rails g apotomo:widget TopBar::Form show -e haml + invoke haml + create app/widgets/top_bar/form/views/show.html.haml + invoke rspec + create spec/widgets/top_bar/form/form_widget_spec.rb + create app/widgets/top_bar/form/form_widget.rb + create app/assets/javascripts/widgets/top_bar/form_widget.coffee + create app/assets/stylesheets/widgets/top_bar/form_widget.css == Plug it in @@ -86,12 +105,12 @@ A widget is like a cell which is like a mini-controller. class CommentsWidget < Apotomo::Widget responds_to_event :post - def display(args) + def show(args) @comments = args[:post].comments # the parameter from outside. render end -Having +display+ as the default state when rendering, this method collects comments to show and renders its view. +Having +show+ as the default state when rendering, this method collects comments to show and renders its view. And look at line 2 - if encountering a :post event we invoke +#post+, which is simply another state. How cool is that? @@ -99,7 +118,7 @@ And look at line 2 - if encountering a :post event we invoke +#post+, w @comment = Comment.new(:post_id => evt[:post_id]) @comment.update_attributes evt[:comment] # a bit like params[]. - update :state => :display + update :state => :show end end @@ -107,7 +126,7 @@ And look at line 2 - if encountering a :post event we invoke +#post+, w The event is processed with three steps in our widget: * create the new comment -* re-render the +display+ state +* re-render the +show+ state * update itself on the page Apotomo helps you focusing on your app and takes away the pain of action dispatching and page updating. @@ -116,7 +135,7 @@ Apotomo helps you focusing on your app and takes away the pain of action disp So how and where is the :post event triggered? -Take a look at the widget's view display.html.haml. +Take a look at the widget's view show.html.haml. = widget_div do %ul - for c in @comments @@ -172,6 +191,7 @@ Extras (jQuery only): * find_element(id, selector) * selector_for(var, id, selector) +* call_fun(name, id, hash) Example usage: @@ -182,13 +202,13 @@ render js: top_item + append_to(:_top_item, markup) Will select `.item:first` under the widget container element as a variable `_apo_top_item` and then append the markup to the DOM element(s) pointed to by that variable. ``` -Inverse jQuery actions +Inverse jQuery manipulation API * append_to(selector, markup) * prepend_to(selector, markup) * replace_all(selector, markup) -Normal jQuery action +Normal jQuery manipulation API * update_text(id, selector, markup) * append(id, selector, markup) @@ -204,13 +224,125 @@ Normal jQuery action * add_class(id, selector, *classes) * toggle_class(id, selector, *classes) * toggle_class_fun(id, selector, fun) +* empty(id, selector) + +jQuery "get" functions + * get_attr(id, selector, name) * get_prop(id, selector, name) * get_val(id, selector) * get_html(id, selector) -* empty(id, selector) -Note: The first argument id is always optional +The first argument `id` is always optional. It is meant to be used to select the div for the widget. The `selector` then finds one or more elements within the widget to perform the action on. + +These functions should be used sparingly, for _Proof of Concept_ only if possible. It is far better to have javascript asset files if possible (== non-intrusive javascript). + +A non-intrusive approach could involve the use of the `call_fun` method which takes the `name` of the javascript method to call, the widget `id` and a hash of data. + +Example: + +`call_fun :toggle_active, 'TopBar', {item: 'item:first'}` + +This will result in the call: + +```javascript +Widget.TopBar.toggleActive({'item': 'item:first'}); +``` + +This ensures that all our javascript for a widget is namespace contained nicely. + +In your `application.js` manifest file + +``` +//= require apotomo/Namespace +``` + +In the `top_bar.coffee` file the following is generated for our convenience + +```javascript +Widget.TopBar = Namespace('Apotomo.Widget.TopBar'); + +Widget.TopBar = {} +``` + +We can then implement clean non-intrusive, namespaced javascript functionality as follows: + +```javascript +Widget.TopBar = Namespace('Apotomo.Widget.TopBar'); + +Widget.TopBar = { + update: function(item) { + 'updated:' + item } + }, + + toggleActive: function(widget_id, options) { + item = options['item']; + // do some toggle magic!!! + $(widget_id).find(item).toggleClass('active'); + } +} +``` + +Note that you can use fx jQuery [extend](http://api.jquery.com/jQuery.extend/) to extend javascript widget functionality similar to modules (prototypical inheritance). +There are many other powerful javascript libraries out there (fx Base2, Prototype.js, JS.Class) that can be used to great effect for OOP javascript with inheritance etc. + +```coffeescript +$.extend Widget.Admin.TopBar, Widget.TopBar +``` + +Here we extended the `Admin.TopBar` with base functionality from `TopBar` :) + +== Using Coffeescript + +Coffeescript has built in [class structure](http://coffeescript.org/#classes). +and [namespaces](http://spin.atomicobject.com/2011/04/01/namespace-a-coffeescript-nugget/). Also see [namespaced classes](http://stackoverflow.com/questions/8730859/classes-within-coffeescript-namespace) + +```coffeescript +namespace "Widget.TopBar", (exports) -> + # add functions or classes here + bar: (foo) -> +``` + +```coffeescript +class MyFirstWidget + constructor: (options = {}) -> + @options = options + @name = options.name + myFunc: () -> + console.log 'works' + +namespace "Widget.TopBar", (exports) -> + exports.MyFirstWidget = MyFirstWidget + + toggleActive: (widget_id, options) -> + item = options.item + $(widget_id).find(item).toggleClass 'active' +``` + +```coffeescript +firstWidget = new Widget.TopBar.MyFirstWidget +firstWidget.myFunc +``` + +=== javascript Widget instance + +For an Ajax enabled page, you can create javascript widget instances in the `Widgets` namespace. + +```coffeescript +Widgets.firstWidget = new Widget.TopBar.MyFirstWidget name: "cool widget" +`` + +Then you can update an existing widget instance from your Widget (controller) using `call_widget` as follows: + +```ruby +call_widget :firstWidget, :flash_light, action: 'search' +``` + +Which will result in the statement: + +```javascript +Widgets.firstWidget.flashLight('action': 'search'); +``` == Testing diff --git a/lib/apotomo/cell/rendering.rb b/lib/apotomo/cell/rendering.rb index d3e7c24..93162c0 100644 --- a/lib/apotomo/cell/rendering.rb +++ b/lib/apotomo/cell/rendering.rb @@ -1,7 +1,12 @@ module Cell module Rendering def render(*args) - view_name = File.join('views', self.action_name) + if args.first.kind_of?(Hash) && args.first[:view] + hash = args.first + hash[:view] = File.join('views', hash[:view].to_s) + args = [hash, args[1..-1]] + end + view_name = File.join('views', self.action_name || '') render_view_for(view_name, *args) end end diff --git a/lib/apotomo/javascript_generator.rb b/lib/apotomo/javascript_generator.rb index 3d8ae10..3d9a711 100644 --- a/lib/apotomo/javascript_generator.rb +++ b/lib/apotomo/javascript_generator.rb @@ -1,4 +1,5 @@ require 'action_view/helpers/javascript_helper' +require "active_support/core_ext" # for Hash.to_json etc. module Apotomo class JavascriptGenerator @@ -67,6 +68,26 @@ def selector_for var, id, selector "var _apo_#{var} = " + element("##{id}") + ".find(\"#{selector}\");" end + # call existing widget + # - call_fun :update, :top_bar, item: 1 + + # --> Widget.TopBar.update('action': 1) + def call_fun name, id, hash + function_name = jq_helper.js_camelize name + namespace = "Widget.#{id.to_s.camelize}" + "#{namespace}.#{function_name}(\"##{id}\", #{hash.to_json});" + end + + # call existing widget + # - call_widget :top_bar, :flash_light, action: 'search' + + # --> Widgets.topBar.flashLight('action': 'search') + + def call_widget name, fun, hash + function_name = jq_helper.js_camelize name + "#{name}.#{function_name}(#{hash.to_json});" + end + [:replace_all, :prepend_to, :append_to].each do |name| define_method name do |selector, markup| jq_helper.inv_markup_action selector, markup, name.to_sym diff --git a/lib/generators/apotomo/widget_generator.rb b/lib/generators/apotomo/widget_generator.rb index 56c97ae..122fae1 100644 --- a/lib/generators/apotomo/widget_generator.rb +++ b/lib/generators/apotomo/widget_generator.rb @@ -9,7 +9,6 @@ def base_path File.join('app/widgets', class_path, file_name) end - def js_path File.join('app/assets/javascripts/widgets', class_path, file_name) end @@ -39,14 +38,26 @@ class WidgetGenerator < ::Cells::Generators::Base check_class_collision :suffix => "Widget" + class_option :js, :type => :boolean, :default => false, :desc => 'Generate javascript asset file' + def create_cell_file template 'widget.rb', File.join(base_path, "#{file_name}_widget.rb") end - def create_assets_files - template 'widget.coffee', "#{js_path}_widget.coffee" + def create_stylesheet_file template 'widget.css', "#{css_path}_widget.css" end + + def creates_script_file + return template 'widget.coffee', "#{js_path}_widget.coffee" if !javascript? + template 'widget.js', "#{js_path}_widget.js" + end + + protected + + def javascript? + options[:js] + end end end end \ No newline at end of file diff --git a/lib/generators/templates/widget.coffee b/lib/generators/templates/widget.coffee index 9168ed7..f3523b5 100644 --- a/lib/generators/templates/widget.coffee +++ b/lib/generators/templates/widget.coffee @@ -1 +1,4 @@ # Define your coffeescript code for the <%= class_name %> widget +namespace "Widget.TopBar", (exports) -> + # add functions or classes here + bar: (foo) -> \ No newline at end of file diff --git a/lib/generators/templates/widget.js b/lib/generators/templates/widget.js new file mode 100644 index 0000000..b0e906e --- /dev/null +++ b/lib/generators/templates/widget.js @@ -0,0 +1,9 @@ +var Widget.<%= class_name %> = Namespace('Apotomo.Widget.<%= class_name %>'); + +Widget.<%= class_name %> = { + // add widget functions here + foo: function(bar) {}, + + bar: function(foor) {} +} + diff --git a/test/widgets/mouse/eat.erb b/test/widgets/mouse/views/eat.erb similarity index 100% rename from test/widgets/mouse/eat.erb rename to test/widgets/mouse/views/eat.erb diff --git a/test/widgets/mouse/eating.html.erb b/test/widgets/mouse/views/eating.html.erb similarity index 100% rename from test/widgets/mouse/eating.html.erb rename to test/widgets/mouse/views/eating.html.erb diff --git a/test/widgets/mouse/educate.html.erb b/test/widgets/mouse/views/educate.html.erb similarity index 100% rename from test/widgets/mouse/educate.html.erb rename to test/widgets/mouse/views/educate.html.erb diff --git a/test/widgets/mouse/feed.html.erb b/test/widgets/mouse/views/feed.html.erb similarity index 100% rename from test/widgets/mouse/feed.html.erb rename to test/widgets/mouse/views/feed.html.erb diff --git a/test/widgets/mouse/make_me_squeak.html.erb b/test/widgets/mouse/views/make_me_squeak.html.erb similarity index 100% rename from test/widgets/mouse/make_me_squeak.html.erb rename to test/widgets/mouse/views/make_me_squeak.html.erb diff --git a/test/widgets/mouse/snuggle.html.erb b/test/widgets/mouse/views/snuggle.html.erb similarity index 100% rename from test/widgets/mouse/snuggle.html.erb rename to test/widgets/mouse/views/snuggle.html.erb diff --git a/vendor/assets/apotomo/Namespace.js b/vendor/assets/apotomo/Namespace.js new file mode 100644 index 0000000..db8900f --- /dev/null +++ b/vendor/assets/apotomo/Namespace.js @@ -0,0 +1,484 @@ +/* +Script: Namespace.js + Namespace utility + +Copyright: + Copyright (c) 2009 Maxime Bouroumeau-Fuseau + +License: + MIT-style license. + +Version: + 1.1 +*/ +var Namespace = (function() { + + var _listeners = {}; + var _includedIdentifiers = []; + + /** + * Returns an object in an array unless the object is an array + * + * @param mixed obj + * @return Array + */ + var _toArray = function(obj) { + // checks if it's an array + if (typeof(obj) == 'object' && obj.sort) { + return obj; + } + return new Array(obj); + }; + + /** + * Creates an XMLHttpRequest object + * + * @return XMLHttpRequest + */ + var _createXmlHttpRequest = function() { + var xhr; + try { xhr = new XMLHttpRequest() } catch(e) { + try { xhr = new ActiveXObject("Msxml2.XMLHTTP.6.0") } catch(e) { + try { xhr = new ActiveXObject("Msxml2.XMLHTTP.3.0") } catch(e) { + try { xhr = new ActiveXObject("Msxml2.XMLHTTP") } catch(e) { + try { xhr = new ActiveXObject("Microsoft.XMLHTTP") } catch(e) { + throw new Error( "This browser does not support XMLHttpRequest." ) + } + } + } + } + } + return xhr; + }; + + /** + * Checks if an http request is successful based on its status code. + * Borrowed from dojo (http://www.dojotoolkit.org). + * + * @param Integer status Http status code + * @return Boolean + */ + var _isHttpRequestSuccessful = function(status) { + return (status >= 200 && status < 300) || // Boolean + status == 304 || // allow any 2XX response code + status == 1223 || // get it out of the cache + (!status && (location.protocol == "file:" || location.protocol == "chrome:") ); // Internet Explorer mangled the status code + }; + + /** + * Creates a script tag with the specified data as content + * + * @param String data The content of the script + */ + var _createScript = function(data) { + var script = document.createElement('script'); + script.type = 'text/javascript'; + script.text = data; + document.body.appendChild(script); + }; + + /** + * Dispatches an event + * + * @param String eventName + * @param Object properties + */ + var _dispatchEvent = function(eventName, properties) { + if (!_listeners[eventName]) return; + properties.event = eventName; + for (var i = 0; i < _listeners[eventName].length; i++) { + _listeners[eventName][i](properties); + } + }; + + /** + * Creates an Object following the specified namespace identifier. + * + * @public + * @param String identifier The namespace string + * @param Object klasses (OPTIONAL) An object which properties will be added to the namespace + * @return Object The most inner object + */ + var _namespace = function(identifier) { + var klasses = arguments[1] || false; + var ns = window; + + if (identifier != '') { + var parts = identifier.split(Namespace.separator); + for (var i = 0; i < parts.length; i++) { + if (!ns[parts[i]]) { + ns[parts[i]] = {}; + } + ns = ns[parts[i]]; + } + } + + if (klasses) { + for (var klass in klasses) { + ns[klass] = klasses[klass]; + } + } + + _dispatchEvent('create', { 'identifier': identifier }); + return ns; + }; + + /** + * Checks if the specified identifier is defined + * + * @public + * @param String identifier The namespace string + * @return Boolean + */ + _namespace.exist = function(identifier) { + if (identifier == '') return true; + + var parts = identifier.split(Namespace.separator); + var ns = window; + for (var i = 0; i < parts.length; i++) { + if (!ns[parts[i]]) { + return false; + } + ns = ns[parts[i]]; + } + + return true; + }; + + /** + * Maps an identifier to a uri. Is public so it can be overriden by custom scripts. + * + * @public + * @param String identifier The namespace identifier + * @return String The uri + */ + _namespace.mapIdentifierToUri = function(identifier) { + var regexp = new RegExp('\\' + Namespace.separator, 'g'); + return Namespace.baseUri + identifier.replace(regexp, '/') + '.js'; + }; + + /** + * Loads a remote script atfer mapping the identifier to an uri + * + * @param String identifier The namespace identifier + * @param Function successCallback When set, the file will be loaded asynchronously. Will be called when the file is loaded + * @param Function errorCallback Callback to be called when an error occurs + * @return Boolean Success of failure when loading synchronously + */ + _loadScript = function(identifier) { + var successCallback = arguments[1] || false; + var errorCallback = arguments[2] || false; + var async = successCallback != false; + var uri = _namespace.mapIdentifierToUri(identifier); + var event = { 'identifier': identifier, 'uri': uri, 'async': async, 'callback': successCallback }; + + var xhr = _createXmlHttpRequest(); + xhr.open("GET", uri, async); + + if (async) { + xhr.onreadystatechange = function() { + if (xhr.readyState == 4) { + if (_isHttpRequestSuccessful(xhr.status || 0)) { + _createScript(xhr.responseText); + _dispatchEvent('include', event); + successCallback(); + return; + } + event.status = xhr.status; + _dispatchEvent('includeError', event); + errorCallback && errorCallback(); + } + }; + } + + xhr.send(null); + + if (!async) { + if (_isHttpRequestSuccessful(xhr.status || 0)) { + _createScript(xhr.responseText); + _dispatchEvent('include', event); + return true; + } + event.status = xhr.status; + _dispatchEvent('includeError', event); + return false; + } + }; + + /** + * Includes a remote javascript file identified by the specified namespace string. The identifier + * must point to a class. Separators in the string will be converted to slashes and the .js extension will be appended. + * + * @public + * @param String identifier The namespace string + * @param Function callback (OPTIONAL) A function to call when the remote script has been included + */ + _namespace.include = function(identifier) { + var successCallback = arguments[1] || false; + var errorCallback = arguments[2] || false; + + // checks if the identifier is not already included + if (_includedIdentifiers[identifier]) { + successCallback && successCallback(); + return true; + } + + if (successCallback) { + _loadScript(identifier, function() { + _includedIdentifiers[identifier] = true; + successCallback(); + }, errorCallback); + } else { + if (_loadScript(identifier)) { + _includedIdentifiers[identifier] = true; + return true; + } + return false; + } + }; + + /** + * Imports properties from the specified namespace to the global space (ie. under window) + * + * The identifier string can contain the * wildcard character as its last segment (eg: com.test.*) + * which will import all properties from the namespace. + * + * If not, the targeted namespace will be imported (ie. if com.test is imported, the test object + * will now be global). If the targeted object is not found, it will be included using include(). + * + * @public + * @param String identifier The namespace string + * @param Function callback (OPTIONAL) A function to call when the process is completed (including the include() if used) + * @param Boolean autoInclude (OPTIONAL) Whether to automatically auto include the targeted object is not found. Default is Namespace.autoInclude + */ + _namespace.use = function(identifier) { + var identifiers = _toArray(identifier); + var callback = arguments[1] || false; + var autoInclude = arguments.length > 2 ? arguments[2] : Namespace.autoInclude; + var event = { 'identifier': identifier }; + + for (var i = 0; i < identifiers.length; i++) { + identifier = identifiers[i]; + + var parts = identifier.split(Namespace.separator); + var target = parts.pop(); + var ns = _namespace(parts.join(Namespace.separator)); + + if (target == '*') { + // imports all objects from the identifier, can't use include() in that case + for (var objectName in ns) { + window[objectName] = ns[objectName]; + } + } else { + // imports only one object + if (ns[target]) { + // the object exists, import it + window[target] = ns[target]; + } else { + // the object does not exist + if (autoInclude) { + // try to auto include it + if (callback) { + _namespace.include(identifier, function() { + window[target] = ns[target]; + + if (i + 1 < identifiers.length) { + // we continue to unpack the rest from here + _namespace.unpack(identifiers.slice(i + 1), callback, autoInclude); + } else { + // no more identifiers to unpack + _dispatchEvent('use', event); + callback && callback(); + } + }); + return; + } else { + _namespace.include(identifier); + window[target] = ns[target]; + } + } + } + } + + } + + // all identifiers have been unpacked + _dispatchEvent('use', event); + callback && callback(); + }; + + /** + * Binds the include() and unpack() method to a specified identifier + * + * @public + * @param String identifier The namespace identifier + * @return Object + */ + _namespace.from = function(identifier) { + return { + /** + * Includes the identifier specified in from() + * + * @see Namespace.include() + */ + include: function() { + var callback = arguments[0] || false; + _namespace.include(identifier, callback); + }, + /** + * Includes the identifier specified in from() and unpack + * the specified indentifier in _identifier + * + * @see Namespace.use() + */ + use: function(_identifier) { + var callback = arguments[1] || false; + if (_identifier.charAt(0) == '.') { + _identifier = identifier + _identifier; + } + + if (callback) { + _namespace.include(identifier, function() { + _namespace.use(_identifier, callback, false); + }); + } else { + _namespace.include(identifier); + _namespace.use(_identifier, callback, false); + } + } + }; + }; + + /** + * Registers a namespace so it won't be included + * + * Idea and code submitted by Nathan Smith (http://github.com/smith) + * + * @param String|Array identifier + */ + _namespace.provide = function(identifier) { + var identifiers = _toArray(identifier); + + for (var i = 0; i < identifiers.length; i++) { + if (!(identifier in _includedIdentifiers)) { + _dispatchEvent('provide', { 'identifier': identifier }); + _includedIdentifiers[identifier] = true; + } + } + }; + + /** + * Registers a function to be called when the specified event is dispatched + * + * @param String eventName + * @param Function callback + */ + _namespace.addEventListener = function(eventName, callback) { + if (!_listeners[eventName]) _listeners[eventName] = []; + _listeners[eventName].push(callback); + }; + + /** + * Unregisters an event listener + * + * @param String eventName + * @param Function callback + */ + _namespace.removeEventListener = function(eventName, callback) { + if (!_listeners[eventName]) return; + for (var i = 0; i < _listeners[eventName].length; i++) { + if (_listeners[eventName][i] == callback) { + delete _listeners[eventName][i]; + return; + } + } + }; + + /** + * Adds methods to javascript native's object + * Inspired by http://thinkweb2.com/projects/prototype/namespacing-made-easy/ + * + * @public + */ + _namespace.registerNativeExtensions = function() { + /** + * @see Namespace() + */ + String.prototype.namespace = function() { + var klasses = arguments[0] || {}; + return _namespace(this.valueOf(), klasses); + }; + /** + * @see Namespace.include() + */ + String.prototype.include = function() { + var callback = arguments[0] || false; + return _namespace.include(this.valueOf(), callback); + } + /** + * @see Namespace.use() + */ + String.prototype.use = function() { + var callback = arguments[0] || false; + return _namespace.use(this.valueOf(), callback); + } + /** + * @see Namespace.from() + */ + String.prototype.from = function() { + return _namespace.from(this.valueOf()); + } + /** + * @see Namespace.provide() + * Idea and code submitted by Nathan Smith (http://github.com/smith) + */ + String.prototype.provide = function() { + return _namespace.provide(this.valueOf()); + } + /** + * @see Namespace.use() + */ + Array.prototype.use = function() { + var callback = arguments[0] || false; + return _namespace.use(this, callback); + } + /** + * @see Namespace.provide() + */ + Array.prototype.provide = function() { + return _namespace.provide(this); + } + }; + + return _namespace; +})(); + +/** + * Namespace segment separator + * + * @var String + */ +Namespace.separator = '.'; + +/** + * Base uri when using Namespace.include() + * Must end with a slash + * + * @var String + */ +Namespace.baseUri = './'; + +/** + * Whether to automatically call Namespace.include() when Namespace.import() + * does not find the targeted object. + * + * @var Boolean + */ +Namespace.autoInclude = true; + +// for widget namespaces +var Widget = Namespace('Apotomo.Widget'); + +// for widget instances +var Widgets = {}; + \ No newline at end of file From 2b3f2ef6ff216c542f0f3eb20b103aeed1c7ef48 Mon Sep 17 00:00:00 2001 From: Kristian Mandrup Date: Thu, 6 Sep 2012 15:13:27 +0200 Subject: [PATCH 11/33] better coffee namespace docs --- README.rdoc | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/README.rdoc b/README.rdoc index 37e89a3..3a5028b 100644 --- a/README.rdoc +++ b/README.rdoc @@ -344,6 +344,27 @@ Which will result in the statement: Widgets.firstWidget.flashLight('action': 'search'); ``` +An even better way... + +See [namespace.coffee](https://github.com/CodeCatalyst/namespace.coffee) for a good namespacing/package solution for coffee classes, as described in [oop coffee](http://www.gridlinked.info/oop-with-coffeescript-javascript/) + +```coffeescript +namespace "samples.coffeescript.oop" + PetMaker : + class PetMaker + constructor : -> + + createDog : (name) -> new Dog(name) + createCat : (name) -> new Cat(name) + createBird: (name) -> new Bird(name) + + # private variable declarations (aliases) + # + Dog = samples.coffeescript.oop.pets.Dog + Cat = samples.coffeescript.oop.pets.Cat + Bird= samples.coffeescript.oop.pets.Bird +``` + == Testing Apotomo comes with its own test case and assertions to build rock-solid web components. From ff6d7ca121a16bf7909cc78af2f8a59231e40068 Mon Sep 17 00:00:00 2001 From: Kristian Mandrup Date: Thu, 6 Sep 2012 16:19:21 +0200 Subject: [PATCH 12/33] better widget client side infra --- README.rdoc | 153 +++++++++++------- lib/apotomo/javascript_generator.rb | 19 +-- .../javascript_generator/jquery_helper.rb | 7 + lib/generators/apotomo/widget_generator.rb | 14 +- lib/generators/templates/widget.coffee | 4 - lib/generators/templates/widget.js | 4 +- lib/generators/templates/widget.js.coffee | 8 + .../{ => javascripts}/apotomo/Namespace.js | 4 +- .../javascripts/apotomo/namespaces.coffee | 23 +++ .../assets/javascripts/apotomo/namespaces.js | 34 ++++ .../javascripts/apotomo/namespaces.min.js | 7 + 11 files changed, 204 insertions(+), 73 deletions(-) delete mode 100644 lib/generators/templates/widget.coffee create mode 100644 lib/generators/templates/widget.js.coffee rename vendor/assets/{ => javascripts}/apotomo/Namespace.js (99%) create mode 100644 vendor/assets/javascripts/apotomo/namespaces.coffee create mode 100644 vendor/assets/javascripts/apotomo/namespaces.js create mode 100644 vendor/assets/javascripts/apotomo/namespaces.min.js diff --git a/README.rdoc b/README.rdoc index 3a5028b..b055a15 100644 --- a/README.rdoc +++ b/README.rdoc @@ -191,7 +191,6 @@ Extras (jQuery only): * find_element(id, selector) * selector_for(var, id, selector) -* call_fun(name, id, hash) Example usage: @@ -237,38 +236,56 @@ The first argument `id` is always optional. It is meant to be used to select the These functions should be used sparingly, for _Proof of Concept_ only if possible. It is far better to have javascript asset files if possible (== non-intrusive javascript). -A non-intrusive approach could involve the use of the `call_fun` method which takes the `name` of the javascript method to call, the widget `id` and a hash of data. +A non-intrusive approach could involve the use of: + +* widget_class_call(id, function, hash) +* widget_call(id, function, hash) Example: -`call_fun :toggle_active, 'TopBar', {item: 'item:first'}` +`widget_class_call 'TopBar', :toggle_active, {item: 'item:first'}` -This will result in the call: +Will call a function on the widget class (or module): ```javascript Widget.TopBar.toggleActive({'item': 'item:first'}); ``` -This ensures that all our javascript for a widget is namespace contained nicely. +`widget_call 'Admin::TopBar', :toggle_active, {item: 'item:first'}` + +Will call a function on a particular instance of that widget! + +```javascript +Widgets.admin.topBar.toggleActive({'item': 'item:first'}); +``` + +This ensures that all our javascript for a widget is nicely namespace contained. In your `application.js` manifest file ``` -//= require apotomo/Namespace +//= require apotomo/namespaces ``` +An alternative `apotomo/Namespace.js` is also available, which you might want to experiment with also. + In the `top_bar.coffee` file the following is generated for our convenience ```javascript -Widget.TopBar = Namespace('Apotomo.Widget.TopBar'); +Widget.TopBar = namespace('Widget.TopBar'); -Widget.TopBar = {} +Widget.TopBar = { + // add widget functions here + foo: function(bar) {}, + + bar: function(foor) {} +} ``` We can then implement clean non-intrusive, namespaced javascript functionality as follows: ```javascript -Widget.TopBar = Namespace('Apotomo.Widget.TopBar'); +Widget.TopBar = namespace('Widget.TopBar'); Widget.TopBar = { update: function(item) { @@ -286,83 +303,107 @@ Widget.TopBar = { Note that you can use fx jQuery [extend](http://api.jquery.com/jQuery.extend/) to extend javascript widget functionality similar to modules (prototypical inheritance). There are many other powerful javascript libraries out there (fx Base2, Prototype.js, JS.Class) that can be used to great effect for OOP javascript with inheritance etc. -```coffeescript -$.extend Widget.Admin.TopBar, Widget.TopBar +```javascript +$.extend(Widget.admin.TopBar, Widget.TopBar); ``` -Here we extended the `Admin.TopBar` with base functionality from `TopBar` :) +Here we extended the `admin.TopBar` with base functionality from `TopBar` :) + +Also see the Coffeescript section below to see how to use namespaces. == Using Coffeescript Coffeescript has built in [class structure](http://coffeescript.org/#classes). -and [namespaces](http://spin.atomicobject.com/2011/04/01/namespace-a-coffeescript-nugget/). Also see [namespaced classes](http://stackoverflow.com/questions/8730859/classes-within-coffeescript-namespace) +and [namespaces](http://spin.atomicobject.com/2011/04/01/namespace-a-coffeescript-nugget/). + +Please also see: + +* [namespaced classes](http://stackoverflow.com/questions/8730859/classes-within-coffeescript-namespace) +* [namespace.coffee](https://github.com/CodeCatalyst/namespace.coffee) +* [oop coffee](http://www.gridlinked.info/oop-with-coffeescript-javascript/) + +Here an appetizer :) replace animals with widgets and you get the idea... + +```coffeescript +namespace "samples.coffeescript.oop.abstract" + Animal : + class Animal + constructor : (@species, @isMammal=false) -> + return this + +# NOTE: Animal must be defined BEFORE Dog +# +namespace "samples.coffeescript.oop.pets" + Dog : + class Dog extends samples.coffeescript.oop.abstract.Animal + constructor : (@name) -> +``` + +You should first require `namespaces`: + +``` +//= require apotomo/namespaces.min +``` + +When you generate a widget, the coffescript file for it should look like this: + +`rails g apotomo:widget TopBar show -e haml` ```coffeescript -namespace "Widget.TopBar", (exports) -> - # add functions or classes here - bar: (foo) -> +namespace "Widget" + TopBar : + class TopBar + constructor : (@widget_id = widget_id) -> + + toggleActive: (options) -> ``` ```coffeescript -class MyFirstWidget - constructor: (options = {}) -> - @options = options - @name = options.name - myFunc: () -> - console.log 'works' - -namespace "Widget.TopBar", (exports) -> - exports.MyFirstWidget = MyFirstWidget - - toggleActive: (widget_id, options) -> - item = options.item - $(widget_id).find(item).toggleClass 'active' +namespace "Widget" + TopBar : + class TopBar + constructor : (@widget_id = widget_id) -> + + toggleActive: (options) -> + item = options.item + $(@widget_id).find(item).toggleClass 'active' ``` +`rails g apotomo:widget TopBar::SuperThang show -e haml` + ```coffeescript -firstWidget = new Widget.TopBar.MyFirstWidget -firstWidget.myFunc +namespace "Widget.topBar" + SuperThang : + class SuperThang extends Widget.TopBar + constructor : (@widget_id = widget_id) -> + + createBottomBar : (widget_id) -> new BottomBar(widget_id) + createFooter : (widget_id) -> new Footer(widget_id) + + # private variable declarations (aliases) + # + BottomBar = Widget.BottomBar + Footer = Widget.Footer ``` -=== javascript Widget instance +=== Browser Widget instances For an Ajax enabled page, you can create javascript widget instances in the `Widgets` namespace. ```coffeescript -Widgets.firstWidget = new Widget.TopBar.MyFirstWidget name: "cool widget" +Widgets.thang = new Widget.topBar.SuperThang `` Then you can update an existing widget instance from your Widget (controller) using `call_widget` as follows: ```ruby -call_widget :firstWidget, :flash_light, action: 'search' +call_widget :thang, :toggleActive, item: ".item:first" ``` Which will result in the statement: ```javascript -Widgets.firstWidget.flashLight('action': 'search'); -``` - -An even better way... - -See [namespace.coffee](https://github.com/CodeCatalyst/namespace.coffee) for a good namespacing/package solution for coffee classes, as described in [oop coffee](http://www.gridlinked.info/oop-with-coffeescript-javascript/) - -```coffeescript -namespace "samples.coffeescript.oop" - PetMaker : - class PetMaker - constructor : -> - - createDog : (name) -> new Dog(name) - createCat : (name) -> new Cat(name) - createBird: (name) -> new Bird(name) - - # private variable declarations (aliases) - # - Dog = samples.coffeescript.oop.pets.Dog - Cat = samples.coffeescript.oop.pets.Cat - Bird= samples.coffeescript.oop.pets.Bird +Widgets.thang.toggleActive('action': ".item:first"); ``` == Testing diff --git a/lib/apotomo/javascript_generator.rb b/lib/apotomo/javascript_generator.rb index 3d9a711..45ec7e0 100644 --- a/lib/apotomo/javascript_generator.rb +++ b/lib/apotomo/javascript_generator.rb @@ -69,23 +69,24 @@ def selector_for var, id, selector end # call existing widget - # - call_fun :update, :top_bar, item: 1 + # - widget_class_call :top_bar, :update, item: 1 # --> Widget.TopBar.update('action': 1) - def call_fun name, id, hash - function_name = jq_helper.js_camelize name - namespace = "Widget.#{id.to_s.camelize}" - "#{namespace}.#{function_name}(\"##{id}\", #{hash.to_json});" + def widget_class_call id, function, hash + function_name = jq_helper.js_camelize function + widget_name = jq_helper.ns_name id.to_s.camelize + namespace = "Widget.#{widget_name}" + "#{namespace}.#{function_name}(#{hash.to_json});" end # call existing widget - # - call_widget :top_bar, :flash_light, action: 'search' + # - widget_call :top_bar, :flash_light, action: 'search' # --> Widgets.topBar.flashLight('action': 'search') - def call_widget name, fun, hash - function_name = jq_helper.js_camelize name - "#{name}.#{function_name}(#{hash.to_json});" + def widget_call id, function, hash + function_name = jq_helper.js_camelize function + "Widgets.#{id}.#{function_name}(#{hash.to_json});" end [:replace_all, :prepend_to, :append_to].each do |name| diff --git a/lib/apotomo/javascript_generator/jquery_helper.rb b/lib/apotomo/javascript_generator/jquery_helper.rb index d9c114b..95eba63 100644 --- a/lib/apotomo/javascript_generator/jquery_helper.rb +++ b/lib/apotomo/javascript_generator/jquery_helper.rb @@ -61,6 +61,13 @@ def js_action action ".#{action};" end + def ns_name class_name + names = class_name.split('::') + ns = names[0..-2].map {|name| js_camelize name }.join('.') + return names.last if ns.blank? + ns << ".#{names.last}" + end + def js_camelize str str = str.to_s str.camelize.sub(/^\w/, str[0].downcase) diff --git a/lib/generators/apotomo/widget_generator.rb b/lib/generators/apotomo/widget_generator.rb index 122fae1..6e9a7aa 100644 --- a/lib/generators/apotomo/widget_generator.rb +++ b/lib/generators/apotomo/widget_generator.rb @@ -49,12 +49,24 @@ def create_stylesheet_file end def creates_script_file - return template 'widget.coffee', "#{js_path}_widget.coffee" if !javascript? + return template 'widget.js.coffee', "#{js_path}_widget.js.coffee" if !javascript? template 'widget.js', "#{js_path}_widget.js" end protected + def ns_name + names = class_name.split('::') + ns = names[0..-2].map {|name| js_camelize name }.join('.') + return names.last if ns.blank? + ns << ".#{names.last}" + end + + def js_camelize str + str = str.to_s + str.camelize.sub(/^\w/, str[0].downcase) + end + def javascript? options[:js] end diff --git a/lib/generators/templates/widget.coffee b/lib/generators/templates/widget.coffee deleted file mode 100644 index f3523b5..0000000 --- a/lib/generators/templates/widget.coffee +++ /dev/null @@ -1,4 +0,0 @@ -# Define your coffeescript code for the <%= class_name %> widget -namespace "Widget.TopBar", (exports) -> - # add functions or classes here - bar: (foo) -> \ No newline at end of file diff --git a/lib/generators/templates/widget.js b/lib/generators/templates/widget.js index b0e906e..48965bb 100644 --- a/lib/generators/templates/widget.js +++ b/lib/generators/templates/widget.js @@ -1,6 +1,6 @@ -var Widget.<%= class_name %> = Namespace('Apotomo.Widget.<%= class_name %>'); +var Widget.<%= ns_name %> = namespace('Widget.<%= ns_name %>'); -Widget.<%= class_name %> = { +Widget.<%= ns_name %> = { // add widget functions here foo: function(bar) {}, diff --git a/lib/generators/templates/widget.js.coffee b/lib/generators/templates/widget.js.coffee new file mode 100644 index 0000000..85738c2 --- /dev/null +++ b/lib/generators/templates/widget.js.coffee @@ -0,0 +1,8 @@ +# Define your coffeescript code for the <%= class_name %> widget +namespace "Widget.<%= ns_name %>" + <%= class_name %>: + class <%= class_name %> + constructor : (@widget_id = widget_id) -> + + # add custom widget function here + toggleActive: (options) -> diff --git a/vendor/assets/apotomo/Namespace.js b/vendor/assets/javascripts/apotomo/Namespace.js similarity index 99% rename from vendor/assets/apotomo/Namespace.js rename to vendor/assets/javascripts/apotomo/Namespace.js index db8900f..e5dc4be 100644 --- a/vendor/assets/apotomo/Namespace.js +++ b/vendor/assets/javascripts/apotomo/Namespace.js @@ -5,6 +5,8 @@ Script: Namespace.js Copyright: Copyright (c) 2009 Maxime Bouroumeau-Fuseau +https://github.com/maximebf/Namespace.js + License: MIT-style license. @@ -477,7 +479,7 @@ Namespace.baseUri = './'; Namespace.autoInclude = true; // for widget namespaces -var Widget = Namespace('Apotomo.Widget'); +var widget = Namespace('widget'); // for widget instances var Widgets = {}; diff --git a/vendor/assets/javascripts/apotomo/namespaces.coffee b/vendor/assets/javascripts/apotomo/namespaces.coffee new file mode 100644 index 0000000..5f906b6 --- /dev/null +++ b/vendor/assets/javascripts/apotomo/namespaces.coffee @@ -0,0 +1,23 @@ +# [namespace.coffee](http://github.com/CodeCatalyst/namespace.coffee) v1.0.1 +# Copyright (c) 2011-2012 [CodeCatalyst, LLC](http://www.codecatalyst.com/). +# Open source under the [MIT License](http://en.wikipedia.org/wiki/MIT_License). + +# A lean namespace implementation for JavaScript written in [CoffeeScript](http://coffeescript.com/). + +# *Export the specified value(s) to the specified package name.* +namespace = ( name, values ) -> + # Export to `exports` for node.js or `window` for the browser. + target = exports ? window + # Nested packages may be specified using dot-notation, and are automatically created as needed. + if name.length > 0 + target = target[ subpackage ] ||= {} for subpackage in name.split( '.' ) + # Export each value in the specified values Object to the specified package name by the value's key. + target[ key ] = value for key, value of values + # Return a reference to the specified namespace. + return target + +# *Export the namespace function to global scope, using itself.* +namespace( '', namespace: namespace ) + +namespace "Widget" +namespace "Widgets" \ No newline at end of file diff --git a/vendor/assets/javascripts/apotomo/namespaces.js b/vendor/assets/javascripts/apotomo/namespaces.js new file mode 100644 index 0000000..1ee728d --- /dev/null +++ b/vendor/assets/javascripts/apotomo/namespaces.js @@ -0,0 +1,34 @@ +// Generated by CoffeeScript 1.3.3 +/* + * namespace.coffee v1.0.1 + * Copyright (c) 2011-2012 CodeCatalyst, LLC. + * Open source under the MIT License. + */ +(function() { + var namespace; + + namespace = function(name, values) { + var key, subpackage, target, value, _i, _len, _ref; + target = typeof exports !== "undefined" && exports !== null ? exports : window; + if (name.length > 0) { + _ref = name.split('.'); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + subpackage = _ref[_i]; + target = target[subpackage] || (target[subpackage] = {}); + } + } + for (key in values) { + value = values[key]; + target[key] = value; + } + return target; + }; + + namespace('', { + namespace: namespace + }); + +}).call(this); + +namespace "Widget"; +namespace "Widgets"; \ No newline at end of file diff --git a/vendor/assets/javascripts/apotomo/namespaces.min.js b/vendor/assets/javascripts/apotomo/namespaces.min.js new file mode 100644 index 0000000..ddc8287 --- /dev/null +++ b/vendor/assets/javascripts/apotomo/namespaces.min.js @@ -0,0 +1,7 @@ +/* + * namespace.coffee v1.0.1 + * Copyright (c) 2011-2012 CodeCatalyst, LLC. + * Open source under the MIT License. + */ +(function(){var a;a=function(a,g){var e,c,b,d,h,f;b="undefined"!==typeof exports&&null!==exports?exports:window;if(0 Date: Fri, 7 Sep 2012 12:20:36 +0200 Subject: [PATCH 13/33] adjusted widget target path to fit with rails standards --- lib/apotomo/widget.rb | 7 +++++-- lib/generators/apotomo/widget_generator.rb | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/apotomo/widget.rb b/lib/apotomo/widget.rb index 579220f..086bc50 100644 --- a/lib/apotomo/widget.rb +++ b/lib/apotomo/widget.rb @@ -151,9 +151,12 @@ def render_widget(widget_id, state=:display, *args) widget = widget_id else widget = find_widget(widget_id) or raise "Couldn't render non-existent widget `#{widget_id}`" - end - + end + widget.invoke(state, *args) + rescue NameError => e + return widget.invoke(:show, *args) if state == :display + raise e end end end diff --git a/lib/generators/apotomo/widget_generator.rb b/lib/generators/apotomo/widget_generator.rb index 6e9a7aa..cdf39cf 100644 --- a/lib/generators/apotomo/widget_generator.rb +++ b/lib/generators/apotomo/widget_generator.rb @@ -6,7 +6,7 @@ module BasePathMethods private def base_path - File.join('app/widgets', class_path, file_name) + File.join('app/widgets', class_path) end def js_path From 706a35392b6b08026cf7be343b04233bf000ebf9 Mon Sep 17 00:00:00 2001 From: Kristian Mandrup Date: Fri, 7 Sep 2012 17:57:02 +0200 Subject: [PATCH 14/33] using engine --- lib/apotomo.rb | 2 +- lib/apotomo/cell/rendering.rb | 20 ++++++++++---- lib/apotomo/engine.rb | 26 +++++++++++++++++++ lib/apotomo/railtie.rb | 24 ----------------- lib/apotomo/widget.rb | 7 ++--- .../assets/javascripts/apotomo/namespaces.js | 4 +-- .../javascripts/apotomo/namespaces.min.js | 2 +- 7 files changed, 49 insertions(+), 36 deletions(-) create mode 100644 lib/apotomo/engine.rb delete mode 100644 lib/apotomo/railtie.rb diff --git a/lib/apotomo.rb b/lib/apotomo.rb index 3102211..17e2962 100644 --- a/lib/apotomo.rb +++ b/lib/apotomo.rb @@ -21,7 +21,7 @@ def setup end require 'apotomo/widget' -require 'apotomo/railtie' +require 'apotomo/engine' require 'apotomo/cell/rendering' require 'apotomo/widget_shortcuts' require 'apotomo/rails/controller_methods' diff --git a/lib/apotomo/cell/rendering.rb b/lib/apotomo/cell/rendering.rb index 93162c0..8a429a2 100644 --- a/lib/apotomo/cell/rendering.rb +++ b/lib/apotomo/cell/rendering.rb @@ -1,12 +1,22 @@ module Cell module Rendering def render(*args) - if args.first.kind_of?(Hash) && args.first[:view] - hash = args.first - hash[:view] = File.join('views', hash[:view].to_s) - args = [hash, args[1..-1]] + puts "render: #{args}" + + if args.first.kind_of?(Hash) + if args.first[:view] + hash = args.first + hash[:view] = File.join('views', hash[:view].to_s) + args = [hash, args[1..-1]] + end + if args.first[:partial] + view_name = self.action_name || '' + end end - view_name = File.join('views', self.action_name || '') + + view_name ||= File.join('views', self.action_name || '') + + puts "view_name: #{view_name}, #{args}" render_view_for(view_name, *args) end end diff --git a/lib/apotomo/engine.rb b/lib/apotomo/engine.rb new file mode 100644 index 0000000..afd1665 --- /dev/null +++ b/lib/apotomo/engine.rb @@ -0,0 +1,26 @@ +require "rails/engine" + +module Apotomo + module Rails + class Engine < ::Rails::Engine + rake_tasks do + load "apotomo/apotomo.rake" + end + + # As we are a Railtie only, the routes won't be loaded automatically. Beside that, we want our + # route to be the very first (otherwise #resources might supersede it). + initializer 'apotomo.prepend_routes', :after => :add_routing_paths do |app| + app.routes_reloader.paths.unshift(File.dirname(__FILE__) + "/../../config/routes.rb") + end + + # Include a lazy loader via has_widgets. + initializer 'apotomo.add_has_widgets' do |app| + ActionController::Base.extend Apotomo::Rails::ControllerMethodsLoader + end + + initializer 'apotomo.setup_view_paths', :after => 'cells.setup_view_paths' do |app| + Apotomo::Widget.setup_view_paths! + end + end + end +end \ No newline at end of file diff --git a/lib/apotomo/railtie.rb b/lib/apotomo/railtie.rb deleted file mode 100644 index cc36333..0000000 --- a/lib/apotomo/railtie.rb +++ /dev/null @@ -1,24 +0,0 @@ -require "rails/railtie" - -module Apotomo - class Railtie < ::Rails::Railtie - rake_tasks do - load "apotomo/apotomo.rake" - end - - # As we are a Railtie only, the routes won't be loaded automatically. Beside that, we want our - # route to be the very first (otherwise #resources might supersede it). - initializer 'apotomo.prepend_routes', :after => :add_routing_paths do |app| - app.routes_reloader.paths.unshift(File.dirname(__FILE__) + "/../../config/routes.rb") - end - - # Include a lazy loader via has_widgets. - initializer 'apotomo.add_has_widgets' do |app| - ActionController::Base.extend Apotomo::Rails::ControllerMethodsLoader - end - - initializer 'apotomo.setup_view_paths', :after => 'cells.setup_view_paths' do |app| - Apotomo::Widget.setup_view_paths! - end - end -end diff --git a/lib/apotomo/widget.rb b/lib/apotomo/widget.rb index 086bc50..84f00bc 100644 --- a/lib/apotomo/widget.rb +++ b/lib/apotomo/widget.rb @@ -35,7 +35,7 @@ module Apotomo # # def update(evt) # @cheese = Cheese.find evt[:cheese_id] - class Widget < Cell::Rails + class Widget < Cell::Rails DEFAULT_VIEW_PATHS = [File.join('app', 'widgets')] include Hooks @@ -82,8 +82,8 @@ def initialize(parent, id, options={}) @name = id @visible = true - setup_tree_node(parent) - + setup_tree_node(parent) + run_hook :after_initialize, self end @@ -122,6 +122,7 @@ def invoke(state, *args) # # issues a squeaking alert dialog on the page. def render(*args, &block) + puts "apotomo render: #{args}" super end diff --git a/vendor/assets/javascripts/apotomo/namespaces.js b/vendor/assets/javascripts/apotomo/namespaces.js index 1ee728d..caa755c 100644 --- a/vendor/assets/javascripts/apotomo/namespaces.js +++ b/vendor/assets/javascripts/apotomo/namespaces.js @@ -30,5 +30,5 @@ }).call(this); -namespace "Widget"; -namespace "Widgets"; \ No newline at end of file +namespace("Widget"); +namespace("Widgets"); \ No newline at end of file diff --git a/vendor/assets/javascripts/apotomo/namespaces.min.js b/vendor/assets/javascripts/apotomo/namespaces.min.js index ddc8287..20cdc7a 100644 --- a/vendor/assets/javascripts/apotomo/namespaces.min.js +++ b/vendor/assets/javascripts/apotomo/namespaces.min.js @@ -4,4 +4,4 @@ * Open source under the MIT License. */ (function(){var a;a=function(a,g){var e,c,b,d,h,f;b="undefined"!==typeof exports&&null!==exports?exports:window;if(0 Date: Mon, 10 Sep 2012 10:58:23 +0200 Subject: [PATCH 15/33] much better generator structure and flexibility - all tests pass --- lib/apotomo/cell/rendering.rb | 15 ++++-- lib/apotomo/widget.rb | 1 - lib/generators/apotomo/widget_generator.rb | 42 +++++++++++++--- lib/generators/erb/widget_generator.rb | 1 - lib/generators/haml/widget_generator.rb | 3 +- lib/generators/slim/widget_generator.rb | 1 - lib/generators/templates/widget.js | 1 + test/rails/widget_generator_test.rb | 49 +++++++++++-------- .../mouse/{views => }/educate.html.erb | 0 9 files changed, 76 insertions(+), 37 deletions(-) rename test/widgets/mouse/{views => }/educate.html.erb (100%) diff --git a/lib/apotomo/cell/rendering.rb b/lib/apotomo/cell/rendering.rb index 8a429a2..bcf89f5 100644 --- a/lib/apotomo/cell/rendering.rb +++ b/lib/apotomo/cell/rendering.rb @@ -1,8 +1,6 @@ module Cell module Rendering def render(*args) - puts "render: #{args}" - if args.first.kind_of?(Hash) if args.first[:view] hash = args.first @@ -14,9 +12,16 @@ def render(*args) end end - view_name ||= File.join('views', self.action_name || '') - - puts "view_name: #{view_name}, #{args}" + view_name ||= File.join('views', self.action_name || '') + render_view_for(view_name, *args) + rescue + # try without view + view_name = view_name.gsub(/views\//, '') + if args.first.kind_of?(Hash) && args.first[:view] + hash = args.first + hash[:view] = File.join(hash[:view].to_s) + args = [hash, args[1..-1]] + end render_view_for(view_name, *args) end end diff --git a/lib/apotomo/widget.rb b/lib/apotomo/widget.rb index 84f00bc..93ffc36 100644 --- a/lib/apotomo/widget.rb +++ b/lib/apotomo/widget.rb @@ -122,7 +122,6 @@ def invoke(state, *args) # # issues a squeaking alert dialog on the page. def render(*args, &block) - puts "apotomo render: #{args}" super end diff --git a/lib/generators/apotomo/widget_generator.rb b/lib/generators/apotomo/widget_generator.rb index cdf39cf..c1291c3 100644 --- a/lib/generators/apotomo/widget_generator.rb +++ b/lib/generators/apotomo/widget_generator.rb @@ -9,6 +9,10 @@ def base_path File.join('app/widgets', class_path) end + def view_base_path + File.join('app/widgets', class_path, file_name) + end + def js_path File.join('app/assets/javascripts/widgets', class_path, file_name) end @@ -19,13 +23,33 @@ def css_path end module Views + extend ActiveSupport::Concern + + included do + class_option :parent, :type => :boolean, :default => false, :desc => 'Parent widget' + + source_root File.expand_path('../../templates', __FILE__) + end + def create_views for state in actions do @state = state - @path = File.join(base_path, 'views', "#{state}.html.#{handler}") #base_path defined in Cells::Generators::Base. + # only make views container for top level widgets, since they are most likely to have + # child widgets + @path = if parent_widget? + File.join(view_base_path, 'views', "#{state}.html.#{handler}") #base_path defined in Cells::Generators::Base. + else + File.join(view_base_path, "#{state}.html.#{handler}") #base_path defined in Cells::Generators::Base. + end template "view.#{handler}", @path end end + + protected + + def parent_widget? + options[:parent] + end end class WidgetGenerator < ::Cells::Generators::Base @@ -38,7 +62,7 @@ class WidgetGenerator < ::Cells::Generators::Base check_class_collision :suffix => "Widget" - class_option :js, :type => :boolean, :default => false, :desc => 'Generate javascript asset file' + class_option :js, :type => :boolean, :default => false, :desc => 'Generate javascript asset file' def create_cell_file template 'widget.rb', File.join(base_path, "#{file_name}_widget.rb") @@ -48,9 +72,15 @@ def create_stylesheet_file template 'widget.css', "#{css_path}_widget.css" end - def creates_script_file - return template 'widget.js.coffee', "#{js_path}_widget.js.coffee" if !javascript? - template 'widget.js', "#{js_path}_widget.js" + def create_script_file + puts "create_script_file" + if !javascript? + # raise "coffee: #{js_path}_widget.js.coffee" + template 'widget.js.coffee', "#{js_path}_widget.js.coffee" + else + puts "js: #{js_path}_widget.js" + template 'widget.js', "#{js_path}_widget.js" + end end protected @@ -68,7 +98,7 @@ def js_camelize str end def javascript? - options[:js] + options[:js] == true end end end diff --git a/lib/generators/erb/widget_generator.rb b/lib/generators/erb/widget_generator.rb index a16c28a..3153bf7 100644 --- a/lib/generators/erb/widget_generator.rb +++ b/lib/generators/erb/widget_generator.rb @@ -6,7 +6,6 @@ module Generators class WidgetGenerator < CellGenerator include ::Apotomo::Generators::BasePathMethods include ::Apotomo::Generators::Views - source_root File.expand_path('../../templates', __FILE__) end end end diff --git a/lib/generators/haml/widget_generator.rb b/lib/generators/haml/widget_generator.rb index 7709bdd..3214f72 100644 --- a/lib/generators/haml/widget_generator.rb +++ b/lib/generators/haml/widget_generator.rb @@ -5,8 +5,7 @@ module Haml module Generators class WidgetGenerator < CellGenerator include ::Apotomo::Generators::BasePathMethods - include ::Apotomo::Generators::Views - source_root File.expand_path('../../templates', __FILE__) + include ::Apotomo::Generators::Views end end end diff --git a/lib/generators/slim/widget_generator.rb b/lib/generators/slim/widget_generator.rb index 23966e1..dc11096 100644 --- a/lib/generators/slim/widget_generator.rb +++ b/lib/generators/slim/widget_generator.rb @@ -6,7 +6,6 @@ module Generators class WidgetGenerator < CellGenerator include ::Apotomo::Generators::BasePathMethods include ::Apotomo::Generators::Views - source_root File.expand_path('../../templates', __FILE__) end end end diff --git a/lib/generators/templates/widget.js b/lib/generators/templates/widget.js index 48965bb..5872f4f 100644 --- a/lib/generators/templates/widget.js +++ b/lib/generators/templates/widget.js @@ -1,3 +1,4 @@ +// Define your javascript code for the <%= class_name %> widget var Widget.<%= ns_name %> = namespace('Widget.<%= ns_name %>'); Widget.<%= ns_name %> = { diff --git a/test/rails/widget_generator_test.rb b/test/rails/widget_generator_test.rb index d177f34..58f8f60 100644 --- a/test/rails/widget_generator_test.rb +++ b/test/rails/widget_generator_test.rb @@ -12,45 +12,52 @@ class WidgetGeneratorTest < Rails::Generators::TestCase run_generator %w(Gerbil squeak snuggle -t test_unit) - assert_file "app/widgets/gerbil/gerbil_widget.rb", /class GerbilWidget < Apotomo::Widget/ - assert_file "app/widgets/gerbil/gerbil_widget.rb", /def snuggle/ - assert_file "app/widgets/gerbil/gerbil_widget.rb", /def squeak/ + assert_file "app/widgets/gerbil_widget.rb", /class GerbilWidget < Apotomo::Widget/ + assert_file "app/widgets/gerbil_widget.rb", /def snuggle/ + assert_file "app/widgets/gerbil_widget.rb", /def squeak/ - assert_file "app/widgets/gerbil/views/snuggle.html.erb", %r(app/widgets/gerbil/views/snuggle\.html\.erb) - assert_file "app/widgets/gerbil/views/snuggle.html.erb", %r(

    ) - assert_file "app/widgets/gerbil/views/squeak.html.erb", %r(app/widgets/gerbil/views/squeak\.html\.erb) + assert_file "app/widgets/gerbil/snuggle.html.erb", %r(app/widgets/gerbil/snuggle\.html\.erb) + assert_file "app/widgets/gerbil/snuggle.html.erb", %r(

    ) + assert_file "app/widgets/gerbil/squeak.html.erb", %r(app/widgets/gerbil/squeak\.html\.erb) assert_file "test/widgets/gerbil/gerbil_widget_test.rb", %r(class GerbilWidgetTest < Apotomo::TestCase) assert_file "test/widgets/gerbil/gerbil_widget_test.rb", %r(widget\(:gerbil\)) end - should "create javascript and css assets" do + should "create coffescript and css assets" do run_generator %w(Gerbil squeak snuggle -t test_unit) - assert_file "app/assets/javascripts/widgets/gerbil_widget.coffee", /Define your coffeescript code for the Gerbil widget*/ + assert_file "app/assets/javascripts/widgets/gerbil_widget.js.coffee", /Define your coffeescript code for the Gerbil widget*/ + assert_file "app/assets/stylesheets/widgets/gerbil_widget.css", /Define your css code for the Gerbil widget*/ + end + + should "create javascript and css assets" do + run_generator %w(Gerbil squeak snuggle -t test_unit --js) + + assert_file "app/assets/javascripts/widgets/gerbil_widget.js", /Define your javascript code for the Gerbil widget*/ assert_file "app/assets/stylesheets/widgets/gerbil_widget.css", /Define your css code for the Gerbil widget*/ end should "create haml assets with -e haml" do run_generator %w(Gerbil squeak snuggle -e haml -t test_unit) - assert_file "app/widgets/gerbil/gerbil_widget.rb", /class GerbilWidget < Apotomo::Widget/ - assert_file "app/widgets/gerbil/gerbil_widget.rb", /def snuggle/ - assert_file "app/widgets/gerbil/gerbil_widget.rb", /def squeak/ + assert_file "app/widgets/gerbil_widget.rb", /class GerbilWidget < Apotomo::Widget/ + assert_file "app/widgets/gerbil_widget.rb", /def snuggle/ + assert_file "app/widgets/gerbil_widget.rb", /def squeak/ - assert_file "app/widgets/gerbil/views/snuggle.html.haml", %r(app/widgets/gerbil/views/snuggle\.html\.haml) - assert_file "app/widgets/gerbil/views/snuggle.html.haml", %r(%p) - assert_file "app/widgets/gerbil/views/squeak.html.haml", %r(app/widgets/gerbil/views/squeak\.html\.haml) + assert_file "app/widgets/gerbil/snuggle.html.haml", %r(app/widgets/gerbil/snuggle\.html\.haml) + assert_file "app/widgets/gerbil/snuggle.html.haml", %r(%p) + assert_file "app/widgets/gerbil/squeak.html.haml", %r(app/widgets/gerbil/squeak\.html\.haml) assert_file "test/widgets/gerbil/gerbil_widget_test.rb" end should "create slim assets with -e slim" do - run_generator %w(Gerbil squeak snuggle -e slim -t test_unit) + run_generator %w(Gerbil squeak snuggle -e slim -t test_unit --parent) - assert_file "app/widgets/gerbil/gerbil_widget.rb", /class GerbilWidget < Apotomo::Widget/ - assert_file "app/widgets/gerbil/gerbil_widget.rb", /def snuggle/ - assert_file "app/widgets/gerbil/gerbil_widget.rb", /def squeak/ + assert_file "app/widgets/gerbil_widget.rb", /class GerbilWidget < Apotomo::Widget/ + assert_file "app/widgets/gerbil_widget.rb", /def snuggle/ + assert_file "app/widgets/gerbil_widget.rb", /def squeak/ assert_file "app/widgets/gerbil/views/snuggle.html.slim", %r(app/widgets/gerbil/views/snuggle\.html\.slim) assert_file "app/widgets/gerbil/views/snuggle.html.slim", %r(p) @@ -62,10 +69,10 @@ class WidgetGeneratorTest < Rails::Generators::TestCase should "work with namespaces" do run_generator %w(Gerbil::Mouse squeak -t test_unit) - assert_file "app/widgets/gerbil/mouse/mouse_widget.rb", /class Gerbil::MouseWidget < Apotomo::Widget/ - assert_file "app/widgets/gerbil/mouse/mouse_widget.rb", /def squeak/ + assert_file "app/widgets/gerbil/mouse_widget.rb", /class Gerbil::MouseWidget < Apotomo::Widget/ + assert_file "app/widgets/gerbil/mouse_widget.rb", /def squeak/ - assert_file "app/widgets/gerbil/mouse/views/squeak.html.erb", %r(app/widgets/gerbil/mouse/views/squeak\.html\.erb) + assert_file "app/widgets/gerbil/mouse/squeak.html.erb", %r(app/widgets/gerbil/mouse/squeak\.html\.erb) assert_file "test/widgets/gerbil/mouse/mouse_widget_test.rb" end diff --git a/test/widgets/mouse/views/educate.html.erb b/test/widgets/mouse/educate.html.erb similarity index 100% rename from test/widgets/mouse/views/educate.html.erb rename to test/widgets/mouse/educate.html.erb From 1607f5787fdac80579c3d9de074702b75eccebbf Mon Sep 17 00:00:00 2001 From: Kristian Mandrup Date: Mon, 10 Sep 2012 11:25:03 +0200 Subject: [PATCH 16/33] allow stylesheet lang option on generator --- lib/generators/apotomo/widget_generator.rb | 36 ++++++++++++++++--- .../{widget.js.coffee => widget.coffee} | 0 lib/generators/templates/widget.css | 2 +- lib/generators/templates/widget.sass | 1 + lib/generators/templates/widget.scss | 1 + test/rails/widget_generator_test.rb | 14 +++++--- 6 files changed, 44 insertions(+), 10 deletions(-) rename lib/generators/templates/{widget.js.coffee => widget.coffee} (100%) create mode 100644 lib/generators/templates/widget.sass create mode 100644 lib/generators/templates/widget.scss diff --git a/lib/generators/apotomo/widget_generator.rb b/lib/generators/apotomo/widget_generator.rb index c1291c3..7616949 100644 --- a/lib/generators/apotomo/widget_generator.rb +++ b/lib/generators/apotomo/widget_generator.rb @@ -62,21 +62,27 @@ class WidgetGenerator < ::Cells::Generators::Base check_class_collision :suffix => "Widget" - class_option :js, :type => :boolean, :default => false, :desc => 'Generate javascript asset file' + class_option :js, :type => :boolean, :default => false, :desc => 'Generate javascript asset file' + class_option :style, :type => :string, :default => 'scss', :desc => 'Style language to use: css, scss or sass' def create_cell_file template 'widget.rb', File.join(base_path, "#{file_name}_widget.rb") end def create_stylesheet_file - template 'widget.css', "#{css_path}_widget.css" + if scss? + template 'widget.scss', "#{css_path}_widget.css.scss" + elsif sass? + template 'widget.sass', "#{css_path}_widget.css.sass" + else + template 'widget.css', "#{css_path}_widget.css" + end end def create_script_file puts "create_script_file" - if !javascript? - # raise "coffee: #{js_path}_widget.js.coffee" - template 'widget.js.coffee', "#{js_path}_widget.js.coffee" + if coffee? + template 'widget.coffee', "#{js_path}_widget.js.coffee" else puts "js: #{js_path}_widget.js" template 'widget.js', "#{js_path}_widget.js" @@ -97,6 +103,26 @@ def js_camelize str str.camelize.sub(/^\w/, str[0].downcase) end + def style + options[:style].to_s.downcase + end + + def sass? + style == 'sass' + end + + def scss? + style == 'scss' + end + + def css? + style == 'css' + end + + def coffee? + !javascript? + end + def javascript? options[:js] == true end diff --git a/lib/generators/templates/widget.js.coffee b/lib/generators/templates/widget.coffee similarity index 100% rename from lib/generators/templates/widget.js.coffee rename to lib/generators/templates/widget.coffee diff --git a/lib/generators/templates/widget.css b/lib/generators/templates/widget.css index 90ce1a6..5d4be42 100644 --- a/lib/generators/templates/widget.css +++ b/lib/generators/templates/widget.css @@ -1 +1 @@ -/* Define your css code for the <%= class_name %> widget */ \ No newline at end of file +/* Define your CSS code for the <%= class_name %> widget */ \ No newline at end of file diff --git a/lib/generators/templates/widget.sass b/lib/generators/templates/widget.sass new file mode 100644 index 0000000..0e2fef7 --- /dev/null +++ b/lib/generators/templates/widget.sass @@ -0,0 +1 @@ +/* Define your SASS code for the <%= class_name %> widget */ \ No newline at end of file diff --git a/lib/generators/templates/widget.scss b/lib/generators/templates/widget.scss new file mode 100644 index 0000000..3512e49 --- /dev/null +++ b/lib/generators/templates/widget.scss @@ -0,0 +1 @@ +/* Define your SCSS code for the <%= class_name %> widget */ \ No newline at end of file diff --git a/test/rails/widget_generator_test.rb b/test/rails/widget_generator_test.rb index 58f8f60..fa69c55 100644 --- a/test/rails/widget_generator_test.rb +++ b/test/rails/widget_generator_test.rb @@ -24,18 +24,24 @@ class WidgetGeneratorTest < Rails::Generators::TestCase assert_file "test/widgets/gerbil/gerbil_widget_test.rb", %r(widget\(:gerbil\)) end - should "create coffescript and css assets" do + should "create coffescript and scss assets" do run_generator %w(Gerbil squeak snuggle -t test_unit) assert_file "app/assets/javascripts/widgets/gerbil_widget.js.coffee", /Define your coffeescript code for the Gerbil widget*/ - assert_file "app/assets/stylesheets/widgets/gerbil_widget.css", /Define your css code for the Gerbil widget*/ + assert_file "app/assets/stylesheets/widgets/gerbil_widget.css.scss", /Define your SCSS code for the Gerbil widget*/ end should "create javascript and css assets" do - run_generator %w(Gerbil squeak snuggle -t test_unit --js) + run_generator %w(Gerbil squeak snuggle -t test_unit --js --style css) assert_file "app/assets/javascripts/widgets/gerbil_widget.js", /Define your javascript code for the Gerbil widget*/ - assert_file "app/assets/stylesheets/widgets/gerbil_widget.css", /Define your css code for the Gerbil widget*/ + assert_file "app/assets/stylesheets/widgets/gerbil_widget.css", /Define your CSS code for the Gerbil widget*/ + end + + should "create javascript and sass assets" do + run_generator %w(Gerbil squeak snuggle -t test_unit --js --style sass) + + assert_file "app/assets/stylesheets/widgets/gerbil_widget.css.sass", /Define your SASS code for the Gerbil widget*/ end should "create haml assets with -e haml" do From 9856a543da8d05a7799a9e16ae86cf8b36794e42 Mon Sep 17 00:00:00 2001 From: Kristian Mandrup Date: Mon, 10 Sep 2012 12:09:30 +0200 Subject: [PATCH 17/33] fix js naming --- lib/generators/apotomo/widget_generator.rb | 5 ++++- lib/generators/templates/widget.coffee | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/generators/apotomo/widget_generator.rb b/lib/generators/apotomo/widget_generator.rb index 7616949..8b86bbe 100644 --- a/lib/generators/apotomo/widget_generator.rb +++ b/lib/generators/apotomo/widget_generator.rb @@ -80,7 +80,6 @@ def create_stylesheet_file end def create_script_file - puts "create_script_file" if coffee? template 'widget.coffee', "#{js_path}_widget.js.coffee" else @@ -98,6 +97,10 @@ def ns_name ns << ".#{names.last}" end + def simple_name + class_name.to_s.demodulize + end + def js_camelize str str = str.to_s str.camelize.sub(/^\w/, str[0].downcase) diff --git a/lib/generators/templates/widget.coffee b/lib/generators/templates/widget.coffee index 85738c2..004fcb5 100644 --- a/lib/generators/templates/widget.coffee +++ b/lib/generators/templates/widget.coffee @@ -1,7 +1,7 @@ # Define your coffeescript code for the <%= class_name %> widget namespace "Widget.<%= ns_name %>" - <%= class_name %>: - class <%= class_name %> + <%= simple_name %>: + class <%= simple_name %> constructor : (@widget_id = widget_id) -> # add custom widget function here From a6d026edf8bdef0d882a0ab696e33dec3a08ab95 Mon Sep 17 00:00:00 2001 From: Kristian Mandrup Date: Thu, 22 Nov 2012 09:25:05 +0100 Subject: [PATCH 18/33] fix rendering? --- lib/apotomo/cell/rendering.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/apotomo/cell/rendering.rb b/lib/apotomo/cell/rendering.rb index bcf89f5..6487204 100644 --- a/lib/apotomo/cell/rendering.rb +++ b/lib/apotomo/cell/rendering.rb @@ -13,10 +13,14 @@ def render(*args) end view_name ||= File.join('views', self.action_name || '') + # puts "view_name: #{view_name}" + render_view_for(view_name, *args) rescue # try without view view_name = view_name.gsub(/views\//, '') + # puts "TRY view_name: #{view_name}" + if args.first.kind_of?(Hash) && args.first[:view] hash = args.first hash[:view] = File.join(hash[:view].to_s) From 8b1a46e1fd5705e347ca5a9f76e192685e3838ff Mon Sep 17 00:00:00 2001 From: Kristian Mandrup Date: Thu, 22 Nov 2012 09:56:07 +0100 Subject: [PATCH 19/33] add engine setup generator --- lib/apotomo.rb | 15 +++++++++++++++ lib/apotomo/widget.rb | 6 ++++++ lib/apotomo/widget/javascript_methods.rb | 10 ++++++++++ lib/apotomo/widget_render_buffer.rb | 18 ++++++++++++++++++ .../apotomo/engine_setup_generator.rb | 11 +++++++++++ lib/generators/apotomo/templates/apotomo.erb | 5 +++++ 6 files changed, 65 insertions(+) create mode 100644 lib/apotomo/widget_render_buffer.rb create mode 100644 lib/generators/apotomo/engine_setup_generator.rb create mode 100644 lib/generators/apotomo/templates/apotomo.erb diff --git a/lib/apotomo.rb b/lib/apotomo.rb index 17e2962..1bb0c80 100644 --- a/lib/apotomo.rb +++ b/lib/apotomo.rb @@ -29,3 +29,18 @@ def setup require 'apotomo/test_case' if Rails.env == "test" Apotomo.js_framework = :jquery ### DISCUSS: move to rails.rb + +# https://github.com/apotonick/apotomo/issues/72 + +# USAGE +# render_buffer do |b| +# b.replace "##{widget_id}", :view => :display if invitation +# b.replace "section#invite", :text => "" +# end + +# where to better put this? +def render_buffer + buffer = WidgetRenderBuffer.new self + yield buffer + buffer.to_s +end \ No newline at end of file diff --git a/lib/apotomo/widget.rb b/lib/apotomo/widget.rb index 93ffc36..384eb52 100644 --- a/lib/apotomo/widget.rb +++ b/lib/apotomo/widget.rb @@ -153,6 +153,12 @@ def render_widget(widget_id, state=:display, *args) widget = find_widget(widget_id) or raise "Couldn't render non-existent widget `#{widget_id}`" end + # support for Cells builders feature + # target_class = self.class.build_class_for(parent_controller, widget.class, args.first) + # if widget.class != target_class + # widget = target_class.new(parent_controller, widget_id, args.first) + # end + widget.invoke(state, *args) rescue NameError => e return widget.invoke(:show, *args) if state == :display diff --git a/lib/apotomo/widget/javascript_methods.rb b/lib/apotomo/widget/javascript_methods.rb index 45976b0..53502fc 100644 --- a/lib/apotomo/widget/javascript_methods.rb +++ b/lib/apotomo/widget/javascript_methods.rb @@ -34,6 +34,16 @@ def replace(*args) def update(*args) wrap_in_javascript_for(:update, *args) end + + # Instruct the browser to perform a redirect to the specified url. + # + # Example: + # + # redirect_to course_path(@course.id) + # #=> "window.location.replace(\"davinci.dev/courses/4f592ee4b5a482327b000008\");" + def redirect_to(url) + render :text => "window.location.replace(\"#{url}\");" + end private def wrap_in_javascript_for(mode, *args) diff --git a/lib/apotomo/widget_render_buffer.rb b/lib/apotomo/widget_render_buffer.rb new file mode 100644 index 0000000..ee9b385 --- /dev/null +++ b/lib/apotomo/widget_render_buffer.rb @@ -0,0 +1,18 @@ +class WidgetRenderBuffer + def initialize w + @widget = w + @buffer = "" + end + + def replace *args + @buffer << @widget.replace(*args) + end + + def render *args + @buffer << @widget.render(@args) + end + + def to_s + @buffer + end +end diff --git a/lib/generators/apotomo/engine_setup_generator.rb b/lib/generators/apotomo/engine_setup_generator.rb new file mode 100644 index 0000000..1aa6f05 --- /dev/null +++ b/lib/generators/apotomo/engine_setup_generator.rb @@ -0,0 +1,11 @@ +module Apotomo + module Generators + class WidgetGenerator < ::Rails::Generators::NamedBase + source_root File.expand_path("../templates", __FILE__) + + def create_initializer + template "apotomo.erb", "config/initializers/apotomo.rb" + end + end + end +end diff --git a/lib/generators/apotomo/templates/apotomo.erb b/lib/generators/apotomo/templates/apotomo.erb new file mode 100644 index 0000000..d0b2f61 --- /dev/null +++ b/lib/generators/apotomo/templates/apotomo.erb @@ -0,0 +1,5 @@ +# Adds this engines' widget view path root to the hosting Rails app widget paths list + +Rails.application.config.after_initialize do + Apotomo::Widget.append_view_path <%= name.to_s.camelize %>::Engine.root + 'app/widgets' +end \ No newline at end of file From 99d55e84c3b90a09b3c8873d9e8fa3f811d8964b Mon Sep 17 00:00:00 2001 From: Kristian Mandrup Date: Thu, 22 Nov 2012 10:13:32 +0100 Subject: [PATCH 20/33] more README instructions --- README.rdoc | 30 +++++++++++++++++++++++++++++ lib/apotomo.rb | 18 ++--------------- lib/apotomo/widget.rb | 12 ++++++++++++ lib/apotomo/widget_render_buffer.rb | 30 +++++++++++++++-------------- 4 files changed, 60 insertions(+), 30 deletions(-) diff --git a/README.rdoc b/README.rdoc index b055a15..054eb62 100644 --- a/README.rdoc +++ b/README.rdoc @@ -73,6 +73,27 @@ And a Form widget within the TopBar namespace create app/assets/javascripts/widgets/top_bar/form_widget.coffee create app/assets/stylesheets/widgets/top_bar/form_widget.css +Note that the widget generator supports the following templating engines: + +* erb +* haml +* slim + +=== Engines + +Apotomo can also be used with engines. In your engine, simply run the apotomo engine_setup generator. + + $ rails g apotomo:engine_setup my_cool + create config/initializers/apotomo.rb + +This will add the following initialization code + + Rails.application.config.after_initialize do + Apotomo::Widget.append_view_path MyCool::Engine.root + 'app/widgets' + end + +This instructs the Rails app hosting the engine to append this engine's widgets path to the global list of apotomo widget paths. + == Plug it in You now tell your controller about the new widget. @@ -406,6 +427,15 @@ Which will result in the statement: Widgets.thang.toggleActive('action': ".item:first"); ``` +== Render buffer + +A render buffer can be used for the views like this: + + render_buffer do |b| + b.replace "##{widget_id}", :view => :display if invitation + b.replace "section#invite", :text => "" + end + == Testing Apotomo comes with its own test case and assertions to build rock-solid web components. diff --git a/lib/apotomo.rb b/lib/apotomo.rb index 1bb0c80..d6025f1 100644 --- a/lib/apotomo.rb +++ b/lib/apotomo.rb @@ -21,6 +21,7 @@ def setup end require 'apotomo/widget' +require 'apotomo/widget_render_buffer' require 'apotomo/engine' require 'apotomo/cell/rendering' require 'apotomo/widget_shortcuts' @@ -28,19 +29,4 @@ def setup require 'apotomo/javascript_generator' require 'apotomo/test_case' if Rails.env == "test" -Apotomo.js_framework = :jquery ### DISCUSS: move to rails.rb - -# https://github.com/apotonick/apotomo/issues/72 - -# USAGE -# render_buffer do |b| -# b.replace "##{widget_id}", :view => :display if invitation -# b.replace "section#invite", :text => "" -# end - -# where to better put this? -def render_buffer - buffer = WidgetRenderBuffer.new self - yield buffer - buffer.to_s -end \ No newline at end of file +Apotomo.js_framework = :jquery ### DISCUSS: move to rails.rb \ No newline at end of file diff --git a/lib/apotomo/widget.rb b/lib/apotomo/widget.rb index 384eb52..a6e75e6 100644 --- a/lib/apotomo/widget.rb +++ b/lib/apotomo/widget.rb @@ -86,6 +86,18 @@ def initialize(parent, id, options={}) run_hook :after_initialize, self end + + # See # https://github.com/apotonick/apotomo/issues/72 + + # render_buffer do |b| + # b.replace "##{widget_id}", :view => :display if invitation + # b.replace "section#invite", :text => "" + # end + def render_buffer + buffer = Apotomo::WidgetRenderBuffer.new self + yield buffer + buffer.to_s + end def parent_controller # i hope we'll get rid of any parent_controller dependency, soon. diff --git a/lib/apotomo/widget_render_buffer.rb b/lib/apotomo/widget_render_buffer.rb index ee9b385..415c3b3 100644 --- a/lib/apotomo/widget_render_buffer.rb +++ b/lib/apotomo/widget_render_buffer.rb @@ -1,18 +1,20 @@ -class WidgetRenderBuffer - def initialize w - @widget = w - @buffer = "" - end +module Apotomo + class WidgetRenderBuffer + def initialize w + @widget = w + @buffer = "" + end - def replace *args - @buffer << @widget.replace(*args) - end + def replace *args + @buffer << @widget.replace(*args) + end - def render *args - @buffer << @widget.render(@args) - end + def render *args + @buffer << @widget.render(@args) + end - def to_s - @buffer + def to_s + @buffer + end end -end +end \ No newline at end of file From c82e335838bcad47edc2e635799dbfb064d751dd Mon Sep 17 00:00:00 2001 From: Kristian Mandrup Date: Thu, 22 Nov 2012 10:25:51 +0100 Subject: [PATCH 21/33] added skeleton to test for widget with action named load - which currently fails --- test/test_case_methods.rb | 2 ++ test/widget_test.rb | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/test/test_case_methods.rb b/test/test_case_methods.rb index 5dc6504..3dd6ea2 100644 --- a/test/test_case_methods.rb +++ b/test/test_case_methods.rb @@ -33,6 +33,7 @@ def mum_and_kid! @mum.respond_to_event :squeak, :with => :answer_squeak @mum.respond_to_event :squeak, :from => 'kid', :with => :alert @mum.respond_to_event :footsteps, :with => :escape + @mum.respond_to_event :load, :with => :load @kid.respond_to_event :footsteps, :with => :peek @@ -43,6 +44,7 @@ def list; @list ||= []; end def answer_squeak; self.list << 'answer squeak'; render :text => "squeak", :render_children => false; end def alert; self.list << 'be alerted'; render :text => "alert!", :render_children => false; end def escape; self.list << 'escape'; render :text => "escape", :render_children => false; end + def load; self.list << 'load'; render :text => "load", :render_children => false; end end @kid.instance_eval do diff --git a/test/widget_test.rb b/test/widget_test.rb index d139b63..68032a5 100644 --- a/test/widget_test.rb +++ b/test/widget_test.rb @@ -133,6 +133,12 @@ class WidgetTest < ActiveSupport::TestCase assert MouseWidget.action_methods.collect{ |m| m.to_s }.include?("squeak") assert Class.new(MouseWidget).action_methods.collect{ |m| m.to_s }.include?("squeak") end + + should "list 'load' in local and inherited states in Widget.action_methods" do + pending 'TODO - currently fails' + # assert MouseWidget.action_methods.collect{ |m| m.to_s }.include?("load") + # assert Class.new(MouseWidget).action_methods.collect{ |m| m.to_s }.include?("load") + end should "not list #display in internal_methods although it's defined in Object" do assert_not Apotomo::Widget.internal_methods.include?(:display) From 78a2bc3ce964d3802d86403bcba48b935c78fa57 Mon Sep 17 00:00:00 2001 From: Kristian Mandrup Date: Thu, 22 Nov 2012 10:44:05 +0100 Subject: [PATCH 22/33] support jQuery noConflict mode --- lib/apotomo/javascript_generator.rb | 4 +-- lib/apotomo/rails/view_helper/dragn_drop.rb | 2 +- lib/apotomo/test_case.rb | 2 +- lib/apotomo/widget/javascript_methods.rb | 6 ++-- .../jquery_helper_test.rb | 14 ++++---- test/javascript_generator_test.rb | 32 +++++++++---------- test/rails/controller_methods_test.rb | 2 +- test/render_test.rb | 8 ++--- 8 files changed, 35 insertions(+), 35 deletions(-) diff --git a/lib/apotomo/javascript_generator.rb b/lib/apotomo/javascript_generator.rb index 45ec7e0..5409050 100644 --- a/lib/apotomo/javascript_generator.rb +++ b/lib/apotomo/javascript_generator.rb @@ -27,7 +27,7 @@ def escape(javascript) module Prototype def prototype; end - def element(selector); "$(\"#{selector}\")"; end + def element(selector); "jQuery(\"#{selector}\")"; end def update(selector, markup); element(selector) + '.update("'+escape(markup)+'");'; end def replace(selector, markup); element(selector) + '.replace("'+escape(markup)+'");'; end @@ -37,7 +37,7 @@ def replace(selector, markup); element(selector) + '.replace("'+escape(markup module Right def right; end - def element(selector); "$(\"#{selector}\")"; end + def element(selector); "jQuery(\"#{selector}\")"; end def update(selector, markup); element(selector) + '.update("'+escape(markup)+'");'; end def replace(selector, markup); element(selector) + '.replace("'+escape(markup)+'");'; end diff --git a/lib/apotomo/rails/view_helper/dragn_drop.rb b/lib/apotomo/rails/view_helper/dragn_drop.rb index 4d214ef..7971c44 100644 --- a/lib/apotomo/rails/view_helper/dragn_drop.rb +++ b/lib/apotomo/rails/view_helper/dragn_drop.rb @@ -13,7 +13,7 @@ def droppable selector, &block # fx # draggable "##{widget_id} li", {revert: "invalid"} def draggable selector, options - %Q{$(\"#{selector}\").draggable(#{options.to_json});} + %Q{jQuery(\"#{selector}\").draggable(#{options.to_json});} end end end diff --git a/lib/apotomo/test_case.rb b/lib/apotomo/test_case.rb index aa2b8b1..8f17d40 100644 --- a/lib/apotomo/test_case.rb +++ b/lib/apotomo/test_case.rb @@ -19,7 +19,7 @@ module Apotomo # # it "should redraw on :update" do # trigger :update - # assert_response "$(\"post-comments\").update ..." + # assert_response "jQuery(\"post-comments\").update ..." # end # # For unit testing, you can grab an instance of your tested widget. diff --git a/lib/apotomo/widget/javascript_methods.rb b/lib/apotomo/widget/javascript_methods.rb index 53502fc..59dc81f 100644 --- a/lib/apotomo/widget/javascript_methods.rb +++ b/lib/apotomo/widget/javascript_methods.rb @@ -15,12 +15,12 @@ def escape_js(script) # # will render the current state's view and wrap it like # - # "$(\"#mouse\").replaceWith(\"

    hungry!<\\/div>\")" + # "jQuery(\"#mouse\").replaceWith(\"
    hungry!<\\/div>\")" # # You may pass a selector and pass options to render here, as well. # # replace "#jerry h1", :view => :squeak - # #=> "$(\"#jerry h1\").replaceWith(\"
    squeak!<\\/div>\")" + # #=> "jQuery(\"#jerry h1\").replaceWith(\"
    squeak!<\\/div>\")" def replace(*args) wrap_in_javascript_for(:replace, *args) end @@ -30,7 +30,7 @@ def replace(*args) # Example for +:jquery+: # # update :view => :peek - # #=> "$(\"#mouse\").html(\"looking...")" + # #=> "jQuery(\"#mouse\").html(\"looking...")" def update(*args) wrap_in_javascript_for(:update, *args) end diff --git a/test/javascript_generator/jquery_helper_test.rb b/test/javascript_generator/jquery_helper_test.rb index 2c27e32..66f755f 100644 --- a/test/javascript_generator/jquery_helper_test.rb +++ b/test/javascript_generator/jquery_helper_test.rb @@ -9,13 +9,13 @@ class JQueryHelperTest < Test::Unit::TestCase context '#find_element' do context 'args: id, selector' do should "generate full action" do - assert_equal "$(\"#my_widget\").find(\".item\")", @helper.find_element('my_widget', '.item') + assert_equal "jQuery(\"#my_widget\").find(\".item\")", @helper.find_element('my_widget', '.item') end end context 'args: selector' do should "generate selector only" do - assert_equal "$(\".item\")", @helper.find_element(nil, '.item') + assert_equal "jQuery(\".item\")", @helper.find_element(nil, '.item') end end end @@ -23,13 +23,13 @@ class JQueryHelperTest < Test::Unit::TestCase context '#jq_action' do context 'args: id, selector, action' do should "generate full action" do - assert_equal "$(\"#my_widget\").find(\".item\").empty();", @helper.jq_action('my_widget', '.item', 'empty()') + assert_equal "jQuery(\"#my_widget\").find(\".item\").empty();", @helper.jq_action('my_widget', '.item', 'empty()') end end context 'args: selector, action' do should "generate selector only" do - assert_equal "$(\".item\").empty();", @helper.jq_action('.item', 'empty()') + assert_equal "jQuery(\".item\").empty();", @helper.jq_action('.item', 'empty()') end end end @@ -37,7 +37,7 @@ class JQueryHelperTest < Test::Unit::TestCase context '#inv_markup_action' do context 'args: selector, markup, action' do should "generate full action" do - assert_equal "$(\"hello > world<\\/b>\").replaceAll(\"#my_widget\");", @helper.inv_markup_action('#my_widget', 'hello > world', :replace_all) + assert_equal "jQuery(\"hello > world<\\/b>\").replaceAll(\"#my_widget\");", @helper.inv_markup_action('#my_widget', 'hello > world', :replace_all) end end end @@ -45,13 +45,13 @@ class JQueryHelperTest < Test::Unit::TestCase context '#markup_action' do context 'args: id, selector, markup, action' do should "generate full action" do - assert_equal "$(\"#my_widget\").find(\".item\").append(\"hello > world<\\/b>\");", @helper.markup_action('my_widget', '.item', 'hello > world', :append) + assert_equal "jQuery(\"#my_widget\").find(\".item\").append(\"hello > world<\\/b>\");", @helper.markup_action('my_widget', '.item', 'hello > world', :append) end end context 'args: selector, markup, action' do should "generate selector action" do - assert_equal "$(\".item\").append(\"hello > world<\\/b>\");", @helper.markup_action('.item', 'hello > world', :append) + assert_equal "jQuery(\".item\").append(\"hello > world<\\/b>\");", @helper.markup_action('.item', 'hello > world', :append) end end end diff --git a/test/javascript_generator_test.rb b/test/javascript_generator_test.rb index 78a5a01..ffb9777 100644 --- a/test/javascript_generator_test.rb +++ b/test/javascript_generator_test.rb @@ -18,19 +18,19 @@ class JavascriptGeneratorTest < Test::Unit::TestCase end should "respond to replace" do - assert_equal "$(\"drinks\").replace(\"EMPTY!\");", @gen.replace(:drinks, 'EMPTY!') + assert_equal "jQuery(\"drinks\").replace(\"EMPTY!\");", @gen.replace(:drinks, 'EMPTY!') end should "respond to replace_id" do - assert_equal "$(\"drinks\").replace(\"EMPTY!\");", @gen.replace_id("drinks", 'EMPTY!') + assert_equal "jQuery(\"drinks\").replace(\"EMPTY!\");", @gen.replace_id("drinks", 'EMPTY!') end should "respond to update" do - assert_equal "$(\"drinks\").update(\"
  • <\\/li>\");", @gen.update(:drinks, '
  • ') + assert_equal "jQuery(\"drinks\").update(\"
  • <\\/li>\");", @gen.update(:drinks, '
  • ') end should "respond to update_id" do - assert_equal "$(\"drinks\").update(\"EMPTY!\");", @gen.update_id("drinks", 'EMPTY!') + assert_equal "jQuery(\"drinks\").update(\"EMPTY!\");", @gen.update_id("drinks", 'EMPTY!') end end @@ -44,19 +44,19 @@ class JavascriptGeneratorTest < Test::Unit::TestCase end should "respond to replace" do - assert_equal "$(\"drinks\").replace(\"EMPTY!\");", @gen.replace(:drinks, 'EMPTY!') + assert_equal "jQuery(\"drinks\").replace(\"EMPTY!\");", @gen.replace(:drinks, 'EMPTY!') end should "respond to replace_id" do - assert_equal "$(\"drinks\").replace(\"EMPTY!\");", @gen.replace_id("drinks", 'EMPTY!') + assert_equal "jQuery(\"drinks\").replace(\"EMPTY!\");", @gen.replace_id("drinks", 'EMPTY!') end should "respond to update" do - assert_equal "$(\"drinks\").update(\"
  • <\\/li>\");", @gen.update(:drinks, '
  • ') + assert_equal "jQuery(\"drinks\").update(\"
  • <\\/li>\");", @gen.update(:drinks, '
  • ') end should "respond to update_id" do - assert_equal "$(\"drinks\").update(\"EMPTY!\");", @gen.update_id("drinks", 'EMPTY!') + assert_equal "jQuery(\"drinks\").update(\"EMPTY!\");", @gen.update_id("drinks", 'EMPTY!') end end @@ -66,7 +66,7 @@ class JavascriptGeneratorTest < Test::Unit::TestCase end should 'create _apo_xyz javascript variable for jQuery element' do - assert_equal "var _apo_xyz = $(\"#my_id\").find(\".item\");", @gen.selector_for(:xyz, 'my_id', '.item') + assert_equal "var _apo_xyz = jQuery(\"#my_id\").find(\".item\");", @gen.selector_for(:xyz, 'my_id', '.item') end should "respond to jquery" do @@ -74,7 +74,7 @@ class JavascriptGeneratorTest < Test::Unit::TestCase end should "respond to replace" do - assert_equal "$(\"#drinks\").replaceWith(\"EMPTY!\");", @gen.replace("#drinks", 'EMPTY!') + assert_equal "jQuery(\"#drinks\").replaceWith(\"EMPTY!\");", @gen.replace("#drinks", 'EMPTY!') end should "escape" do @@ -82,29 +82,29 @@ class JavascriptGeneratorTest < Test::Unit::TestCase end should "respond to replace and escape" do - assert_equal "$(\"#drinks\").replaceWith(\"
  • <\\/li>\");", @gen.replace("#drinks", '
  • ') + assert_equal "jQuery(\"#drinks\").replaceWith(\"
  • <\\/li>\");", @gen.replace("#drinks", '
  • ') end should "respond to replace with block" do txt = @gen.replace("#drinks") { '
  • '} - assert_equal "$(\"#drinks\").replaceWith(\"
  • <\\/li>\");", txt + assert_equal "jQuery(\"#drinks\").replaceWith(\"
  • <\\/li>\");", txt end should "respond to replace_id" do - assert_equal "$(\"#drinks\").replaceWith(\"EMPTY!\");", @gen.replace_id("drinks", 'EMPTY!') + assert_equal "jQuery(\"#drinks\").replaceWith(\"EMPTY!\");", @gen.replace_id("drinks", 'EMPTY!') end should "respond to update and escape" do - assert_equal "$(\"#drinks\").html(\"
  • <\\/li>\");", @gen.update("#drinks", '
  • ') + assert_equal "jQuery(\"#drinks\").html(\"
  • <\\/li>\");", @gen.update("#drinks", '
  • ') end should "respond to update with block" do txt = @gen.update("#drinks") { '
  • '} - assert_equal "$(\"#drinks\").html(\"
  • <\\/li>\");", txt + assert_equal "jQuery(\"#drinks\").html(\"
  • <\\/li>\");", txt end should "respond to update_id" do - assert_equal "$(\"#drinks\").html(\"EMPTY!\");", @gen.update_id("drinks", 'EMPTY!') + assert_equal "jQuery(\"#drinks\").html(\"EMPTY!\");", @gen.update_id("drinks", 'EMPTY!') end end end diff --git a/test/rails/controller_methods_test.rb b/test/rails/controller_methods_test.rb index 2aa4289..4f382c1 100644 --- a/test/rails/controller_methods_test.rb +++ b/test/rails/controller_methods_test.rb @@ -117,7 +117,7 @@ def squeak; render :text => 'squeak!', :update => :true; end get :render_event_response, :source => :kid, :type => :doorSlam assert_equal Mime::JS, @response.content_type - assert_equal "$(\"mum\").replace(\"
    burp!<\\/div>\")\n$(\"kid\").update(\"squeak!\")\nsqueak();", @response.body + assert_equal "jQuery(\"mum\").replace(\"
    burp!<\\/div>\")\njQuery(\"kid\").update(\"squeak!\")\nsqueak();", @response.body end end end diff --git a/test/render_test.rb b/test/render_test.rb index 7bcc720..3dba339 100644 --- a/test/render_test.rb +++ b/test/render_test.rb @@ -92,7 +92,7 @@ def squeak update :text => "squeak!" end end - assert_equal "$(\"#mum\").html(\"squeak!\");", @mum.invoke(:squeak) + assert_equal "jQuery(\"#mum\").html(\"squeak!\");", @mum.invoke(:squeak) end should "accept a selector" do @@ -101,7 +101,7 @@ def squeak update "div#mouse", :text => '
    squeak!
    ' end end - assert_equal "$(\"div#mouse\").html(\"
    squeak!<\\/div>\");", @mum.invoke(:squeak) + assert_equal "jQuery(\"div#mouse\").html(\"
    squeak!<\\/div>\");", @mum.invoke(:squeak) end end @@ -112,7 +112,7 @@ def squeak replace :text => '
    squeak!
    ' end end - assert_equal "$(\"#mum\").replaceWith(\"
    squeak!<\\/div>\");", @mum.invoke(:squeak) + assert_equal "jQuery(\"#mum\").replaceWith(\"
    squeak!<\\/div>\");", @mum.invoke(:squeak) end should "accept a selector" do @@ -121,7 +121,7 @@ def squeak replace "div#mouse", :text => '
    squeak!
    ' end end - assert_equal "$(\"div#mouse\").replaceWith(\"
    squeak!<\\/div>\");", @mum.invoke(:squeak) + assert_equal "jQuery(\"div#mouse\").replaceWith(\"
    squeak!<\\/div>\");", @mum.invoke(:squeak) end end From 7b141be7ed52a556c86bd0ed7d1ab64eb1ef7c01 Mon Sep 17 00:00:00 2001 From: Kristian Mandrup Date: Thu, 22 Nov 2012 10:48:16 +0100 Subject: [PATCH 23/33] jquery noConflic mode - tests passes --- README.rdoc | 6 +++--- lib/apotomo/javascript_generator.rb | 2 +- lib/apotomo/javascript_generator/jquery_helper.rb | 4 ++-- lib/apotomo/rails/view_helper/ajax.rb | 2 +- lib/apotomo/rails/view_helper/dragn_drop.rb | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/README.rdoc b/README.rdoc index 054eb62..4778206 100644 --- a/README.rdoc +++ b/README.rdoc @@ -188,7 +188,7 @@ Also, updating the page is in your hands. Where Apotomo provides handy helpers a Look, +replace+ basically generates - $("comments").replaceWith(); + jQuery("comments").replaceWith(); If that's not what you want, do @@ -316,7 +316,7 @@ Widget.TopBar = { toggleActive: function(widget_id, options) { item = options['item']; // do some toggle magic!!! - $(widget_id).find(item).toggleClass('active'); + jQuery(widget_id).find(item).toggleClass('active'); } } ``` @@ -387,7 +387,7 @@ namespace "Widget" toggleActive: (options) -> item = options.item - $(@widget_id).find(item).toggleClass 'active' + jQuery(@widget_id).find(item).toggleClass 'active' ``` `rails g apotomo:widget TopBar::SuperThang show -e haml` diff --git a/lib/apotomo/javascript_generator.rb b/lib/apotomo/javascript_generator.rb index 5409050..03896f5 100644 --- a/lib/apotomo/javascript_generator.rb +++ b/lib/apotomo/javascript_generator.rb @@ -49,7 +49,7 @@ module Jquery def jquery; end def element(selector) selector = jq_helper.calc_selector selector - "$(#{selector})" + "jQuery(#{selector})" end def update(*args, &block) diff --git a/lib/apotomo/javascript_generator/jquery_helper.rb b/lib/apotomo/javascript_generator/jquery_helper.rb index 95eba63..3733bb1 100644 --- a/lib/apotomo/javascript_generator/jquery_helper.rb +++ b/lib/apotomo/javascript_generator/jquery_helper.rb @@ -19,7 +19,7 @@ def jq_action *args, action def inv_markup_action(selector, markup, action) selector = calc_selector selector action = js_camelize(action) - "$(#{escaped(markup)}).#{action}(#{selector});" + "jQuery(#{escaped(markup)}).#{action}(#{selector});" end def markup_act name, *args, &block @@ -89,7 +89,7 @@ def apo_match? selector def element(selector) selector = calc_selector selector - "$(#{selector})" + "jQuery(#{selector})" end # id, selector, markup, action diff --git a/lib/apotomo/rails/view_helper/ajax.rb b/lib/apotomo/rails/view_helper/ajax.rb index a1357c0..4fa61e3 100644 --- a/lib/apotomo/rails/view_helper/ajax.rb +++ b/lib/apotomo/rails/view_helper/ajax.rb @@ -3,7 +3,7 @@ module Ajax # fx # ajax_url "#trashbin", "&id=" + ui.draggable.attr("data-id") def ajax_url selector, params - %Q{$.ajax({url: $("#{selector}").attr("data-event-url") + #{params};})} + %Q{jQuery.ajax({url: jQuery("#{selector}").attr("data-event-url") + #{params};})} end end end diff --git a/lib/apotomo/rails/view_helper/dragn_drop.rb b/lib/apotomo/rails/view_helper/dragn_drop.rb index 7971c44..38d647d 100644 --- a/lib/apotomo/rails/view_helper/dragn_drop.rb +++ b/lib/apotomo/rails/view_helper/dragn_drop.rb @@ -3,7 +3,7 @@ module DragnDrop # fx # droppable "#trashbin", ajax_url("#trashbin", "&id=" + ui.draggable.attr("data-id")) def droppable selector, &block - %Q{$("#{selector}").droppable({ + %Q{jQuery("#{selector}").droppable({ drop: function(event, ui) { #{yield} } From 1ee5fe956cc7a376382ca4067ed302c35690f3f9 Mon Sep 17 00:00:00 2001 From: Kristian Mandrup Date: Thu, 22 Nov 2012 11:02:44 +0100 Subject: [PATCH 24/33] global partials --- README.rdoc | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.rdoc b/README.rdoc index 4778206..aae456f 100644 --- a/README.rdoc +++ b/README.rdoc @@ -436,6 +436,13 @@ A render buffer can be used for the views like this: b.replace "section#invite", :text => "" end +== Rendering global partials from within a widget + +Sometimes you need to render a global partial from app/views within a cell. For instance, the `gmaps4rails` helper depends on a global partial. While this breaks encapsulation it’s still possible in cells - just add the global view path to the widget. To add the view path to a group of widgets, simply add it to their shared base widget. + + class MyWidget < Apotomo::Widget + append_view_path "app/views" + == Testing Apotomo comes with its own test case and assertions to build rock-solid web components. From 5e8abdc0981c9e808da62ee7c58fe74d5eae0fda Mon Sep 17 00:00:00 2001 From: Kristian Mandrup Date: Thu, 22 Nov 2012 12:13:20 +0100 Subject: [PATCH 25/33] add essential cells documentation to apotomo README --- README.rdoc | 33 +++++++++++++++++++++++++++++++++ lib/apotomo/widget.rb | 14 +++++++------- 2 files changed, 40 insertions(+), 7 deletions(-) diff --git a/README.rdoc b/README.rdoc index aae456f..1df732a 100644 --- a/README.rdoc +++ b/README.rdoc @@ -18,6 +18,8 @@ Apotomo is based on {Cells}[http://github.com/apotonick/cells], the popular View It gives you widgets and encapsulation, bubbling events, AJAX page updates, rock-solid testing and more. Check out http://apotomo.de for a bunch of tutorials and a nice web 2.0 logo. +To get the most out of Apotomo, you should carefully read through the {Cells}[http://github.com/apotonick/cells] README documentation and {wiki}[https://github.com/apotonick/cells/wiki] + == Installation Easy as hell. @@ -94,6 +96,37 @@ This will add the following initialization code This instructs the Rails app hosting the engine to append this engine's widgets path to the global list of apotomo widget paths. +== Devise integration + + class MySessionWidget < Apotomo::Widget + include Devise::Controllers::Helpers + helper_method :current_user #all your needed helper + +== Kaminari integration + +In your Kaminari initializer file, add this: + + Kaminari::Helpers::Tag.class_eval do + def to_s(locals = {}) #:nodoc: + @template.render :partial => "../views/kaminari/#{@theme}#{self.class.name.demodulize.underscore}", :locals => @options.merge(locals) + end + end + +After that, Kaminari calls from Cell will render app/cells/../views/kaminari/* files, so you don’t need to add the entire app/views folder to Cells view paths + +== Builders + + class LoginCell < Cell::Rails + build do + UnauthorizedUserCell unless logged_in? + end + +A call to + + render_cell(:login, :box) + +will render the configured UnauthorizedUserCell instead of the original LoginCell if the login test fails. + == Plug it in You now tell your controller about the new widget. diff --git a/lib/apotomo/widget.rb b/lib/apotomo/widget.rb index a6e75e6..5be5785 100644 --- a/lib/apotomo/widget.rb +++ b/lib/apotomo/widget.rb @@ -156,21 +156,21 @@ def url_for_event(type, options={}) def self.controller_path @controller_path ||= name.sub(/Widget$/, '').underscore unless anonymous? end + + def cell(name, *args, &block) + Cell::Rails.create_cell_for(name, parent_controller, *args).tap do |cell| + cell.instance_eval &block if block_given? + end + end # Renders the +widget+ (instance or id). - def render_widget(widget_id, state=:display, *args) + def render_widget(widget_id, state=:display, *args, &block) if widget_id.kind_of?(Widget) widget = widget_id else widget = find_widget(widget_id) or raise "Couldn't render non-existent widget `#{widget_id}`" end - # support for Cells builders feature - # target_class = self.class.build_class_for(parent_controller, widget.class, args.first) - # if widget.class != target_class - # widget = target_class.new(parent_controller, widget_id, args.first) - # end - widget.invoke(state, *args) rescue NameError => e return widget.invoke(:show, *args) if state == :display From 41f56edaa37b7a547b6920c76378f181fb0f368a Mon Sep 17 00:00:00 2001 From: Kristian Mandrup Date: Thu, 22 Nov 2012 12:26:46 +0100 Subject: [PATCH 26/33] added kaminari setup generator --- README.rdoc | 5 ++++ .../apotomo/engine_setup_generator.rb | 2 +- .../apotomo/kaminari_setup_generator.rb | 27 +++++++++++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 lib/generators/apotomo/kaminari_setup_generator.rb diff --git a/README.rdoc b/README.rdoc index 1df732a..632ba40 100644 --- a/README.rdoc +++ b/README.rdoc @@ -114,6 +114,11 @@ In your Kaminari initializer file, add this: After that, Kaminari calls from Cell will render app/cells/../views/kaminari/* files, so you don’t need to add the entire app/views folder to Cells view paths +To automate this, a generator is can be used: + + $ rails g apotomo:kaminary_setup + append config/initializers/kaminari_config.rb + == Builders class LoginCell < Cell::Rails diff --git a/lib/generators/apotomo/engine_setup_generator.rb b/lib/generators/apotomo/engine_setup_generator.rb index 1aa6f05..79719ac 100644 --- a/lib/generators/apotomo/engine_setup_generator.rb +++ b/lib/generators/apotomo/engine_setup_generator.rb @@ -1,6 +1,6 @@ module Apotomo module Generators - class WidgetGenerator < ::Rails::Generators::NamedBase + class EngineSetupGenerator < ::Rails::Generators::NamedBase source_root File.expand_path("../templates", __FILE__) def create_initializer diff --git a/lib/generators/apotomo/kaminari_setup_generator.rb b/lib/generators/apotomo/kaminari_setup_generator.rb new file mode 100644 index 0000000..6e98125 --- /dev/null +++ b/lib/generators/apotomo/kaminari_setup_generator.rb @@ -0,0 +1,27 @@ +module Apotomo + module Generators + class KaminariSetupGenerator < ::Rails::Generators::Base + # source_root File.expand_path("../templates", __FILE__) + + def create_initializer + append_to_file kaminari_config_file do + kaminari_config_code + end + end + + protected + + def kaminari_config_file + "config/initializers/kaminari_config.rb" + end + + def kaminari_config_code + %q{ +Kaminari::Helpers::Tag.class_eval do + def to_s(locals = {}) #:nodoc: + @template.render :partial => "../views/kaminari/#{@theme}#{self.class.name.demodulize.underscore}", :locals => @options.merge(locals) + end +end +} + end +end \ No newline at end of file From b70e4d6666e780c8e0cf0b68224300ab89ecce50 Mon Sep 17 00:00:00 2001 From: Kristian Mandrup Date: Thu, 22 Nov 2012 12:41:04 +0100 Subject: [PATCH 27/33] notes on url helpers --- README.rdoc | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/README.rdoc b/README.rdoc index 632ba40..e5a4008 100644 --- a/README.rdoc +++ b/README.rdoc @@ -132,6 +132,19 @@ A call to will render the configured UnauthorizedUserCell instead of the original LoginCell if the login test fails. +Note: Not sure Cell Builders are supported in Apotomo widgets? + +== URL helpers + +Using the *_url helpers implies accessing the request instance, which kinda breaks encapsulation. Cells doesn’t support breaking things, that’s why you have to include it manually. + +Include the UrlFor module into your widget. + + class MyLinkingWidget < Apotomo::Widget + include ActionController::UrlFor + +Inside a cell there should normally be no need to generate urls this way, since the cell should only render the views that relate directly to its actions. The url helpers would instead be used in the templates. Try to avoid "complex" redirection logic in your cells. + == Plug it in You now tell your controller about the new widget. From c300cc89cdf1daf95dad4f5f70ee8f133500aa9f Mon Sep 17 00:00:00 2001 From: Kristian Mandrup Date: Thu, 22 Nov 2012 13:32:35 +0100 Subject: [PATCH 28/33] using rspec with apotomo --- README.rdoc | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/README.rdoc b/README.rdoc index e5a4008..3991ef9 100644 --- a/README.rdoc +++ b/README.rdoc @@ -514,6 +514,37 @@ Apotomo comes with its own test case and assertions to build rock-solid web c You can render your widgets, spec the markup, trigger events and assert the event responses, so far. If you need more, let us know! +Alternatively, to use Rspec use {rspec-apotomo}[https://github.com/apotonick/rspec-apotomo] + + group :test do + gem 'rspec-apotomo' + end + +Put specs in the spec/widgets directory + + describe CommentsWidget do + has_widgets do |root| + root << widget(:comments) + end + + it 'renders properly' do + render_widget(:comments).should == "

    No Comment!

    " + end + + it 'responds to :post events' do + trigger(:post, :comments, :text => "I like you!").should == ["Thanks!"] + end + end + + +Run your examples with: + + rake spec:widgets + +Or... + + bundle exec rspec spec/widgets + == More features There's even more, too much for a simple README. From 847df8510dba2eabc05bc1fe674f5ac8a893eb76 Mon Sep 17 00:00:00 2001 From: Kristian Mandrup Date: Thu, 22 Nov 2012 13:41:38 +0100 Subject: [PATCH 29/33] uncommented travis for now --- .travis.yml => the.travis.yml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .travis.yml => the.travis.yml (100%) diff --git a/.travis.yml b/the.travis.yml similarity index 100% rename from .travis.yml rename to the.travis.yml From 518b8247ece5d2924e6c701a26d926b101aeee13 Mon Sep 17 00:00:00 2001 From: Kristian Mandrup Date: Thu, 22 Nov 2012 14:20:52 +0100 Subject: [PATCH 30/33] fixed kaminari setup generator --- lib/generators/apotomo/kaminari_setup_generator.rb | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/generators/apotomo/kaminari_setup_generator.rb b/lib/generators/apotomo/kaminari_setup_generator.rb index 6e98125..bdad666 100644 --- a/lib/generators/apotomo/kaminari_setup_generator.rb +++ b/lib/generators/apotomo/kaminari_setup_generator.rb @@ -1,7 +1,7 @@ module Apotomo module Generators class KaminariSetupGenerator < ::Rails::Generators::Base - # source_root File.expand_path("../templates", __FILE__) + source_root File.expand_path("../templates", __FILE__) def create_initializer append_to_file kaminari_config_file do @@ -15,13 +15,14 @@ def kaminari_config_file "config/initializers/kaminari_config.rb" end - def kaminari_config_code - %q{ + def kaminari_config_code + %q{ Kaminari::Helpers::Tag.class_eval do def to_s(locals = {}) #:nodoc: @template.render :partial => "../views/kaminari/#{@theme}#{self.class.name.demodulize.underscore}", :locals => @options.merge(locals) end -end -} +end} + end + end end end \ No newline at end of file From 02981ae29b99b38dbc7009deeb16ad5713493b3d Mon Sep 17 00:00:00 2001 From: Kristian Mandrup Date: Thu, 22 Nov 2012 14:33:49 +0100 Subject: [PATCH 31/33] notes on use with rspec --- README.rdoc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.rdoc b/README.rdoc index 3991ef9..2969e64 100644 --- a/README.rdoc +++ b/README.rdoc @@ -520,7 +520,9 @@ Alternatively, to use Rspec use {rspec-apotomo}[https://github.com/apotonick/rsp gem 'rspec-apotomo' end -Put specs in the spec/widgets directory +Now when you run your widget generator, it should generate spec stubs `*_specs.rb` in the `spec/widgets` folder instead. + +Specs should be in the `spec/widgets` directory describe CommentsWidget do has_widgets do |root| @@ -536,7 +538,6 @@ Put specs in the spec/widgets directory end end - Run your examples with: rake spec:widgets From 3b5e23b7d330c5afa80e59fd1bd1be0500279dcb Mon Sep 17 00:00:00 2001 From: Kristian Mandrup Date: Thu, 22 Nov 2012 14:58:26 +0100 Subject: [PATCH 32/33] how to configure generators --- README.rdoc | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/README.rdoc b/README.rdoc index 2969e64..e47d5f4 100644 --- a/README.rdoc +++ b/README.rdoc @@ -96,6 +96,31 @@ This will add the following initialization code This instructs the Rails app hosting the engine to append this engine's widgets path to the global list of apotomo widget paths. +== Generator configuration + +If you use apotomo within a Rails app, go to the `config/application.rb` file and configure as you see fit. +See {configuring-generators}[http://guides.rubyonrails.org/configuring.html#configuring-generators] for details. + + module FriendlyRent + class Application < Rails::Application + config.generators do |g| + g.view_specs false + g.helper_specs false + g.test_framework :rspec, fixture: true + g.fixture_replacement :fabrication + end + + +If you use apotomo within a Rails engine, go to the main engine files, fx `lib/mycool/engine.rb`and add a `config.generators` block. + + class MyCool + class Engine < ::Rails::Engine + + config.generators do |g| + g.template_engine :haml + g.test_framework :rspec + end + == Devise integration class MySessionWidget < Apotomo::Widget @@ -520,7 +545,9 @@ Alternatively, to use Rspec use {rspec-apotomo}[https://github.com/apotonick/rsp gem 'rspec-apotomo' end -Now when you run your widget generator, it should generate spec stubs `*_specs.rb` in the `spec/widgets` folder instead. +See the *Generator configuration* for how to configure generator defaults. + +When you run your widget generator, it will generate spec stubs `*_specs.rb` in the `spec/widgets` folder. Specs should be in the `spec/widgets` directory From 54313121d393e8cae7319be84cb62f33837fb7d6 Mon Sep 17 00:00:00 2001 From: Kristian Mandrup Date: Thu, 22 Nov 2012 15:03:14 +0100 Subject: [PATCH 33/33] minor twist in readme --- README.rdoc | 1 + 1 file changed, 1 insertion(+) diff --git a/README.rdoc b/README.rdoc index e47d5f4..3633b3b 100644 --- a/README.rdoc +++ b/README.rdoc @@ -108,6 +108,7 @@ See {configuring-generators}[http://guides.rubyonrails.org/configuring.html#conf g.helper_specs false g.test_framework :rspec, fixture: true g.fixture_replacement :fabrication + # g.fixture_replacement :factory_girl, :dir => "spec/factories" end