mjs 0.0.6

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