puffer 0.0.7 → 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. data/VERSION +1 -1
  2. data/app/cells/puffer/base/additional.html.erb +13 -5
  3. data/app/helpers/puffer_helper.rb +6 -1
  4. data/app/views/layouts/puffer.html.erb +5 -3
  5. data/app/views/puffer/_form.html.erb +1 -1
  6. data/app/views/puffer/associated/one.js.erb +9 -0
  7. data/app/views/puffer/edit.html.erb +1 -1
  8. data/app/views/puffer/index.html.erb +7 -7
  9. data/app/views/puffer/new.html.erb +1 -1
  10. data/app/views/puffer/show.html.erb +4 -4
  11. data/lib/generators/puffer/install/templates/puffer/javascripts/application.js +10 -0
  12. data/lib/generators/puffer/install/templates/puffer/javascripts/rails-src.js +36 -378
  13. data/lib/generators/puffer/install/templates/puffer/javascripts/right-autocompleter-src.js +621 -0
  14. data/lib/generators/puffer/install/templates/puffer/javascripts/right-autocompleter.js +18 -0
  15. data/lib/generators/puffer/install/templates/puffer/javascripts/right-autocompleter.js.gz +0 -0
  16. data/lib/generators/puffer/install/templates/puffer/stylesheets/puffer.css +107 -6
  17. data/lib/puffer/base.rb +4 -3
  18. data/lib/puffer/controller/actions.rb +4 -70
  19. data/lib/puffer/controller/dsl.rb +23 -5
  20. data/lib/puffer/controller/generated.rb +65 -0
  21. data/lib/puffer/extensions/activerecord.rb +3 -3
  22. data/lib/puffer/extensions/core.rb +14 -0
  23. data/lib/puffer/extensions/mapper.rb +20 -0
  24. data/lib/puffer/fields.rb +11 -2
  25. data/lib/puffer/fields/field.rb +24 -1
  26. data/lib/puffer/inputs.rb +1 -3
  27. data/lib/puffer/inputs/association.rb +27 -1
  28. data/lib/puffer/inputs/base.rb +12 -4
  29. data/lib/puffer/inputs/boolean.rb +1 -1
  30. data/lib/puffer/inputs/date_time.rb +1 -1
  31. data/lib/puffer/resource.rb +1 -1
  32. data/lib/puffer/resource/routing.rb +2 -2
  33. data/lib/puffer/resource/scoping.rb +5 -1
  34. data/puffer.gemspec +15 -5
  35. data/spec/dummy/app/controllers/admin/profiles_controller.rb +2 -2
  36. data/spec/dummy/app/views/admin/users/index.html.erb +3 -1
  37. data/spec/dummy/config/database.yml +1 -1
  38. data/spec/dummy/public/puffer/javascripts/application.js +10 -0
  39. data/spec/dummy/public/puffer/javascripts/rails-src.js +36 -378
  40. data/spec/dummy/public/puffer/javascripts/right-autocompleter-src.js +621 -0
  41. data/spec/dummy/public/puffer/javascripts/right-autocompleter.js +18 -0
  42. data/spec/dummy/public/puffer/javascripts/right-autocompleter.js.gz +0 -0
  43. data/spec/dummy/public/puffer/stylesheets/puffer.css +107 -6
  44. data/spec/lib/extensions/core_spec.rb +29 -0
  45. data/spec/lib/resource/routing_spec.rb +11 -11
  46. metadata +28 -18
  47. data/lib/generators/puffer/install/templates/puffer/javascripts/rails.js +0 -14
  48. data/spec/dummy/public/puffer/javascripts/rails.js +0 -14
@@ -0,0 +1,18 @@
1
+ /**
2
+ * RightJS-UI: Autocompleter
3
+ * http://rightjs.org/ui/autocompleter
4
+ *
5
+ * Copyright (C) 2010 Nikolay Nemshilov
6
+ */
7
+ var Autocompleter=RightJS.Autocompleter=function(h,c){function l(a,b,d,e){if(c.Fx)if(d===undefined){d=a.options.fxName;if(e===undefined){e={duration:a.options.fxDuration,onFinish:c(a.fire).bind(a,b)};if(b==="hide")e.duration=(c.Fx.Durations[e.duration]||e.duration)/2}}if(!c.Fx||!d)a.fire(b);return a.$super(d,e)}function m(a,b,d){var e=(this.reAnchor||(this.reAnchor=new c.Element("div",{"class":"rui-re-anchor"})).insert(this)).insertTo(a,"after").position(),f=a.dimensions(),i=parseInt(a.getStyle("borderTopWidth")),
8
+ j=parseInt(a.getStyle("borderLeftWidth")),q=parseInt(a.getStyle("borderRightWidth")),r=parseInt(a.getStyle("borderBottomWidth"));a=f.top-e.y+i;e=f.left-e.x+j;j=f.width-j-q;f=f.height-i-r;this.setStyle("visibility:hidden").show(null);if(b==="right")e+=j-this.size().x;else a+=f;this.moveTo(e,a);if(d)b==="left"||b==="right"?this.setHeight(f):this.setWidth(j);this.setStyle("visibility:visible").hide(null)}var g=c,n=c.$,s=c.$w,t=c.Xhr,o=c.RegExp,u=c.isArray,p=new c.Class(c.Element,{initialize:function(a){this.$super("div",
9
+ {"class":"rui-spinner"});this.dots=[];for(var b=0;b<(a||4);b++)this.dots.push(new c.Element("div"));this.dots[0].addClass("glowing");this.insert(this.dots);c(this.shift).bind(this).periodical(300)},shift:function(){if(this.visible()){var a=this.dots.pop();this.dots.unshift(a);this.insert(a,"top")}}}),k=new (function(a,b){if(!b){b=a;a="DIV"}var d=new c.Class(c.Element.Wrappers[a]||c.Element,{initialize:function(e,f){this.key=e;var i=[{"class":"rui-"+e}];this instanceof c.Input||this instanceof c.Form||
10
+ i.unshift(a);this.$super.apply(this,i);if(c.isString(f))f=c.$(f);if(f instanceof c.Element){this._=f._;if("$listeners"in f)f.$listeners=f.$listeners;f={}}this.setOptions(f,this);return c.Wrapper.Cache[c.$uid(this._)]=this},setOptions:function(e,f){f=f||this;c.Options.setOptions.call(this,c.Object.merge(e,eval("("+(f.get("data-"+this.key)||"{}")+")")));return this}});d=new c.Class(d,b);c.Observer.createShortcuts(d.prototype,d.EVENTS||[]);return d})("UL",{include:{show:function(a,b){this.constructor.current=
11
+ this;return l(this,"show",a,b)},hide:function(a,b){this.constructor.current=null;return l(this,"show",a,b)},showAt:function(a,b,d){this.hide(null).shownAt=a=c.$(a);m.call(this,a,b,d);return this.show()},toggleAt:function(a,b,d){return this.hidden()?this.showAt(a,b,d):this.hide()}},extend:{version:"2.2.0",EVENTS:s("show hide update load select done"),Options:{url:h.location.href,param:"search",method:"get",minLength:1,threshold:200,cache:true,local:null,fxName:"slide",fxDuration:"short",spinner:"native",
12
+ cssRule:"input[data-autocompleter]"}},initialize:function(a,b){this.input=n(a);this.$super("autocompleter",b).addClass("rui-dd-menu").onMousedown(this.clicked);this.input.autocompleter=this},destroy:function(){delete this.input.autocompleter;return this},prev:function(){return this.pick("prev")},next:function(){return this.pick("next")},done:function(a){if(a=a||this.first("li.current")){a.radioClass("current");this.input.setValue(g(a.html()).stripTags());this.fire("done")}return this.hide()},setOptions:function(a){this.$super(a,
13
+ this.input);a=this.options;g(a.url).includes("%{search}")||(a.url+=(g(a.url).includes("?")?"&":"?")+a.param+"=%{search}")},pick:function(a){var b=this.children(),d=b.first("hasClass","current"),e=b.indexOf(d);if(a=="prev")d=e<1?b.last():b[e<0?0:e-1];else if(a=="next")d=e<0||e==b.length-1?b.first():b[e+1];return this.fire("select",{item:d.radioClass("current")})},clicked:function(a){this.done(a.stop().find("li"))},keypressed:function(){if(this.input.value().length>=this.options.minLength){this.timeout&&
14
+ this.timeout.cancel();this.timeout=g(this.trigger).bind(this).delay(this.options.threshold)}else return this.hide()},trigger:function(){this.timeout=null;this.cache=this.cache||{};var a=this.input.value(),b=this.options;if(a.length<b.minLength)return this.hide();if(this.cache[a])this.suggest(this.cache[a],a);else if(u(b.local))this.suggest(this.findLocal(a),a);else this.request=t.load(b.url.replace("%{search}",encodeURIComponent(a)),{method:b.method,spinner:this.getSpinner(),onComplete:g(function(d){this.fire("load").suggest(d.text,
15
+ a)}).bind(this)})},suggest:function(a,b){if(this.options.cache)this.cache[b]=a;if(g(a).blank())this.hide();else{this.update(a.replace(/<ul[^>]*>|<\/ul>/im,""));this.fire("update");if(!this._connected||this.hidden()){this.showAt(this.input,"bottom","resize");this._connected=true}}return this},findLocal:function(a){var b=new o("("+o.escape(a)+")","ig");return g(this.options.local).map(function(d){if(d.match(b))return"<li>"+d.replace(b,"<strong>$1</strong>")+"</li>"}).compact().join("")},getSpinner:function(){var a=
16
+ this.options,b=a.spinner;if(b=="native"){b=a.spinner=(new p(3)).insertTo(this);b.addClass("rui-autocompleter-spinner")}b instanceof p&&m.call(b,this.input,"right","resize");return b}});n(h).on({focus:function(a){if((a=a.target)&&a instanceof c.Element&&(a.autocompleter||a.match(k.Options.cssRule)))a.autocompleter||new k(a)},blur:function(a){(a=a.target?a.target.autocompleter:null)&&a.visible()&&a.hide()},keydown:function(a){var b=a.target?a.target.autocompleter:null;if(b&&b.visible()){var d={27:"hide",
17
+ 38:"prev",40:"next",13:"done"}[a.keyCode];if(d){a.stop();b[d]()}}},keyup:function(a){var b=a.target?a.target.autocompleter:null;b&&!g([9,27,37,38,39,40,13]).include(a.keyCode)&&b.keypressed(a)}});(function(){var a=h.createElement("style"),b=h.createTextNode(" *.rui-dd-menu, *.rui-dd-menu li{margin:0;padding:0;border:none;background:none;list-style:none;font-weight:normal;float:none} *.rui-dd-menu{display:none;position:absolute;z-index:9999;background:white;border:1px solid #BBB;border-radius:.2em;-moz-border-radius:.2em;-webkit-border-radius:.2em;box-shadow:#DDD .2em .2em .4em;-moz-box-shadow:#DDD .2em .2em .4em;-webkit-box-shadow:#DDD .2em .2em .4em} *.rui-dd-menu li{padding:.2em .4em;border-top:none;border-bottom:none;cursor:pointer} *.rui-dd-menu li.current{background:#DDD} *.rui-dd-menu li:hover{background:#EEE}dl.rui-dd-menu dt{padding:.3em .5em;cursor:default;font-weight:bold;font-style:italic;color:#444;background:#EEE}dl.rui-dd-menu dd li{padding-left:1.5em}div.rui-spinner,div.rui-spinner div{margin:0;padding:0;border:none;background:none;list-style:none;font-weight:normal;float:none;display:inline-block; *display:inline; *zoom:1;border-radius:.12em;-moz-border-radius:.12em;-webkit-border-radius:.12em}div.rui-spinner{text-align:center;white-space:nowrap;background:#EEE;border:1px solid #DDD;height:1.2em;padding:0 .2em}div.rui-spinner div{width:.4em;height:70%;background:#BBB;margin-left:1px}div.rui-spinner div:first-child{margin-left:0}div.rui-spinner div.glowing{background:#777}div.rui-re-anchor{margin:0;padding:0;background:none;border:none;float:none;display:inline;position:absolute;z-index:9999}.rui-autocompleter{border-top-color:#DDD !important;border-top-left-radius:0 !important;border-top-right-radius:0 !important;-moz-border-radius-topleft:0 !important;-moz-border-radius-topright:0 !important;-webkit-border-top-left-radius:0 !important;-webkit-border-top-right-radius:0 !important}.rui-autocompleter-spinner{border:none !important;background:none !important;position:absolute;z-index:9999}.rui-autocompleter-spinner div{margin-top:.2em !important; *margin-top:0.1em !important}");
18
+ a.type="text/css";if(a.styleSheet)a.styleSheet.cssText=b.nodeValue;else a.appendChild(b);h.getElementsByTagName("head")[0].appendChild(a)})();return k}(document,RightJS);
@@ -88,7 +88,7 @@ h1
88
88
  *display: inline;
89
89
  zoom: 1;
90
90
  color: #ddd;
91
- padding: 3px 12px;
91
+ padding: 7px 12px;
92
92
  font-size: 11pt;
93
93
  text-decoration: none;
94
94
  text-shadow: #304759 -1px -1px 0;
@@ -120,6 +120,16 @@ h1
120
120
  border-bottom-right-radius: 3px;
121
121
  }
122
122
 
123
+ .sidebar .navigation li .additional dt
124
+ {
125
+ margin-bottom: 2px;
126
+ }
127
+
128
+ .sidebar .navigation li .additional dd
129
+ {
130
+ margin-bottom: 8px;
131
+ }
132
+
123
133
  .columns
124
134
  {
125
135
  width: 100%;
@@ -170,7 +180,7 @@ h1
170
180
  border-top-left-radius: 5px;
171
181
  border-bottom-left-radius: 5px;
172
182
  background: #fff;
173
- padding: 15px;
183
+ padding: 30px;
174
184
  }
175
185
 
176
186
  .list_table
@@ -186,6 +196,11 @@ h1
186
196
  -webkit-border-radius: 5px;
187
197
  }
188
198
 
199
+ .list_table *
200
+ {
201
+ font-size: 8pt;
202
+ }
203
+
189
204
  .list_table tr
190
205
  {
191
206
  vertical-align: top;
@@ -258,6 +273,40 @@ h1
258
273
  background: #f4f4f4;
259
274
  }
260
275
 
276
+ label, input, textarea
277
+ {
278
+ font-family: Helvetica, Arial, sans-serif;
279
+ }
280
+
281
+ input[type=text], input[type=password], textarea
282
+ {
283
+ border: #ccc solid 1px;
284
+ font-size: 11pt;
285
+ padding: 3px 0;
286
+ border-radius: 3px;
287
+ }
288
+
289
+ input[type=text]:focus, input[type=password]:focus, textarea:focus
290
+ {
291
+ border: #536C80 solid 1px;
292
+ }
293
+
294
+ input[type=submit]
295
+ {
296
+ border: 0;
297
+ border-radius: 3px;
298
+ background: #536C80;
299
+ color: #ddd;
300
+ text-shadow: -1px -1px 0 #304759;
301
+ padding: 3px 10px;
302
+ font-size: 11pt;
303
+ }
304
+
305
+ #search
306
+ {
307
+ width: 120px;
308
+ }
309
+
261
310
  .form
262
311
  {
263
312
  margin-bottom: 30px;
@@ -269,21 +318,73 @@ h1
269
318
  margin-bottom: 8px;
270
319
  }
271
320
 
321
+ .form li .label
322
+ {
323
+ margin-bottom: 3px;
324
+ }
325
+
326
+ .form li .label *
327
+ {
328
+ display: inline;
329
+ }
330
+
272
331
  .form li label
273
332
  {
274
- display: block;
275
- font-size: 11pt;
333
+ font-size: 10pt;
276
334
  font-weight: bold;
277
335
  }
278
336
 
279
337
  .form li input[type=text], .form li input[type=password], .form li textarea
280
338
  {
281
- border: #ccc solid 1px;
282
339
  width: 100%;
283
- font-size: 12pt;
284
340
  }
285
341
 
286
342
  .form li textarea
287
343
  {
288
344
  height: 60px;
289
345
  }
346
+
347
+ .field_error
348
+ {
349
+ color: #C83E3E;
350
+ }
351
+
352
+ .association
353
+ {
354
+ position: relative;
355
+ }
356
+
357
+ .association input[disabled]
358
+ {
359
+ text-indent: 25px;
360
+ }
361
+
362
+ .association .association_clear
363
+ {
364
+ position: absolute;
365
+ top: 4px;
366
+ left: 9px;
367
+ cursor: pointer;
368
+ display: none;
369
+ }
370
+
371
+ .association .association_clear:hover
372
+ {
373
+ color: #b00;
374
+ text-shadow: 1px 1px 2px #555;
375
+ }
376
+
377
+ .association input[disabled] ~ .association_clear
378
+ {
379
+ display: block;
380
+ }
381
+
382
+ .association .rui-autocompleter li .description
383
+ {
384
+ display: none;
385
+ }
386
+
387
+ .association .rui-autocompleter li:hover .description
388
+ {
389
+ display: block;
390
+ }
data/lib/puffer/base.rb CHANGED
@@ -6,6 +6,7 @@ module Puffer
6
6
  include Puffer::Controller::Helpers
7
7
  include Puffer::Controller::Dsl
8
8
  include Puffer::Controller::Config
9
+ include Puffer::Controller::Generated
9
10
 
10
11
  respond_to :html, :js
11
12
 
@@ -28,19 +29,19 @@ module Puffer
28
29
  def create
29
30
  @record = resource.new_member
30
31
  @record.save
31
- respond_with @record, :location => resource.index_path
32
+ respond_with @record, :location => resource.collection_path
32
33
  end
33
34
 
34
35
  def update
35
36
  @record = resource.member
36
37
  @record.update_attributes resource.attributes
37
- respond_with @record, :location => resource.index_path
38
+ respond_with @record, :location => resource.collection_path
38
39
  end
39
40
 
40
41
  def destroy
41
42
  @record = resource.member
42
43
  @record.destroy
43
- redirect_to (request.referrer || resource.index_path)
44
+ redirect_to (request.referrer || resource.collection_path)
44
45
  end
45
46
 
46
47
  end
@@ -1,79 +1,13 @@
1
1
  module Puffer
2
2
  module Controller
3
- module Actions
3
+ class Actions < Array
4
4
 
5
- def self.included base
6
- base.class_eval do
7
- extend ClassMethods
5
+ %w(match get post put delete).each do |method|
6
+ define_method method do |*args|
7
+ push args.unshift(method)
8
8
  end
9
9
  end
10
10
 
11
- module ClassMethods
12
-
13
- def generate_association_actions field
14
- field.collection? ? generate_collection_association_actions(field) : generate_single_association_actions(field)
15
- end
16
-
17
- def generate_single_association_actions field
18
- define_method "associated_#{field}_choosing" do
19
- @record = model.find params[:id]
20
- @records = field.association.klass.scoped(:conditions => search_query(field.association_fields)).paginate(:page => params[:page], :include => includes(field.association_fields))
21
- @field = field
22
- render 'puffer/associated/one'
23
- end
24
- end
25
-
26
- def generate_collection_association_actions field
27
- define_method "associated_#{field}" do
28
- @record = model.find params[:id]
29
- @records = field.association.klass.scoped(:conditions => {:id => params[:ids]}).scoped(:conditions => search_query(field.association_fields)).paginate(:page => params[:page], :include => includes(field.association_fields))
30
- @field = field
31
- render 'puffer/associated/many'
32
- end
33
-
34
- define_method "associated_#{field}_choosing" do
35
- @record = model.find params[:id]
36
- @records = field.association.klass.scoped(:conditions => search_query(field.association_fields)).paginate(:page => params[:page], :include => includes(field.association_fields))
37
- @choosen = field.association.klass.scoped(:conditions => {:id => params[:ids]}).scoped(:conditions => search_query(field.association_fields)).paginate(:page => params[:page], :include => includes(field.association_fields))
38
- @field = field
39
- render 'puffer/associated/many'
40
- end
41
- end
42
-
43
- def generate_change_actions field
44
- define_method "toggle_#{field}" do
45
- @record = model.find params[:id]
46
- @field = field
47
- @record.toggle! field.name.to_sym
48
- render 'puffer/toggle'
49
- end
50
- end
51
-
52
- private
53
-
54
- def route_member_actions
55
- unless @route_member_actions
56
- @route_member_actions = {}
57
- actions.each do |action|
58
- @route_member_actions.merge!(action => :get)
59
- end
60
- [:form_fields, :update_fields, :create_fields].each do |fields|
61
- fields = send fields
62
- fields.each do |field|
63
- if field.association?
64
- field.collection? ? @route_member_actions.merge!("associated_#{field}" => :get, "associated_#{field}_choosing" => :get) : @route_member_actions.merge!("associated_#{field}_choosing" => :get)
65
- end
66
- end if fields
67
- end
68
- index_fields.each do |field|
69
- @route_member_actions.merge!("toggle_#{field}" => :post) if field.toggable?
70
- end if index_fields
71
- end
72
- @route_member_actions
73
- end
74
-
75
- end
76
-
77
11
  end
78
12
  end
79
13
  end
@@ -10,26 +10,44 @@ module Puffer
10
10
 
11
11
  %w(index show form create update).each do |action|
12
12
  class_attribute "_#{action}_fields"
13
- send "_#{action}_fields=", Puffer::Fields.new unless send("_#{action}_fields").present?
13
+ send "_#{action}_fields=", Puffer::Fields.new unless send("_#{action}_fields?")
14
14
  helper_method "#{action}_fields"
15
15
  end
16
+
17
+ class_attribute :_members
18
+ self._members = Puffer::Controller::Actions.new
19
+ class_attribute :_collections
20
+ self._collections = Puffer::Controller::Actions.new
16
21
  end
17
22
  end
18
23
 
19
24
  module ClassMethods
20
25
 
26
+ def inherited klass
27
+ klass._members = _members.dup
28
+ klass._collections = _collections.dup
29
+ super
30
+ end
31
+
32
+ def member &block
33
+ block.bind(_members).call if block_given?
34
+ end
35
+
36
+ def collection &block
37
+ block.bind(_collections).call if block_given?
38
+ end
39
+
21
40
  %w(index show form create update).each do |action|
22
41
  define_method action do |&block|
23
- @_fields = send "_#{action}_fields"
24
- @_fields.clear
42
+ @_fields = send("_#{action}_fields=", Puffer::Fields.new)
25
43
  block.call if block
26
- @_fields = nil
44
+ remove_instance_variable :@_fields
27
45
  end
28
46
  end
29
47
 
30
48
  def field name, options = {}
31
49
  field = @_fields.field(model, name, options) if @_fields
32
- #generate_association_actions field if field.association?
50
+ generate_association_actions field if field.reflection
33
51
  #generate_change_actions field if field.toggable?
34
52
  end
35
53
 
@@ -0,0 +1,65 @@
1
+ module Puffer
2
+ module Controller
3
+ module Generated
4
+
5
+ def self.included base
6
+ base.class_eval do
7
+ extend ClassMethods
8
+ end
9
+ end
10
+
11
+ module ClassMethods
12
+
13
+ def generate_association_actions field
14
+ field.collection? ? generate_collection_association_actions(field) : generate_single_association_actions(field)
15
+ end
16
+
17
+ def generate_single_association_actions field
18
+ define_method "associated_#{field}_choosing" do
19
+ @field = field
20
+ @record = resource.member if params[:id]
21
+ @records = field.reflection.klass.includes(field.association_fields.includes).joins(field.association_fields.includes).where(field.association_fields.searches(params[:search])).limit(100).all
22
+ render 'puffer/associated/one'
23
+ end
24
+
25
+ collection do
26
+ get "associated_#{field}_choosing"
27
+ end
28
+ end
29
+
30
+ def generate_collection_association_actions field
31
+ define_method "associated_#{field}" do
32
+ @record = resource.member
33
+ @records = field.association.klass.scoped(:conditions => {:id => params[:ids]}).scoped(:conditions => search_query(field.association_fields)).paginate(:page => params[:page], :include => includes(field.association_fields))
34
+ @field = field
35
+ render 'puffer/associated/many'
36
+ end
37
+
38
+ define_method "associated_#{field}_choosing" do
39
+ @record = resource.member
40
+ @records = field.association.klass.scoped(:conditions => search_query(field.association_fields)).paginate(:page => params[:page], :include => includes(field.association_fields))
41
+ @choosen = field.association.klass.scoped(:conditions => {:id => params[:ids]}).scoped(:conditions => search_query(field.association_fields)).paginate(:page => params[:page], :include => includes(field.association_fields))
42
+ @field = field
43
+ render 'puffer/associated/many'
44
+ end
45
+
46
+ collection do
47
+ get "associated_#{field}"
48
+ get "associated_#{field}_choosing"
49
+ end
50
+ end
51
+
52
+ def generate_change_actions field
53
+ define_method "toggle_#{field}" do
54
+ @record = model.find params[:id]
55
+ @field = field
56
+ @record.toggle! field.name.to_sym
57
+ render 'puffer/toggle'
58
+ end
59
+ end
60
+
61
+ end
62
+
63
+ end
64
+ end
65
+ end
@@ -8,11 +8,11 @@ module Puffer
8
8
  end
9
9
 
10
10
  def to_title
11
- send title_column
11
+ send title_method
12
12
  end
13
13
 
14
- def title_column
15
- self.class.column_names[1].to_sym
14
+ def title_method
15
+ self.class.column_names.detect {|c| c =~ /name|title/} || self.class.column_names[1].to_sym
16
16
  end
17
17
 
18
18
  end
@@ -21,11 +21,25 @@ module Puffer
21
21
  end
22
22
  end
23
23
 
24
+ module Array
25
+ def to_includes
26
+ map do |field|
27
+ sections = field.split('.').map(&:to_sym)
28
+ hash = sections.pop
29
+ sections.reverse_each do |section|
30
+ hash = {section => hash}
31
+ end
32
+ hash
33
+ end
34
+ end
35
+ end
36
+
24
37
  end
25
38
  end
26
39
 
27
40
  String.send :include, Puffer::Extensions::String
28
41
  Symbol.send :include, Puffer::Extensions::Symbol
42
+ Array.send :include, Puffer::Extensions::Array
29
43
 
30
44
  Kernel.class_eval do
31
45
  def swallow_nil