best_in_place 1.0.2 → 1.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -5,3 +5,4 @@ gemspec
5
5
 
6
6
  gem 'sqlite3-ruby', :require => 'sqlite3'
7
7
  gem 'jquery-rails'
8
+ gem 'rdiscount'
data/README.md CHANGED
@@ -26,11 +26,13 @@ The editor works by PUTting the updated value to the server and GETting the upda
26
26
  - Allows external activator
27
27
  - ESC key destroys changes (requires user confirmation)
28
28
  - Autogrowing textarea
29
-
30
- ---
29
+ - Helper for generating the best_in_place field only if a condition is satisfied
30
+ - Provided test helpers to be used in your integration specs
31
+ - Custom display methods
31
32
 
32
33
  ##Usage of Rails 3 Gem
33
34
 
35
+ ###best_in_place
34
36
  **best_in_place object, field, OPTIONS**
35
37
 
36
38
  Params:
@@ -48,10 +50,38 @@ Options:
48
50
  If not defined it will show *"-"*.
49
51
  - **:activator**: Is the DOM object that can activate the field. If not defined the user will making editable by clicking on it.
50
52
  - **:sanitize**: True by default. If set to false the input/textarea will accept html tags.
51
- - **:html_args**: Hash of html arguments, such as maxlength, default-value etc.
53
+ - **:html_attrs**: Hash of html arguments, such as maxlength, default-value etc.
54
+ - **:inner_class**: Class that is set to the rendered form.
55
+ - **:display_as**: A model method which will be called in order to display
56
+ this field.
57
+
58
+ ###best_in_place_if
59
+ **best_in_place_if condition, object, field, OPTIONS**
60
+
61
+ It allows us to use best_in_place only if the first new parameter, a
62
+ condition, is satisfied. Specifically:
63
+
64
+ * Will show a normal best_in_place if the condition is satisfied
65
+ * Will only show the attribute from the instance if the condition is not satisfied
66
+
67
+ Say we have something like
68
+
69
+ <%= best_in_place condition, @user, :name, :type => :input %>
70
+
71
+ In case *condition* is satisfied, the outcome will be just the same as:
72
+
73
+ <%= best_in_place @user, :name, :type => :input %>
74
+
75
+ Otherwise, we will have the same outcome as:
76
+
77
+ <%= @user.name %>
52
78
 
79
+ It is a very useful feature to use with, for example, [Ryan Bates](https://github.com/ryanb)' [CanCan](https://github.com/ryanb/cancan), so we only allow BIP edition if the current user has permission to do it.
53
80
 
54
- I created a [test_app](https://github.com/bernat/best_in_place/tree/master/test_app) and a running demo in heroku to test the features.
81
+ ---
82
+
83
+ ##TestApp and examples
84
+ A [test_app](https://github.com/bernat/best_in_place/tree/master/test_app) was created, and can be seen in action in a [running demo on heroku](http://bipapp.heroku.com).
55
85
 
56
86
  Examples (code in the views):
57
87
 
@@ -69,83 +99,230 @@ Examples (code in the views):
69
99
 
70
100
  <%= best_in_place @user, :country, :type => :select, :collection => [[1, "Spain"], [2, "Italy"], [3, "Germany"], [4, "France"]] %>
71
101
 
72
- Of course it can take an instance or global variable for the collection, just remember the structure [[key, value], [key, value],...].
102
+ Of course it can take an instance or global variable for the collection, just remember the structure `[[key, value], [key, value],...]`.
73
103
  The key can be a string or an integer.
74
104
 
75
105
  ### Checkbox
76
106
 
77
107
  <%= best_in_place @user, :receive_emails, :type => :checkbox, :collection => ["No, thanks", "Yes, of course!"] %>
78
108
 
79
- The first value is always the negative boolean value and the second the positive. Structure: ["false value", "true value"].
109
+ The first value is always the negative boolean value and the second the positive. Structure: `["false value", "true value"]`.
80
110
  If not defined, it will default to *Yes* and *No* options.
81
111
 
82
- ### Display server validation errors
112
+ ## Controller response and respond_with_bip
83
113
 
84
- If you are using a Rails application, your controller's should respond to json in case of error.
85
- Example:
114
+ Your controller should respond to json as it's the format used by best in
115
+ place javascript. A simple example would be:
86
116
 
87
- def update
88
- @user = User.find(params[:id])
117
+ class UserController < ApplicationController
118
+ def update
119
+ @user = User.find(params[:id])
89
120
 
90
- respond_to do |format|
91
- if @user.update_attributes(params[:user])
92
- format.html { redirect_to(@user, :notice => 'User was successfully updated.') }
93
- format.json { head :ok }
94
- else
95
- format.html { render :action => "edit" }
96
- format.json { render :json => @user.errors.full_messages, :status => :unprocessable_entity }
121
+ respond_to do |format|
122
+ if @user.update_attributes(params[:user])
123
+ format.html { redirect_to(@user, :notice => 'User was successfully updated.') }
124
+ format.json { head :ok }
125
+ else
126
+ format.html { render :action => "edit" }
127
+ format.json { render :json => @user.errors.full_messages, :status => :unprocessable_entity }
128
+ end
97
129
  end
98
130
  end
99
131
  end
100
132
 
101
- At the same time, you must define the restrictions, validations and error messages in the model, as the example below:
102
-
103
- class User < ActiveRecord::Base
104
- validates :name,
105
- :length => { :minimum => 2, :maximum => 24, :message => "has invalid length"},
106
- :presence => {:message => "can't be blank"}
107
- validates :last_name,
108
- :length => { :minimum => 2, :maximum => 24, :message => "has invalid length"},
109
- :presence => {:message => "can't be blank"}
110
- validates :address,
111
- :length => { :minimum => 5, :message => "too short length"},
112
- :presence => {:message => "can't be blank"}
113
- validates :email,
114
- :presence => {:message => "can't be blank"},
115
- :format => {:with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i, :message => "has wrong email format"}
116
- validates :zip, :numericality => true, :length => { :minimum => 5 }
133
+ If you respond with a json like `{:display_as => "New value to show"}` with
134
+ status 200 (ok), then the updated field will show *New value to show* after
135
+ being updated. This is needed in order to support the custom display methods,
136
+ and it's automatically handled if you use the new method to encapsulate
137
+ the responses:
138
+
139
+
140
+ respond_to do |format|
141
+ if @user.update_attributes(params[:user])
142
+ format.html { redirect_to(@user, :notice => 'User was successfully updated.') }
143
+ format.json { respond_with_bip(@user) }
144
+ else
145
+ format.html { render :action => "edit" }
146
+ format.json { respond_with_bip(@user) }
147
+ end
148
+ end
149
+
150
+ This will be exactly the same as the previous example, but with support to
151
+ handle custom display methods.
152
+
153
+ ##Using custom display methods
154
+
155
+ As of best in place 1.0.3 you can use custom methods in your model in order to
156
+ decide how a certain field has to be displayed. You can write something like:
157
+
158
+ = best_in_place @user, :description, :type => :textarea, :display_as => :mk_description
159
+
160
+ Then instead of using `@user.description` to show the actual value, best in
161
+ place will call `@user.mk_description`. This can be used for any kind of
162
+ custom formatting, text with markdown, currency values, etc...
163
+
164
+ Because best in place has no way to call that method in your model from
165
+ javascript after a successful update, the only way to display the new correct
166
+ value after an edition is to use the provided methods to respond in your
167
+ controllers, or implement the same in your own way.
168
+
169
+ If you respond a successful update with a json having a `display_as` key, that
170
+ value will be used to update the value in the view. The provided
171
+ `respond_with_bip` handles this for you, but if you want you can always
172
+ customize this behaviour.
173
+
174
+
175
+ ##Non Active Record environments
176
+ We are not planning to support other ORMs apart from Active Record, at least for now. So, you can perfectly consider the following workaround as *the right way* until a specific implementation is done for your ORM.
177
+
178
+ Best In Place automatically assumes that Active Record is the ORM you are using. However, this might not be your case, as you might use another ORM (or not ORM at all for that case!). Good news for you: even in such situation Best In Place can be used!
179
+
180
+ Let's setup an example so we can illustrate how to use Best In Place too in a non-ORM case. Imagine you have an awesome ice cream shop, and you have a model representing a single type of ice cream. The IceCream model has a name, a description, a... nevermind. The thing is that it also has a stock, which is a combination of flavour and size. A big chocolate ice cream (yummy!), a small paella ice cream (...really?), and so on. Shall we see some code?
181
+
182
+ class IceCream < ActiveRecord::Base
183
+ serialize :stock, Hash
184
+
185
+ # consider the get_stock and set_stock methods are already defined
117
186
  end
118
187
 
119
- When the user tries to introduce invalid data, the error messages defined in the model will be displayed in pop-up windows using the jQuery.purr plugin.
188
+ Imagine we want to have a grid showing all the combinations of flavour and size and, for each combination, an editable stock. Since the stock for a flavour and a size is not a single and complete model attribute, we cannot use Best In Place *directly*. But we can set it up with an easy workaround.
189
+
190
+ In the view, we'd do:
191
+
192
+ // @ice_cream is already available
193
+ - flavours = ... // get them somewhere
194
+ - sizes = ... // get them somewhere
195
+ %table
196
+ %tr
197
+ - ([""] + flavours).each do |flavour|
198
+ %th= flavour
199
+ - sizes.each do |size|
200
+ %tr
201
+ %th= size
202
+ - flavours.each do |flavour|
203
+ - v = @ice_cream.get_stock(:flavour => flavour, :size => size)
204
+ %td= best_in_place v, :to_i, :type => :input, :path => set_stock_ice_cream_path(:flavour => flavour, :size => size)
205
+
206
+ Now we need a route to which send the stock updates:
207
+
208
+ TheAwesomeIceCreamShop::Application.routes.draw do
209
+ ...
210
+
211
+ resources :ice_creams, :only => :none do
212
+ member do
213
+ put :set_stock
214
+ end
215
+ end
216
+
217
+ ...
218
+ end
219
+
220
+ And finally we need a controller:
221
+
222
+
223
+ class IceCreamsController < ApplicationController::Base
224
+ respond_to :html, :json
225
+
226
+ ...
227
+
228
+ def set_stock
229
+ flavour = params[:flavour]
230
+ size = params[:size]
231
+ new_stock = (params["fixnum"] || {})["to_i"]
232
+
233
+ @ice_cream.set_stock(new_stock, { :flavour => flavour, :size => size })
234
+ if @ice_cream.save
235
+ head :ok
236
+ else
237
+ render :json => @ice_cream.errors.full_messages, :status => :unprocessable_entity
238
+ end
239
+ end
240
+
241
+ ...
242
+
243
+ end
244
+
245
+ And this is how it is done!
246
+
247
+ ---
248
+
249
+ ##Test Helpers
250
+ Best In Place has also some helpers that may be very useful for integration testing. Since it might very common to test some views using Best In Place, some helpers are provided to ease it.
251
+
252
+ As of now, a total of four helpers are available. There is one for each of the following BIP types: a plain text input, a textarea, a boolean input and a selector. Its function is to simulate the user's action of filling such fields.
253
+
254
+ These four helpers are listed below:
255
+
256
+ * **bip_area(model, attr, new_value)**
257
+ * **bip_text(model, attr, new_value)**
258
+ * **bip_bool(model, attr)**
259
+ * **bip_select(model, attr, name)**
260
+
261
+ The parameters are defined here (some are method-specific):
262
+
263
+ * **model**: the model to which this action applies.
264
+ * **attr**: the attribute of the model to which this action applies.
265
+ * **new_value** (only **bip_area** and **bip_text**): the new value with which to fill the BIP field.
266
+ * **name** (only **bip_select**): the name to select from the dropdown selector.
120
267
 
121
268
  ---
122
269
 
123
270
  ##Installation
124
271
 
125
- It works by simply copying and loading the files from the folder **/public/javascripts** to your application and loading them in your layouts
126
- in the following order:
272
+ ###Rails 3.1 and higher
127
273
 
128
- - jquery-1.4.4.js
129
- - jquery.purr.js
130
- - **best_in_place.js**
274
+ Installing *best_in_place* is very easy and straight-forward, even more
275
+ thanks to Rails 3.1. Just begin including the gem in your Gemfile:
131
276
 
132
- The last one you can copy it (and keeping up to date to the last version) by running the following generator in your application's root.
133
- Remember to do it every time you update the gem (or you will see no change).
277
+ gem "best_in_place"
134
278
 
135
- rails g best_in_place:setup
279
+ After that, specify the use of the jquery, jquery.purr and best in place
280
+ javascripts in your application.js:
281
+
282
+ //= require jquery
283
+ //= require jquery.purr
284
+ //= require best_in_place
136
285
 
137
- To be able to use the script the following block must be added as well:
286
+ Then, just add a binding to prepare all best in place fields when the document is ready:
138
287
 
139
288
  $(document).ready(function() {
140
289
  /* Activating Best In Place */
141
- jQuery(".best_in_place").best_in_place()
290
+ jQuery(".best_in_place").best_in_place();
142
291
  });
143
292
 
144
- In order to use the Rails 3 gem, just add the following line to the gemfile:
293
+ You are done!
145
294
 
146
- gem "best_in_place"
295
+ ###Rails 3.0 and lower
296
+
297
+ Installing *best_in_place* for Rails 3.0 or below is a little bit
298
+ different, since the master branch is specifically updated for Rails
299
+ 3.1. But don't be scared, you'll be fine!
300
+
301
+ Rails 3.0 support will be held in the 0.2.X versions, but we have planned not to continue developing for this version of Rails. Nevertheless, you can by implementing what you want and sending us a pull request.
302
+
303
+ First, add the gem's 0.2 version in the Gemfile:
304
+
305
+ gem "best_in_place", "~> 0.2.0"
306
+
307
+ After that, install and load all the javascripts from the folder
308
+ **/public/javascripts** in your layouts. They have to be in the order:
309
+
310
+ * jquery
311
+ * jquery.purr
312
+ * **best_in_place**
313
+
314
+ You can automatize this installation by doing
147
315
 
148
- ----
316
+ rails g best_in_place:setup
317
+
318
+ Finally, as for Rails 3.1, just add a binding to prepare all best in place fields when the document is ready:
319
+
320
+ $(document).ready(function() {
321
+ /* Activating Best In Place */
322
+ jQuery(".best_in_place").best_in_place();
323
+ });
324
+
325
+ ---
149
326
 
150
327
  ## Security
151
328
 
@@ -154,17 +331,45 @@ If the script is used with the Rails Gem no html tags will be allowed unless the
154
331
  <meta name="csrf-param" content="authenticity_token"/>
155
332
  <meta name="csrf-token" content="YOUR UNIQUE TOKEN HERE"/>
156
333
 
334
+ ---
335
+
157
336
  ##TODO
158
337
 
159
338
  - Client Side Validation definitions
160
339
  - Accepting more than one handler to activate best_in_place fields
161
- - Specs *(sacrilege!!)*
340
+
341
+ ---
342
+
343
+ ## Development
344
+
345
+ Fork the project on [github](https://github.com/bernat/best_in_place 'bernat / best_in_place on Github')
346
+
347
+ $ git clone <<your fork>
348
+ $ cd best_in_place
349
+ $ bundle
350
+
351
+ ### Prepare the test app
352
+
353
+ $ cd test_app
354
+ $ bundle
355
+ $ bundle exec rake db:test:prepare
356
+ $ cd ..
357
+
358
+ ### Run the specs
359
+
360
+ $ bundle exec rspec spec/
361
+
362
+ ### Bundler / gem troubleshooting
363
+
364
+ - make sure you've run the bundle command for both the app and test_app!
365
+ - run bundle update <<gem name> (in the right place) for any gems that are causing issues
162
366
 
163
367
  ---
164
368
 
165
369
  ##Changelog
166
370
 
167
- - v.0.1.0 Initial deploy
371
+ ###Master branch (and part of the Rails 3.0 branch)
372
+ - v.0.1.0 Initial commit
168
373
  - v.0.1.2 Fixing errors in collections (taken value[0] instead of index) and fixing test_app controller responses
169
374
  - v.0.1.3 Bug in Rails Helper. Key wrongly considered an Integer.
170
375
  - v.0.1.4 Adding two new parameters for further customization urlObject and nilValue and making input update on blur.
@@ -173,9 +378,23 @@ If the script is used with the Rails Gem no html tags will be allowed unless the
173
378
  of key ESCAPE for destroying changes before they are made permanent (in inputs and textarea).
174
379
  - v.0.1.6-0.1.7 Avoiding request when the input is not modified and allowing the user to not sanitize input data.
175
380
  - v.0.1.8 jslint compliant, sanitizing tags in the gem, getting right csrf params, controlling size of textarea (elastic script, for autogrowing textarea)
381
+ - v.0.1.9 Adding elastic autogrowing textareas
382
+ - v.1.0.0 Setting RSpec and Capybara up, and adding some utilities. Mantaining some HTML attributes. Fix a respond_with bug (thanks, @moabite). Triggering ajax:success when ajax call is complete (thanks, @indrekj). Setting up Travis CI. Updated for Rails 3.1.
383
+ - v.1.0.1 Fixing a double initialization bug
384
+ - v.1.0.2 New bip_area text helper to work with text areas.
385
+ - v.1.0.3 replace apostrophes in collection with corresponding HTML entity,
386
+ thanks @taavo. Implemented `:display_as` option and adding
387
+ `respond_with_bip` to be used in the controller.
388
+
389
+ ###Rails 3.0 branch only
390
+ - v.0.2.0 Added RSpec and Capybara setup, and some tests. Fix countries map syntax, Allowing href and some other HTML attributes. Adding Travis CI too. Added the best_in_place_if option. Added ajax:success trigger, thanks to @indrekj.
391
+ - v.0.2.1 Fixing double initialization bug.
392
+ - v.0.2.2 New bip_area text helper.
393
+
394
+ ---
176
395
 
177
396
  ##Authors, License and Stuff
178
397
 
179
398
  Code by [Bernat Farrero](http://bernatfarrero.com) from [Itnig Web Services](http://itnig.net) (it was based on the [original project](http://github.com/janv/rest_in_place/) of Jan Varwig) and released under [MIT license](http://www.opensource.org/licenses/mit-license.php).
180
399
 
181
- Many thanks to the contributors: [Roger Campos](http://github.com/rogercampos) and [Jack Senechal](https://github.com/jacksenechal)
400
+ Many thanks to the contributors: [Roger Campos](http://github.com/rogercampos), [Jack Senechal](https://github.com/jacksenechal) and [Albert Bellonch](https://github.com/albertbellonch).
@@ -31,8 +31,20 @@ BestInPlaceEditor.prototype = {
31
31
  // Public Interface Functions //////////////////////////////////////////////
32
32
 
33
33
  activate : function() {
34
+ var to_display = "";
35
+ if (this.isNil) {
36
+ to_display = "";
37
+ }
38
+ else if (this.original_content) {
39
+ to_display = this.original_content;
40
+ }
41
+ else {
42
+ to_display = this.element.html();
43
+ }
44
+
34
45
  var elem = this.isNil ? "" : this.element.html();
35
46
  this.oldValue = elem;
47
+ this.display_value = to_display;
36
48
  $(this.activator).unbind("click", this.clickHandler);
37
49
  this.activateForm();
38
50
  },
@@ -91,6 +103,7 @@ BestInPlaceEditor.prototype = {
91
103
  self.nil = self.nil || jQuery(this).attr("data-nil");
92
104
  self.inner_class = self.inner_class || jQuery(this).attr("data-inner-class");
93
105
  self.html_attrs = self.html_attrs || jQuery(this).attr("data-html-attrs");
106
+ self.original_content = self.original_content || jQuery(this).attr("data-original-content");
94
107
  });
95
108
 
96
109
  // Try Rails-id based if parents did not explicitly supply something
@@ -111,6 +124,7 @@ BestInPlaceEditor.prototype = {
111
124
  self.nil = self.element.attr("data-nil") || self.nil || "-";
112
125
  self.inner_class = self.element.attr("data-inner-class") || self.inner_class || null;
113
126
  self.html_attrs = self.element.attr("data-html-attrs") || self.html_attrs;
127
+ self.original_content = self.element.attr("data-original-content") || self.original_content;
114
128
 
115
129
  if (!self.element.attr("data-sanitize")) {
116
130
  self.sanitize = true;
@@ -177,7 +191,10 @@ BestInPlaceEditor.prototype = {
177
191
  // Handlers ////////////////////////////////////////////////////////////////
178
192
 
179
193
  loadSuccessCallback : function(data) {
180
- this.element.html(data[this.objectName]);
194
+ var response = $.parseJSON($.trim(data));
195
+ if (response != null && response.hasOwnProperty("display_as")) {
196
+ this.element.html(response["display_as"]);
197
+ }
181
198
  this.element.trigger($.Event("ajax:success"), data);
182
199
 
183
200
  // Binding back after being clicked
@@ -216,7 +233,7 @@ BestInPlaceEditor.forms = {
216
233
  "input" : {
217
234
  activateForm : function() {
218
235
  var output = '<form class="form_in_place" action="javascript:void(0)" style="display:inline;">';
219
- output += '<input type="text" name="'+ this.attributeName + '" value="' + this.sanitizeValue(this.oldValue) + '"';
236
+ output += '<input type="text" name="'+ this.attributeName + '" value="' + this.sanitizeValue(this.display_value) + '"';
220
237
  if (this.inner_class != null) {
221
238
  output += ' class="' + this.inner_class + '"';
222
239
  }
@@ -301,7 +318,7 @@ BestInPlaceEditor.forms = {
301
318
 
302
319
  // construct the form
303
320
  var output = '<form action="javascript:void(0)" style="display:inline;"><textarea>';
304
- output += this.sanitizeValue(this.oldValue);
321
+ output += this.sanitizeValue(this.display_value);
305
322
  output += '</textarea></form>';
306
323
  this.element.html(output);
307
324
  this.setHtmlAttributes();
data/lib/best_in_place.rb CHANGED
@@ -1,6 +1,8 @@
1
1
  require "best_in_place/utils"
2
2
  require "best_in_place/helper"
3
3
  require "best_in_place/engine"
4
+ require "best_in_place/controller_extensions"
5
+ require "best_in_place/display_methods"
4
6
 
5
7
  module BestInPlace
6
8
  autoload :TestHelpers, "best_in_place/test_helpers"
@@ -0,0 +1,23 @@
1
+ module BestInPlace
2
+ module ControllerExtensions
3
+ def respond_with_bip(obj)
4
+ obj.changed? ? respond_bip_error(obj) : respond_bip_ok(obj)
5
+ end
6
+
7
+ private
8
+ def respond_bip_ok(obj)
9
+ klass = obj.class.to_s
10
+ updating_attr = params[klass.underscore].keys.first
11
+
12
+ if renderer = BestInPlace::DisplayMethods.lookup(klass, updating_attr)
13
+ render :json => {:display_as => obj.send(renderer)}.to_json
14
+ else
15
+ head :ok
16
+ end
17
+ end
18
+
19
+ def respond_bip_error(obj)
20
+ render :json => obj.errors.full_messages, :status => :unprocessable_entity
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,16 @@
1
+ module BestInPlace
2
+ module DisplayMethods
3
+ extend self
4
+
5
+ @@table = Hash.new { |h,k| h[k] = Hash.new(&h.default_proc) }
6
+
7
+ def lookup(klass, attr)
8
+ foo = @@table[klass.to_s][attr.to_s]
9
+ foo == {} ? nil : foo
10
+ end
11
+
12
+ def add(klass, attr, display_as)
13
+ @@table[klass.to_s][attr.to_s] = display_as.to_sym
14
+ end
15
+ end
16
+ end
@@ -2,6 +2,7 @@ module BestInPlace
2
2
  class Engine < Rails::Engine
3
3
  initializer "setup for rails" do
4
4
  ActionView::Base.send(:include, BestInPlace::BestInPlaceHelpers)
5
+ ActionController::Base.send(:include, BestInPlace::ControllerExtensions)
5
6
  end
6
7
  end
7
8
  end
@@ -5,7 +5,9 @@ module BestInPlace
5
5
  opts[:type] ||= :input
6
6
  opts[:collection] ||= []
7
7
  field = field.to_s
8
- value = object.send(field).blank? ? "" : object.send(field)
8
+
9
+ value = build_value_for(object, field, opts)
10
+
9
11
  collection = nil
10
12
  if opts[:type] == :select && !opts[:collection].blank?
11
13
  v = object.send(field)
@@ -24,13 +26,14 @@ module BestInPlace
24
26
  out << " id='#{BestInPlace::Utils.build_best_in_place_id(object, field)}'"
25
27
  out << " data-url='#{opts[:path].blank? ? url_for(object).to_s : url_for(opts[:path])}'"
26
28
  out << " data-object='#{object.class.to_s.gsub("::", "_").underscore}'"
27
- out << " data-collection='#{collection}'" unless collection.blank?
29
+ out << " data-collection='#{collection.gsub(/'/, "&#39;")}'" unless collection.blank?
28
30
  out << " data-attribute='#{field}'"
29
31
  out << " data-activator='#{opts[:activator]}'" unless opts[:activator].blank?
30
32
  out << " data-nil='#{opts[:nil].to_s}'" unless opts[:nil].blank?
31
33
  out << " data-type='#{opts[:type].to_s}'"
32
34
  out << " data-inner-class='#{opts[:inner_class].to_s}'" if opts[:inner_class]
33
35
  out << " data-html-attrs='#{opts[:html_attrs].to_json}'" unless opts[:html_attrs].blank?
36
+ out << " data-original-content='#{object.send(field)}'" if opts[:display_as]
34
37
  if !opts[:sanitize].nil? && !opts[:sanitize]
35
38
  out << " data-sanitize='false'>"
36
39
  out << sanitize(value.to_s, :tags => %w(b i u s a strong em p h1 h2 h3 h4 h5 ul li ol hr pre span img br), :attributes => %w(id class href))
@@ -45,7 +48,17 @@ module BestInPlace
45
48
  if condition
46
49
  best_in_place(object, field, opts)
47
50
  else
48
- object.send field
51
+ build_value_for object, field, opts
52
+ end
53
+ end
54
+
55
+ private
56
+ def build_value_for(object, field, opts)
57
+ if opts[:display_as]
58
+ BestInPlace::DisplayMethods.add(object.class.to_s, field, opts[:display_as])
59
+ object.send(opts[:display_as]).to_s
60
+ else
61
+ object.send(field).blank? ? "" : object.send(field).to_s
49
62
  end
50
63
  end
51
64
  end
@@ -1,3 +1,3 @@
1
1
  module BestInPlace
2
- VERSION = "1.0.2"
2
+ VERSION = "1.0.3"
3
3
  end
@@ -123,6 +123,16 @@ describe BestInPlace::BestInPlaceHelpers do
123
123
  span = nk.css("span")
124
124
  span.attribute("data-activator").value.should == "awesome"
125
125
  end
126
+
127
+ describe "display_as" do
128
+ it "should render the address with a custom renderer" do
129
+ @user.should_receive(:address_format).and_return("the result")
130
+ out = helper.best_in_place @user, :address, :display_as => :address_format
131
+ nk = Nokogiri::HTML.parse(out)
132
+ span = nk.css("span")
133
+ span.text.should == "the result"
134
+ end
135
+ end
126
136
  end
127
137
 
128
138
 
@@ -199,6 +209,18 @@ describe BestInPlace::BestInPlaceHelpers do
199
209
  it "should show the current country" do
200
210
  @span.text.should == "Italy"
201
211
  end
212
+
213
+ context "with an apostrophe in it" do
214
+ before do
215
+ @apostrophe_countries = [[1, "Joe's Country"], [2, "Bob's Country"]]
216
+ nk = Nokogiri::HTML.parse(helper.best_in_place @user, :country, :type => :select, :collection => @apostrophe_countries)
217
+ @span = nk.css("span")
218
+ end
219
+
220
+ it "should have a proper data collection" do
221
+ @span.attribute("data-collection").value.should == @apostrophe_countries.to_json
222
+ end
223
+ end
202
224
  end
203
225
  end
204
226
 
@@ -208,7 +230,7 @@ describe BestInPlace::BestInPlaceHelpers do
208
230
  @output = "Some Value"
209
231
  @field = :somefield
210
232
  @object = mock("object", @field => @output)
211
- @options = mock("options")
233
+ @options = {}
212
234
  end
213
235
  context "when the condition is true" do
214
236
  before {@condition = true}
@@ -161,5 +161,51 @@ describe "JS behaviour", :js => true do
161
161
  page.should have_content("Another")
162
162
  end
163
163
  end
164
+
165
+ describe "display_as" do
166
+ it "should render the address with a custom format" do
167
+ @user.save!
168
+ visit user_path(@user)
169
+
170
+ within("#address") do
171
+ page.should have_content("addr => [Via Roma 99]")
172
+ end
173
+ end
174
+
175
+ it "should still show the custom format after an error" do
176
+ @user.save!
177
+ visit user_path(@user)
178
+
179
+ bip_text @user, :address, "inva"
180
+
181
+ within("#address") do
182
+ page.should have_content("addr => [Via Roma 99]")
183
+ end
184
+ end
185
+
186
+ it "should show the new result with the custom format after an update" do
187
+ @user.save!
188
+ visit user_path(@user)
189
+
190
+ bip_text @user, :address, "New address"
191
+
192
+ within("#address") do
193
+ page.should have_content("addr => [New address]")
194
+ end
195
+ end
196
+
197
+ it "should display the original content when editing the form" do
198
+ @user.save!
199
+ visit user_path(@user)
200
+
201
+ id = BestInPlace::Utils.build_best_in_place_id @user, :address
202
+ page.execute_script <<-JS
203
+ $("##{id}").click();
204
+ JS
205
+
206
+ text = page.find("##{id} input").value
207
+ text.should == "Via Roma 99"
208
+ end
209
+ end
164
210
  end
165
211
 
data/test_app/Gemfile CHANGED
@@ -7,6 +7,8 @@ gem 'best_in_place', :path => ".."
7
7
 
8
8
  gem 'jquery-rails'
9
9
 
10
+ gem 'rdiscount'
11
+
10
12
  group :assets do
11
13
  gem 'sass-rails', '~> 3.1.4'
12
14
  gem 'coffee-rails', '~> 3.1.1'
@@ -69,10 +69,10 @@ class UsersController < ApplicationController
69
69
  respond_to do |format|
70
70
  if @user.update_attributes(params[:user])
71
71
  format.html { redirect_to(@user, :notice => 'User was successfully updated.') }
72
- format.json { head :ok }
72
+ format.json { respond_with_bip(@user) }
73
73
  else
74
74
  format.html { render :action => "edit" }
75
- format.json { render :json => @user.errors.full_messages, :status => :unprocessable_entity }
75
+ format.json { respond_with_bip(@user) }
76
76
  end
77
77
  end
78
78
  end
@@ -12,4 +12,12 @@ class User < ActiveRecord::Base
12
12
  :presence => {:message => "can't be blank"},
13
13
  :format => {:with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i, :message => "has wrong email format"}
14
14
  validates :zip, :numericality => true, :length => { :minimum => 5 }
15
- end
15
+
16
+ def address_format
17
+ "<b>addr => [#{address}]</b>".html_safe
18
+ end
19
+
20
+ def markdown_desc
21
+ RDiscount.new(description).to_html.html_safe
22
+ end
23
+ end
@@ -25,7 +25,7 @@
25
25
  <tr>
26
26
  <td>Address</td>
27
27
  <td id="address">
28
- <%= best_in_place @user, :address %>
28
+ <%= best_in_place @user, :address, :display_as => :address_format %>
29
29
  </td>
30
30
  </tr>
31
31
  <tr>
@@ -49,7 +49,7 @@
49
49
  <tr>
50
50
  <td>User description</td>
51
51
  <td id="description">
52
- <%= best_in_place @user, :description, :type => :textarea, :sanitize => false %>
52
+ <%= best_in_place @user, :description, :display_as => :markdown_desc, :type => :textarea, :sanitize => false %>
53
53
  </td>
54
54
  </table>
55
55
  <br />
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: best_in_place
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.2
4
+ version: 1.0.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-11-07 00:00:00.000000000 Z
12
+ date: 2011-12-04 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rails
16
- requirement: &84323650 !ruby/object:Gem::Requirement
16
+ requirement: &12601240 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: 3.1.0
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *84323650
24
+ version_requirements: *12601240
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: jquery-rails
27
- requirement: &84320540 !ruby/object:Gem::Requirement
27
+ requirement: &12616680 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '0'
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *84320540
35
+ version_requirements: *12616680
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: rspec-rails
38
- requirement: &84319130 !ruby/object:Gem::Requirement
38
+ requirement: &12615660 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ~>
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: 2.7.0
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *84319130
46
+ version_requirements: *12615660
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: nokogiri
49
- requirement: &84346000 !ruby/object:Gem::Requirement
49
+ requirement: &12614960 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ! '>='
@@ -54,10 +54,10 @@ dependencies:
54
54
  version: 1.5.0
55
55
  type: :development
56
56
  prerelease: false
57
- version_requirements: *84346000
57
+ version_requirements: *12614960
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: capybara
60
- requirement: &84344990 !ruby/object:Gem::Requirement
60
+ requirement: &12614300 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ! '>='
@@ -65,7 +65,7 @@ dependencies:
65
65
  version: 1.0.1
66
66
  type: :development
67
67
  prerelease: false
68
- version_requirements: *84344990
68
+ version_requirements: *12614300
69
69
  description: BestInPlace is a jQuery script and a Rails 3 helper that provide the
70
70
  method best_in_place to display any object field easily editable for the user by
71
71
  just clicking on it. It supports input data, text data, boolean data and custom
@@ -86,6 +86,8 @@ files:
86
86
  - lib/assets/javascripts/best_in_place.js
87
87
  - lib/assets/javascripts/jquery.purr.js
88
88
  - lib/best_in_place.rb
89
+ - lib/best_in_place/controller_extensions.rb
90
+ - lib/best_in_place/display_methods.rb
89
91
  - lib/best_in_place/engine.rb
90
92
  - lib/best_in_place/helper.rb
91
93
  - lib/best_in_place/test_helpers.rb
@@ -167,7 +169,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
167
169
  version: '0'
168
170
  segments:
169
171
  - 0
170
- hash: 38414223
172
+ hash: 1789915415382083168
171
173
  required_rubygems_version: !ruby/object:Gem::Requirement
172
174
  none: false
173
175
  requirements:
@@ -176,7 +178,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
176
178
  version: '0'
177
179
  segments:
178
180
  - 0
179
- hash: 38414223
181
+ hash: 1789915415382083168
180
182
  requirements: []
181
183
  rubyforge_project: best_in_place
182
184
  rubygems_version: 1.8.10