in_place_editing 1.0.0
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/.gitignore +4 -0
- data/README +14 -0
- data/Rakefile +24 -0
- data/in_place_editing.gemspec +20 -0
- data/init.rb +2 -0
- data/lib/in_place_editing.rb +28 -0
- data/lib/in_place_macros_helper.rb +80 -0
- data/test/in_place_editing_test.rb +89 -0
- data/test/test_helper.rb +8 -0
- metadata +77 -0
data/.gitignore
ADDED
data/README
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
InPlaceEditing
|
2
|
+
==============
|
3
|
+
|
4
|
+
Example:
|
5
|
+
|
6
|
+
# Controller
|
7
|
+
class BlogController < ApplicationController
|
8
|
+
in_place_edit_for :post, :title
|
9
|
+
end
|
10
|
+
|
11
|
+
# View
|
12
|
+
<%= in_place_editor_field :post, 'title' %>
|
13
|
+
|
14
|
+
Copyright (c) 2007 David Heinemeier Hansson, released under the MIT license
|
data/Rakefile
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'rake/rdoctask'
|
4
|
+
require 'bundler'
|
5
|
+
Bundler::GemHelper.install_tasks
|
6
|
+
|
7
|
+
desc 'Default: run unit tests.'
|
8
|
+
task :default => :test
|
9
|
+
|
10
|
+
desc 'Test in_place_editing plugin.'
|
11
|
+
Rake::TestTask.new(:test) do |t|
|
12
|
+
t.libs << 'lib'
|
13
|
+
t.pattern = 'test/**/*_test.rb'
|
14
|
+
t.verbose = true
|
15
|
+
end
|
16
|
+
|
17
|
+
desc 'Generate documentation for in_place_editing plugin.'
|
18
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
19
|
+
rdoc.rdoc_dir = 'rdoc'
|
20
|
+
rdoc.title = 'InPlaceEditing'
|
21
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
22
|
+
rdoc.rdoc_files.include('README')
|
23
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
24
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = "in_place_editing"
|
6
|
+
s.version = "1.0.0"
|
7
|
+
s.platform = Gem::Platform::RUBY
|
8
|
+
s.authors = ["David Heinemeier Hansson", "Jeremy Kemper", "Jose Fernandez", "Pawel Stradomski"]
|
9
|
+
s.email = ["mark@amerine.net"]
|
10
|
+
s.homepage = "https://github.com/amerine/in_place_editing"
|
11
|
+
s.summary = %q{In Place Editing Rails Plugin}
|
12
|
+
s.description = %q{In Place Editing Rails Plugin}
|
13
|
+
|
14
|
+
s.rubyforge_project = "in_place_editing"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
end
|
data/init.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
module InPlaceEditing
|
2
|
+
def self.included(base)
|
3
|
+
base.extend(ClassMethods)
|
4
|
+
end
|
5
|
+
|
6
|
+
# Example:
|
7
|
+
#
|
8
|
+
# # Controller
|
9
|
+
# class BlogController < ApplicationController
|
10
|
+
# in_place_edit_for :post, :title
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
# # View
|
14
|
+
# <%= in_place_editor_field :post, 'title' %>
|
15
|
+
#
|
16
|
+
module ClassMethods
|
17
|
+
def in_place_edit_for(object, attribute, options = {})
|
18
|
+
define_method("set_#{object}_#{attribute}") do
|
19
|
+
unless [:post, :put].include?(request.method) then
|
20
|
+
return render(:text => 'Method not allowed', :status => 405)
|
21
|
+
end
|
22
|
+
@item = object.to_s.camelize.constantize.find(params[:id])
|
23
|
+
@item.update_attribute(attribute, params[:value])
|
24
|
+
render :text => CGI::escapeHTML(@item.send(attribute).to_s)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module InPlaceMacrosHelper
|
2
|
+
# Makes an HTML element specified by the DOM ID +field_id+ become an in-place
|
3
|
+
# editor of a property.
|
4
|
+
#
|
5
|
+
# A form is automatically created and displayed when the user clicks the element,
|
6
|
+
# something like this:
|
7
|
+
# <form id="myElement-in-place-edit-form" target="specified url">
|
8
|
+
# <input name="value" text="The content of myElement"/>
|
9
|
+
# <input type="submit" value="ok"/>
|
10
|
+
# <a onclick="javascript to cancel the editing">cancel</a>
|
11
|
+
# </form>
|
12
|
+
#
|
13
|
+
# The form is serialized and sent to the server using an AJAX call, the action on
|
14
|
+
# the server should process the value and return the updated value in the body of
|
15
|
+
# the reponse. The element will automatically be updated with the changed value
|
16
|
+
# (as returned from the server).
|
17
|
+
#
|
18
|
+
# Required +options+ are:
|
19
|
+
# <tt>:url</tt>:: Specifies the url where the updated value should
|
20
|
+
# be sent after the user presses "ok".
|
21
|
+
#
|
22
|
+
# Addtional +options+ are:
|
23
|
+
# <tt>:rows</tt>:: Number of rows (more than 1 will use a TEXTAREA)
|
24
|
+
# <tt>:cols</tt>:: Number of characters the text input should span (works for both INPUT and TEXTAREA)
|
25
|
+
# <tt>:size</tt>:: Synonym for :cols when using a single line text input.
|
26
|
+
# <tt>:cancel_text</tt>:: The text on the cancel link. (default: "cancel")
|
27
|
+
# <tt>:save_text</tt>:: The text on the save link. (default: "ok")
|
28
|
+
# <tt>:loading_text</tt>:: The text to display while the data is being loaded from the server (default: "Loading...")
|
29
|
+
# <tt>:saving_text</tt>:: The text to display when submitting to the server (default: "Saving...")
|
30
|
+
# <tt>:external_control</tt>:: The id of an external control used to enter edit mode.
|
31
|
+
# <tt>:load_text_url</tt>:: URL where initial value of editor (content) is retrieved.
|
32
|
+
# <tt>:options</tt>:: Pass through options to the AJAX call (see prototype's Ajax.Updater)
|
33
|
+
# <tt>:with</tt>:: JavaScript snippet that should return what is to be sent
|
34
|
+
# in the AJAX call, +form+ is an implicit parameter
|
35
|
+
# <tt>:script</tt>:: Instructs the in-place editor to evaluate the remote JavaScript response (default: false)
|
36
|
+
# <tt>:click_to_edit_text</tt>::The text shown during mouseover the editable text (default: "Click to edit")
|
37
|
+
def in_place_editor(field_id, options = {})
|
38
|
+
function = "new Ajax.InPlaceEditor("
|
39
|
+
function << "'#{field_id}', "
|
40
|
+
function << "'#{url_for(options[:url])}'"
|
41
|
+
|
42
|
+
js_options = {}
|
43
|
+
|
44
|
+
if protect_against_forgery?
|
45
|
+
options[:with] ||= "Form.serialize(form)"
|
46
|
+
options[:with] += " + '&authenticity_token=' + encodeURIComponent('#{form_authenticity_token}')"
|
47
|
+
end
|
48
|
+
|
49
|
+
js_options['cancelText'] = %('#{options[:cancel_text]}') if options[:cancel_text]
|
50
|
+
js_options['okText'] = %('#{options[:save_text]}') if options[:save_text]
|
51
|
+
js_options['loadingText'] = %('#{options[:loading_text]}') if options[:loading_text]
|
52
|
+
js_options['savingText'] = %('#{options[:saving_text]}') if options[:saving_text]
|
53
|
+
js_options['rows'] = options[:rows] if options[:rows]
|
54
|
+
js_options['cols'] = options[:cols] if options[:cols]
|
55
|
+
js_options['size'] = options[:size] if options[:size]
|
56
|
+
js_options['externalControl'] = "'#{options[:external_control]}'" if options[:external_control]
|
57
|
+
js_options['loadTextURL'] = "'#{url_for(options[:load_text_url])}'" if options[:load_text_url]
|
58
|
+
js_options['ajaxOptions'] = options[:options] if options[:options]
|
59
|
+
js_options['htmlResponse'] = !options[:script] if options[:script]
|
60
|
+
js_options['callback'] = "function(form) { return #{options[:with]} }" if options[:with]
|
61
|
+
js_options['clickToEditText'] = %('#{options[:click_to_edit_text]}') if options[:click_to_edit_text]
|
62
|
+
js_options['textBetweenControls'] = %('#{options[:text_between_controls]}') if options[:text_between_controls]
|
63
|
+
function << (', ' + options_for_javascript(js_options)) unless js_options.empty?
|
64
|
+
|
65
|
+
function << ')'
|
66
|
+
|
67
|
+
javascript_tag(function)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Renders the value of the specified object and method with in-place editing capabilities.
|
71
|
+
def in_place_editor_field(object, method, tag_options = {}, in_place_editor_options = {})
|
72
|
+
instance_tag = ::ActionView::Helpers::InstanceTag.new(object, method, self)
|
73
|
+
tag_options = {:tag => "span",
|
74
|
+
:id => "#{object}_#{method}_#{instance_tag.object.id}_in_place_editor",
|
75
|
+
:class => "in_place_editor_field"}.merge!(tag_options)
|
76
|
+
in_place_editor_options[:url] = in_place_editor_options[:url] || url_for({ :action => "set_#{object}_#{method}", :id => instance_tag.object.id })
|
77
|
+
tag = content_tag(tag_options.delete(:tag), h(instance_tag.value(instance_tag.object)),tag_options)
|
78
|
+
return tag + in_place_editor(tag_options[:id], in_place_editor_options)
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + "/test_helper")
|
2
|
+
|
3
|
+
class InPlaceEditingTest < Test::Unit::TestCase
|
4
|
+
include InPlaceEditing
|
5
|
+
include InPlaceMacrosHelper
|
6
|
+
|
7
|
+
include ActionView::Helpers::UrlHelper
|
8
|
+
include ActionView::Helpers::TagHelper
|
9
|
+
include ActionView::Helpers::TextHelper
|
10
|
+
include ActionView::Helpers::FormHelper
|
11
|
+
include ActionView::Helpers::CaptureHelper
|
12
|
+
|
13
|
+
def setup
|
14
|
+
@controller = Class.new do
|
15
|
+
def url_for(options)
|
16
|
+
url = "http://www.example.com/"
|
17
|
+
url << options[:action].to_s if options and options[:action]
|
18
|
+
url
|
19
|
+
end
|
20
|
+
end
|
21
|
+
@controller = @controller.new
|
22
|
+
@protect_against_forgery = false
|
23
|
+
end
|
24
|
+
|
25
|
+
def protect_against_forgery?
|
26
|
+
@protect_against_forgery
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_in_place_editor_external_control
|
30
|
+
assert_dom_equal %(<script type=\"text/javascript\">\n//<![CDATA[\nnew Ajax.InPlaceEditor('some_input', 'http://www.example.com/inplace_edit', {externalControl:'blah'})\n//]]>\n</script>),
|
31
|
+
in_place_editor('some_input', {:url => {:action => 'inplace_edit'}, :external_control => 'blah'})
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_in_place_editor_size
|
35
|
+
assert_dom_equal %(<script type=\"text/javascript\">\n//<![CDATA[\nnew Ajax.InPlaceEditor('some_input', 'http://www.example.com/inplace_edit', {size:4})\n//]]>\n</script>),
|
36
|
+
in_place_editor('some_input', {:url => {:action => 'inplace_edit'}, :size => 4})
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_in_place_editor_cols_no_rows
|
40
|
+
assert_dom_equal %(<script type=\"text/javascript\">\n//<![CDATA[\nnew Ajax.InPlaceEditor('some_input', 'http://www.example.com/inplace_edit', {cols:4})\n//]]>\n</script>),
|
41
|
+
in_place_editor('some_input', {:url => {:action => 'inplace_edit'}, :cols => 4})
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_in_place_editor_cols_with_rows
|
45
|
+
assert_dom_equal %(<script type=\"text/javascript\">\n//<![CDATA[\nnew Ajax.InPlaceEditor('some_input', 'http://www.example.com/inplace_edit', {cols:40, rows:5})\n//]]>\n</script>),
|
46
|
+
in_place_editor('some_input', {:url => {:action => 'inplace_edit'}, :rows => 5, :cols => 40})
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_inplace_editor_loading_text
|
50
|
+
assert_dom_equal %(<script type=\"text/javascript\">\n//<![CDATA[\nnew Ajax.InPlaceEditor('some_input', 'http://www.example.com/inplace_edit', {loadingText:'Why are we waiting?'})\n//]]>\n</script>),
|
51
|
+
in_place_editor('some_input', {:url => {:action => 'inplace_edit'}, :loading_text => 'Why are we waiting?'})
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_in_place_editor_url
|
55
|
+
assert_match "Ajax.InPlaceEditor('id-goes-here', 'http://www.example.com/action_to_set_value')",
|
56
|
+
in_place_editor( 'id-goes-here', :url => { :action => "action_to_set_value" })
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_in_place_editor_load_text_url
|
60
|
+
assert_match "Ajax.InPlaceEditor('id-goes-here', 'http://www.example.com/action_to_set_value', {loadTextURL:'http://www.example.com/action_to_get_value'})",
|
61
|
+
in_place_editor( 'id-goes-here',
|
62
|
+
:url => { :action => "action_to_set_value" },
|
63
|
+
:load_text_url => { :action => "action_to_get_value" })
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_in_place_editor_html_response
|
67
|
+
assert_match "Ajax.InPlaceEditor('id-goes-here', 'http://www.example.com/action_to_set_value', {htmlResponse:false})",
|
68
|
+
in_place_editor( 'id-goes-here',
|
69
|
+
:url => { :action => "action_to_set_value" },
|
70
|
+
:script => true )
|
71
|
+
end
|
72
|
+
|
73
|
+
def form_authenticity_token
|
74
|
+
"authenticity token"
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_in_place_editor_with_forgery_protection
|
78
|
+
@protect_against_forgery = true
|
79
|
+
assert_match "Ajax.InPlaceEditor('id-goes-here', 'http://www.example.com/action_to_set_value', {callback:function(form) { return Form.serialize(form) + '&authenticity_token=' + encodeURIComponent('authenticity token') }})",
|
80
|
+
in_place_editor( 'id-goes-here', :url => { :action => "action_to_set_value" })
|
81
|
+
end
|
82
|
+
|
83
|
+
def test_in_place_editor_text_between_controls
|
84
|
+
assert_match "Ajax.InPlaceEditor('id-goes-here', 'http://www.example.com/action_to_set_value', {textBetweenControls:'or'})",
|
85
|
+
in_place_editor( 'id-goes-here',
|
86
|
+
:url => { :action => "action_to_set_value" },
|
87
|
+
:text_between_controls => "or" )
|
88
|
+
end
|
89
|
+
end
|
data/test/test_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: in_place_editing
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 1
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
version: 1.0.0
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- David Heinemeier Hansson
|
13
|
+
- Jeremy Kemper
|
14
|
+
- Jose Fernandez
|
15
|
+
- Pawel Stradomski
|
16
|
+
autorequire:
|
17
|
+
bindir: bin
|
18
|
+
cert_chain: []
|
19
|
+
|
20
|
+
date: 2011-03-07 00:00:00 -08:00
|
21
|
+
default_executable:
|
22
|
+
dependencies: []
|
23
|
+
|
24
|
+
description: In Place Editing Rails Plugin
|
25
|
+
email:
|
26
|
+
- mark@amerine.net
|
27
|
+
executables: []
|
28
|
+
|
29
|
+
extensions: []
|
30
|
+
|
31
|
+
extra_rdoc_files: []
|
32
|
+
|
33
|
+
files:
|
34
|
+
- .gitignore
|
35
|
+
- README
|
36
|
+
- Rakefile
|
37
|
+
- in_place_editing.gemspec
|
38
|
+
- init.rb
|
39
|
+
- lib/in_place_editing.rb
|
40
|
+
- lib/in_place_macros_helper.rb
|
41
|
+
- test/in_place_editing_test.rb
|
42
|
+
- test/test_helper.rb
|
43
|
+
has_rdoc: true
|
44
|
+
homepage: https://github.com/amerine/in_place_editing
|
45
|
+
licenses: []
|
46
|
+
|
47
|
+
post_install_message:
|
48
|
+
rdoc_options: []
|
49
|
+
|
50
|
+
require_paths:
|
51
|
+
- lib
|
52
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
segments:
|
58
|
+
- 0
|
59
|
+
version: "0"
|
60
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ">="
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
segments:
|
66
|
+
- 0
|
67
|
+
version: "0"
|
68
|
+
requirements: []
|
69
|
+
|
70
|
+
rubyforge_project: in_place_editing
|
71
|
+
rubygems_version: 1.3.7
|
72
|
+
signing_key:
|
73
|
+
specification_version: 3
|
74
|
+
summary: In Place Editing Rails Plugin
|
75
|
+
test_files:
|
76
|
+
- test/in_place_editing_test.rb
|
77
|
+
- test/test_helper.rb
|