mjs 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Your Name
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,89 @@
1
+ Mjs
2
+ ===
3
+
4
+ A slice for the Merb framework that offers Ajax actions like RJS with jQuery
5
+ This privides new 'link_to' method for clients and 'page' object for servers.
6
+
7
+
8
+ Methods
9
+ =======
10
+
11
+ * Merb::Controller#link_to(name, url='', opts={})
12
+ link_to now recognize :remote, :submit option
13
+
14
+ * Merb::Controller#page
15
+ page method is reserved for RJS object.
16
+ You can access dom elements by it as many times and in anywhere.
17
+ No more ugly render(:update) block, just type 'page' as you want!
18
+
19
+
20
+ Setup
21
+ =====
22
+
23
+ Include jQuery as usual
24
+
25
+ app/views/layouts/application.html.erb:
26
+ <%= js_include_tag "jquery.js" -%>
27
+
28
+
29
+ Example1
30
+ ========
31
+ [ajax get request]
32
+
33
+ 1. Add :remote option to create ajax link
34
+
35
+ app/views/top/index.html.erb:
36
+ <div id="message"></div>
37
+ <%= link_to "hello", url(:action=>"hello"), :remote=>true %>
38
+ ^^^^^^^^^^^^^
39
+ generates:
40
+ <a onclick="; $.getScript('/top/hello'); return false;" href="#">hello</a>
41
+
42
+ 2. Use 'page' object to respond as RJS.
43
+
44
+ app/controllers/top.rb:
45
+ def hello
46
+ page[:message].text "Good morning!"
47
+ return page
48
+ end
49
+
50
+ generates:
51
+ $("#message").text("Good morning!");
52
+
53
+
54
+ Example2
55
+ ========
56
+ [ajax post request]
57
+
58
+ 1. Add :submit option to specify a DOM element that should be serialized
59
+
60
+ app/views/top/index.html.erb:
61
+ <div id="edit">
62
+ <%= text_field ... %>
63
+ <%= text_area ... %>
64
+ <%= link_to "update", url(:action=>"update"), :submit=>:edit %>
65
+ ^^^^^^^^^^^^^
66
+ generates:
67
+ <a onclick="; $.post('/top/update', $('#edit input').serialize(), null, 'script');; return false;" href="#">update</a>
68
+
69
+ 2. enjoy 'page' as you like
70
+
71
+ app/controllers/top.rb:
72
+ def update(id)
73
+ @item = Item.find(id)
74
+ ... # update logic is here
75
+ page[:message].text "successfully saved"
76
+ update_canvas_for(@item)
77
+ return page
78
+ end
79
+
80
+ private
81
+ def update_canvas_for(item)
82
+ page[:workspace].html partial("record", :item=>item)
83
+ rescue => error
84
+ page[:message].text error.to_s
85
+ page << "alert('something wrong!')"
86
+ end
87
+
88
+
89
+ Copyright (c) 2008 maiha@wota.jp, released under the MIT license
data/Rakefile ADDED
@@ -0,0 +1,55 @@
1
+ require 'rubygems'
2
+ require 'rake/gempackagetask'
3
+
4
+ require 'merb-core'
5
+ require 'merb-core/tasks/merb'
6
+
7
+ GEM_NAME = "mjs"
8
+ AUTHOR = "maiha"
9
+ EMAIL = "maiha@wota.jp"
10
+ HOMEPAGE = "http://github.com/maiha/mjs"
11
+ SUMMARY = "A slice for the Merb framework that offers Ajax actions like RJS with jQuery"
12
+ GEM_VERSION = "0.0.6"
13
+
14
+ spec = Gem::Specification.new do |s|
15
+ s.rubyforge_project = 'merb'
16
+ s.name = GEM_NAME
17
+ s.version = GEM_VERSION
18
+ s.platform = Gem::Platform::RUBY
19
+ s.has_rdoc = true
20
+ s.extra_rdoc_files = ["README", "LICENSE", 'TODO']
21
+ s.summary = SUMMARY
22
+ s.description = s.summary
23
+ s.author = AUTHOR
24
+ s.email = EMAIL
25
+ s.homepage = HOMEPAGE
26
+ s.add_dependency('merb-slices', '>= 1.0.7.1')
27
+ s.require_path = 'lib'
28
+ s.files = %w(LICENSE README Rakefile TODO) + Dir.glob("{lib,spec,app,public,stubs}/**/*")
29
+ end
30
+
31
+ Rake::GemPackageTask.new(spec) do |pkg|
32
+ pkg.gem_spec = spec
33
+ end
34
+
35
+ desc "Install the gem"
36
+ task :install do
37
+ Merb::RakeHelper.install(GEM_NAME, :version => GEM_VERSION)
38
+ end
39
+
40
+ desc "Uninstall the gem"
41
+ task :uninstall do
42
+ Merb::RakeHelper.uninstall(GEM_NAME, :version => GEM_VERSION)
43
+ end
44
+
45
+ desc "Create a gemspec file"
46
+ task :gemspec do
47
+ File.open("#{GEM_NAME}.gemspec", "w") do |file|
48
+ file.puts spec.to_ruby
49
+ end
50
+ end
51
+
52
+ require 'spec/rake/spectask'
53
+ require 'merb-core/test/tasks/spectasks'
54
+ desc 'Default: run spec examples'
55
+ task :default => 'spec'
data/TODO ADDED
@@ -0,0 +1,15 @@
1
+ TODO:
2
+
3
+ - Fix Mjs.description and Mjs.version
4
+ - Fix LICENSE with your name
5
+ - Fix Rakefile with your name and contact info
6
+ - Add your code to lib/mjs.rb
7
+ - Add your Merb rake tasks to lib/mjs/merbtasks.rb
8
+
9
+ Remove anything that you don't need:
10
+
11
+ - app/controllers/main.rb Mjs::Main controller
12
+ - app/views/layout/mjs.html.erb
13
+ - spec/controllers/main_spec.rb controller specs
14
+ - public/* any public files
15
+ - stubs/* any stub files
@@ -0,0 +1,5 @@
1
+ class Mjs::Application < Merb::Controller
2
+
3
+ controller_for_slice
4
+
5
+ end
@@ -0,0 +1,7 @@
1
+ class Mjs::Main < Mjs::Application
2
+
3
+ def index
4
+ render
5
+ end
6
+
7
+ end
@@ -0,0 +1,63 @@
1
+ module Merb
2
+ module Mjs
3
+ module ApplicationHelper
4
+ # @param *segments<Array[#to_s]> Path segments to append.
5
+ #
6
+ # @return <String>
7
+ # A path relative to the public directory, with added segments.
8
+ def image_path(*segments)
9
+ public_path_for(:image, *segments)
10
+ end
11
+
12
+ # @param *segments<Array[#to_s]> Path segments to append.
13
+ #
14
+ # @return <String>
15
+ # A path relative to the public directory, with added segments.
16
+ def javascript_path(*segments)
17
+ public_path_for(:javascript, *segments)
18
+ end
19
+
20
+ # @param *segments<Array[#to_s]> Path segments to append.
21
+ #
22
+ # @return <String>
23
+ # A path relative to the public directory, with added segments.
24
+ def stylesheet_path(*segments)
25
+ public_path_for(:stylesheet, *segments)
26
+ end
27
+
28
+ # Construct a path relative to the public directory
29
+ #
30
+ # @param <Symbol> The type of component.
31
+ # @param *segments<Array[#to_s]> Path segments to append.
32
+ #
33
+ # @return <String>
34
+ # A path relative to the public directory, with added segments.
35
+ def public_path_for(type, *segments)
36
+ ::Mjs.public_path_for(type, *segments)
37
+ end
38
+
39
+ # Construct an app-level path.
40
+ #
41
+ # @param <Symbol> The type of component.
42
+ # @param *segments<Array[#to_s]> Path segments to append.
43
+ #
44
+ # @return <String>
45
+ # A path within the host application, with added segments.
46
+ def app_path_for(type, *segments)
47
+ ::Mjs.app_path_for(type, *segments)
48
+ end
49
+
50
+ # Construct a slice-level path.
51
+ #
52
+ # @param <Symbol> The type of component.
53
+ # @param *segments<Array[#to_s]> Path segments to append.
54
+ #
55
+ # @return <String>
56
+ # A path within the slice source (Gem), with added segments.
57
+ def slice_path_for(type, *segments)
58
+ ::Mjs.slice_path_for(type, *segments)
59
+ end
60
+
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,16 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
2
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-us" lang="en-us">
3
+ <head>
4
+ <meta http-equiv="content-type" content="text/html; charset=utf-8" />
5
+ <title>Fresh Mjs Slice</title>
6
+ <link href="<%= public_path_for :stylesheet, 'master.css' %>" type="text/css" charset="utf-8" rel="stylesheet" media="all" />
7
+ <script src="<%= public_path_for :javascript, 'master.js' %>" type="text/javascript" charset="utf-8"></script>
8
+ </head>
9
+ <!-- you can override this layout at slices/mjs/app/views/layout/mjs.html.erb -->
10
+ <body class="mjs-slice">
11
+ <div id="container">
12
+ <h1>Mjs Slice</h1>
13
+ <div id="main"><%= catch_content :for_layout %></div>
14
+ </div>
15
+ </body>
16
+ </html>
@@ -0,0 +1 @@
1
+ <strong><%= slice.description %></strong> (v. <%= slice.version %>)
data/lib/mjs/helper.rb ADDED
@@ -0,0 +1,165 @@
1
+ require 'mjs/java_script_context'
2
+
3
+ module Mjs
4
+ module Helper
5
+ def page
6
+ @page ||= Mjs::JavaScriptContext.new
7
+ end
8
+
9
+ def remote_function(opts)
10
+ build_href(opts)
11
+ unless opts[:submit]
12
+ opts[:url] ||= opts[:href]
13
+ opts[:dataType] = "script"
14
+ end
15
+ function = "jQuery.ajax(%s);" % options_for_ajax(opts)
16
+ confirm = opts.delete(:confirm)
17
+ function = "if (confirm('#{escape_javascript(confirm)}')) { #{function}; }" if confirm
18
+ return function
19
+ end
20
+
21
+ # experimental: not tested yet
22
+ def button_to(name, url='', opts={})
23
+ ajax = remote_function(opts)
24
+ opts[:type] = 'button'
25
+ opts[:value] = name
26
+ opts[:remote] ||= true if opts[:submit]
27
+ if opts.delete(:remote)
28
+ ajax = remote_function(opts)
29
+ opts[:onclick] = "#{opts.delete(:onclick)}; #{ajax}; return false;"
30
+ end
31
+ %{<input #{ opts.to_xml_attributes }>}
32
+ end
33
+
34
+ # override! :link_to # for Ajax
35
+ def link_to(name, url='', opts={})
36
+ opts[:href] ||= url
37
+ opts[:remote] ||= true if opts[:submit]
38
+ return super unless opts.delete(:remote)
39
+
40
+ ajax = remote_function(opts)
41
+ opts[:onclick] = "#{opts.delete(:onclick)}; #{ajax}; return false;"
42
+ opts[:href] = '#'
43
+ %{<a #{ opts.to_xml_attributes }>#{name}</a>}
44
+ end
45
+
46
+ ######################################################################
47
+ ### derived from actionpack
48
+
49
+ JS_ESCAPE_MAP = {
50
+ '\\' => '\\\\', '</' => '<\/',
51
+ "\r\n" => '\n',
52
+ "\n" => '\n', "\r" => '\n',
53
+ '"' => '\\"',
54
+ "'" => "\\'" }
55
+
56
+ # Escape carrier returns and single and double quotes for JavaScript segments.
57
+ def escape_javascript(javascript)
58
+ if javascript
59
+ javascript.gsub(/(\\|<\/|\r\n|[\n\r"'])/) { JS_ESCAPE_MAP[$1] }
60
+ else
61
+ ''
62
+ end
63
+ end
64
+
65
+ private
66
+ def build_href(opts)
67
+ if opts[:href].is_a?(DataMapper::Resource)
68
+ record = opts[:href]
69
+ if record.new_record?
70
+ opts[:href] = resource(record.class.name.downcase.pluralize.intern, :new)
71
+ else
72
+ opts[:href] = resource(record)
73
+ end
74
+ end
75
+ opts
76
+ end
77
+
78
+ AJAX_FUNCTIONS = {
79
+ :before => :beforeSend,
80
+ :before_send => :beforeSend,
81
+ :beforeSend => :beforeSend,
82
+ :complete => :complete,
83
+ :success => :success,
84
+ :error => :error,
85
+ :dataFilter => :dataFilter,
86
+ :data_filter => :dataFilter,
87
+ :xhr => :xhr,
88
+ }
89
+
90
+
91
+ def options_for_ajax(options)
92
+ js_options = build_callbacks!(options)
93
+
94
+ submit = options.delete(:submit)
95
+ target =
96
+ case submit
97
+ when Symbol then "jQuery('##{submit} input, ##{submit} select, ##{submit} textarea')"
98
+ when String then "jQuery('#{submit}')"
99
+ when NilClass # GET requst
100
+ else
101
+ raise ArgumentError, "link_to :submit expects Symbol or String, but got #{submit.class.name}"
102
+ end
103
+ build_href(options)
104
+
105
+ if target
106
+ js_options[:type] = "'POST'"
107
+ js_options[:data] = "#{target}.serialize()"
108
+ end
109
+ js_options[:url] = "'#{options[:url] || options[:href]}'"
110
+ js_options[:dataType] = "'script'"
111
+
112
+ if js_options[:url].blank?
113
+ raise "Cannot build ajax options because url is blank. (#{options.inspect})"
114
+ end
115
+
116
+ options_for_javascript(js_options)
117
+ end
118
+
119
+ def options_for_javascript(options)
120
+ if options.empty?
121
+ '{}'
122
+ else
123
+ "{#{options.keys.map { |k| "#{k}:#{options[k]}" }.sort.join(', ')}}"
124
+ end
125
+ end
126
+
127
+ def jquery_selector(key)
128
+ case key
129
+ when Symbol then "jQuery('##{key}')"
130
+ when String then "jQuery('#{key}')"
131
+ else
132
+ raise "invalid jquery selector: [#{key.class}] #{key}"
133
+ end
134
+ end
135
+
136
+ # this method affects "options"
137
+ def build_callbacks!(options)
138
+ callbacks = {}
139
+
140
+ [:before, :complete].each do |event|
141
+ options[event] = Array(options[event])
142
+ end
143
+
144
+ # special callback (:spinner)
145
+ if options[:spinner]
146
+ target = jquery_selector(options.delete(:spinner))
147
+ options[:before] << "#{target}.show()"
148
+ options[:complete] << "#{target}.hide()"
149
+ end
150
+
151
+ [:before, :complete].each do |event|
152
+ options[event] = options[event].compact * ';'
153
+ end
154
+
155
+ options.each do |callback, code|
156
+ if (name = AJAX_FUNCTIONS[callback])
157
+ callbacks[name.to_s] = "function(request){#{code}}"
158
+ options.delete(callback)
159
+ end
160
+ end
161
+
162
+ callbacks
163
+ end
164
+ end
165
+ end