Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(WIP, need for a feedback!) jQuery helpers #108

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 82 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,21 @@ Look, `replace` basically generates
jQuery("comments").replaceWith(<the rendered view>);
```

All these available helpers are:

* `update(selector, render_args)`
* `replace(selector, render_args)`
* `update_text(selector, render_args)`
* `append(selector, render_args)`
* `prepend(selector, render_args)`
* `after(selector, render_args)`
* `before(selector, render_args)`
* `wrap(selector, render_args)`
* `wrap_inner(selector, render_args)`
* `wrap_all(selector, render_args)`

Note: The first argument `selector` is always optional or can be `nil`.

If that's not what you want, do

```ruby
Expand All @@ -180,6 +195,73 @@ end

Apotomo doesn't depend on _any_ JS framework - you choose!

Note: Let's explain `widget_call`!

## JavaScript Generator Helpers

You can use `js_generator` object to access cool JavaScript helpers in a state.

Available helpers (of `js_generator` object) to get elements and call JavaScript code on them:

* `find_element_by_selector(selector)`
* `find_element_by_id(id)`
* `element(id)`
* `find_element_selector(id, selector)`
* `find_element_selector(nil, selector)`
* `find_element_selector(id, nil)`
* `element_action(id, selector, action)`
* `element_action(selector, action)`
* `element_action(nil, selector, action)`
* `element_call(id, selector, method_name, method_args)`
* `element_call(selector, method_name, method_args)`
* `element_call(nil, selector, method_name, method_args)`
* `element_call(method_name, method_args)`
* `element_call(nil, nil, selector, method_name, method_args)

Helpers to call elements' methods:

* `update(id, selector, markup)`
* `replace(id, selector, markup)`
* `update_text(id, selector, markup)`
* `append(id, selector, markup)`
* `prepend(id, selector, markup)`
* `after(id, selector, markup)`
* `before(id, selector, markup)`
* `wrap(id, selector, markup)`
* `wrap_inner(id, selector, markup)`
* `wrap_all(id, selector, markup)`
* `unwrap(id, selector)`
* `remove(id, selector)`
* `remove_class(id, selector, *classes)`
* `remove_classes(id, selector, *classes)`
* `add_class(id, selector, *classes)`
* `add_classes(id, selector, *classes)`
* `toggle_class(id, selector, *classes)`
* `toggle_classes(id, selector, *classes)`
* `attr(id, selector, name)`
* `prop(id, selector, name)`
* `val(id, selector)`
* `html(id, selector)`
* `empty(id, selector)`

Note: The first argument `id` is always optional or can be `nil`.

Other JavaScript stuff:

* `escape(javascript)`
* `selector_by_id(id)`
* `camelize(str)`
* `underscore(str)`
* `represent(arg)`
* `represent_as_string(arg)`
* `represent_as_number(arg)`
* `represent_as_literal(arg)`
* `represent_as_array(arg)`
* `represent_as_hash(arg)`
* `represent_as_arguments_list(*args)`
* `action(action)`
* `call(method_name, *args)

## Testing

Apotomo comes with its own test case and assertions to <b>build rock-solid web components</b>.
Expand Down
14 changes: 5 additions & 9 deletions lib/apotomo/javascript_generator.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
require 'action_view/helpers/javascript_helper'
require 'apotomo/javascript_generator/javascript_helper'

module Apotomo
class JavascriptGenerator
include JavascriptHelper

autoload :Jquery, 'apotomo/javascript_generator/jquery'

def initialize(framework)
raise "No JS framework specified" if framework.blank?
extend "apotomo/javascript_generator/#{framework}".camelize.constantize
Expand Down Expand Up @@ -39,14 +44,5 @@ 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
end

module Jquery
def jquery; end
def element(id); "jQuery(\"#{id}\")"; 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
end
end
end
71 changes: 71 additions & 0 deletions lib/apotomo/javascript_generator/javascript_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
module Apotomo
module JavascriptHelper
include ::ActionView::Helpers::JavaScriptHelper

def selector_by_id(id)
"##{id}"
end

def camelize(str)
str.to_s.camelize.sub(/^\w/, str.to_s[0].downcase)
end

def underscore(str)
str.to_s.underscore
end

def represent_as_string(arg)
%Q{"#{escape_javascript(arg)}"}
end

def represent_as_number(arg)
%Q{#{arg}}
end

def represent_as_literal(arg)
%Q{#{camelize(arg)}}
end

def represent_as_array(arg)
raise NotImplementedError
end

def represent_as_hash(arg)
raise NotImplementedError
end

def represent_as_function(arg)
raise NotImplementedError
end

def represent(arg)
case arg
when String; represent_as_string(arg)
when Numeric; represent_as_number(arg)
when Symbol; represent_as_literal(arg)
when Array; represent_as_array(arg)
when Hash; represent_as_hash(arg)
else raise NotImplementedError
end
end

def represent_as_arguments_list(*args)
%Q{#{args.join(", ")}}
end

def action(action)
%Q{.#{action};}
end

def call(method_name, *args)
method_name = camelize(method_name) # DISCUSS: underscore ?
args = args.collect { |arg| represent(arg) }

action(%Q{#{method_name}(#{represent_as_arguments_list(*args)})})
end

def extract_args(required_count, *args)
Array.new(required_count-args.count) + Array.wrap(args)
end
end
end
109 changes: 109 additions & 0 deletions lib/apotomo/javascript_generator/jquery.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
module Apotomo
class JavascriptGenerator
module Jquery

def jquery
end

# Wraps the args in a statement according to your +Apotomo.js_framework+ setting.
#
# Example for #replace (with <tt>Apotomo.js_framework = :jquery</tt>):
#
# replace "squeak"
# #=> "jQuery(\"#mouse\").replaceWith(\"squeak\")"
#
# You may pass a selector and pass options to render here, as well.
#
# replace "#jerry h1", "squeak"
# #=> "jQuery(\"#jerry h1\").replaceWith(\"squeak\")"

# - id, selector, method_args
# - selector, method_args
# - nil, selector, method_args

def update(*args, &block)
id, selector, markup = *extract_args(3, *args)
element_call(id, selector, :html, Array.wrap(markup), &block)
end
alias_method :html, :update

def replace(*args, &block)
id, selector, markup = *extract_args(3, *args)
element_call(id, selector, :replace_with, Array.wrap(markup), &block)
end
alias_method :replace_with, :replace

[:update_text, :append, :prepend, :after, :before, :wrap, :wrap_inner, :wrap_all].each do |method_name|
define_method method_name do |*args|
id, selector, markup = args.shift, *extract_args(2, args)
element_call(id, selector, method_name, Array.wrap(markup), &block)
end
end

[:unwrap, :remove, :attr, :prop, :val, :empty].each do |method_name|
define_method method_name do |*args|
id, selector, method_args = *extract_args(3, *args)
element_call(id, selector, method_name, Array.wrap(method_args), &block)
end
end

[:add_class, :remove_class, :toggle_class].each do |method_name|
define_method method_name do |*args|
id, selector, classes = *extract_args(3, *args).flatten.join(' ')
element_call(id, selector, method_name, Array.wrap(classes_str), &block)
end
alias_method "#{method_name}es", "#{method_name}"
end
end


def find_element_by_selector(selector)
%Q{#{jquery_namespace}("#{selector}")}
end

def find_element_by_id(id)
find_element_by_selector(selector_by_id(id))
end

alias_method :element, :find_element_by_id

# - selector, action
# - nil, action
# - selector, nil
def find_element_selector(id, selector)
if id
if selector
find_element_by_id(id) + action(%Q{.find("#{selector}")})
else
find_element_by_id(id)
end
else
find_element_by_selector(selector)
end
end

# - id, selector, action
# - selector, action
# - nil, selector, action
def element_action(*args, action)
id, selector = *extract_args(2, args)
find_element_selector(id, selector) + action(action)
end

# - id, selector, method_name, method_args
# - selector, method_name, method_args
# - nil, selector, method_name, method_args
# - method_name, method_args
# - nil, nil, method_name, method_args
def element_call(*args)
id, selector, method_name, method_args = *extract_args(4, *args)
find_element_selector(id, selector) + call(method_name, *method_args)
end

private

def jquery_namespace
"jQuery"
end
end
end
66 changes: 27 additions & 39 deletions lib/apotomo/widget/javascript_methods.rb
Original file line number Diff line number Diff line change
@@ -1,48 +1,36 @@
module Apotomo
module JavascriptMethods
# Returns the JavascriptGenerator object.
def js_generator
Apotomo.js_generator
end

# Returns the escaped script.
def escape_js(script)
Apotomo.js_generator.escape(script)
end

# Wraps the rendered content in a replace statement according to your +Apotomo.js_framework+ setting.
# Received the same options as #render plus an optional +selector+ to change the selector.
#
# Example (with <tt>Apotomo.js_framework = :jquery</tt>):
#
# def hungry
# replace
#
# will render the current state's view and wrap it like
#
# "jQuery(\"#mouse\").replaceWith(\"<div id=\\\"mouse\\\">hungry!<\\/div>\")"
#
# You may pass a selector and pass options to render here, as well.
#
# replace "#jerry h1", :view => :squeak
# #=> "jQuery(\"#jerry h1\").replaceWith(\"<div id=\\\"mouse\\\">squeak!<\\/div>\")"
def replace(*args)
wrap_in_javascript_for(:replace, *args)
js_generator.escape(script)
end
# Same as #replace except that the content is wrapped in an update statement.
#
# Example for +:jquery+:
#
# update :view => :peek
# #=> "jQuery(\"#mouse\").html(\"looking...")"
def update(*args)
wrap_in_javascript_for(:update, *args)

# - selector, method_name, render_args
# - method_name, render_args
# - nil, method_name, render_args
# - method_name
# - nil, method_name
def widget_call(*args)
selector, method_name, method_args = Apotomo.js_generator.extract_args(3, args)
Apotomo.js_generator.element_call(name, selector, method_name, Array.wrap(method_args))
end

private
def wrap_in_javascript_for(mode, *args)
selector = args.first.is_a?(String) ? args.shift : false
content = render(*args)

selector ?
Apotomo.js_generator.send(mode, selector, content) : # replace(:twitter)
Apotomo.js_generator.send("#{mode}_id", name, content) # replace_id(:twitter)

# If you call a method corresponding to JavaScript action (#replace, #update, etc.),
# it calls the corresponding +JavascriptGenerator+ method with rendered content.
# The same options as #render plus an optional +selector+ to change the selector are supposed.
[:update, :replace, :update_text, :append, :prepend, :after, :before, :wrap, :wrap_inner, :wrap_all].each do |helper_name|
define_method helper_name do |*args, &block|
selector = args.first.is_a?(String) ? args.shift : nil
content = render(*args, &block)

args = selector ? [nil, selector, [content]] : [name, nil, [content]]
Apotomo.js_generator.send("#{helper_name}", *args)
end
end
end
end
Loading