puffer 0.0.7 → 0.0.8

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.
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