in_place_editing 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
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
@@ -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,2 @@
1
+ ActionController::Base.send :include, InPlaceEditing
2
+ ActionController::Base.helper InPlaceMacrosHelper
@@ -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
@@ -0,0 +1,8 @@
1
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
2
+
3
+ require 'test/unit'
4
+ require 'rubygems'
5
+ require 'action_controller'
6
+ require 'action_controller/assertions'
7
+ require 'in_place_editing'
8
+ require 'in_place_macros_helper'
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