puffer 0.0.3 → 0.0.4

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 (32) hide show
  1. data/Gemfile +1 -4
  2. data/Gemfile.lock +1 -1
  3. data/VERSION +1 -1
  4. data/app/views/puffer/index.html.erb +7 -7
  5. data/app/views/puffer/show.html.erb +3 -3
  6. data/lib/generators/puffer/install/install_generator.rb +9 -0
  7. data/lib/generators/puffer/install/templates/puffer/javascripts/application.js +2 -0
  8. data/lib/generators/puffer/install/templates/puffer/javascripts/controls.js +965 -0
  9. data/lib/generators/puffer/install/templates/puffer/javascripts/dragdrop.js +974 -0
  10. data/lib/generators/puffer/install/templates/puffer/javascripts/effects.js +1123 -0
  11. data/lib/generators/puffer/install/templates/puffer/javascripts/prototype.js +6001 -0
  12. data/lib/generators/puffer/install/templates/puffer/javascripts/rails.js +175 -0
  13. data/lib/generators/puffer/install/templates/puffer.rb +0 -0
  14. data/lib/puffer/base.rb +1 -1
  15. data/lib/puffer/controller/dsl.rb +9 -10
  16. data/lib/puffer/controller/helpers.rb +1 -9
  17. data/lib/puffer/controller/mutate.rb +0 -4
  18. data/lib/puffer/fields/field.rb +58 -0
  19. data/lib/puffer/fields.rb +17 -0
  20. data/lib/puffer/resource/scoping.rb +6 -0
  21. data/lib/puffer/resource.rb +5 -5
  22. data/puffer.gemspec +17 -8
  23. data/spec/dummy/app/controllers/admin/categories_controller.rb +10 -0
  24. data/spec/dummy/app/controllers/admin/posts_controller.rb +12 -0
  25. data/spec/dummy/app/controllers/admin/profiles_controller.rb +14 -0
  26. data/spec/dummy/app/controllers/admin/tags_controller.rb +8 -0
  27. data/spec/lib/fields_spec.rb +54 -0
  28. data/spec/lib/params_spec.rb +1 -1
  29. data/spec/lib/resource_spec.rb +37 -3
  30. data/spec/spec_helper.rb +2 -1
  31. metadata +30 -19
  32. data/lib/puffer/field.rb +0 -88
@@ -0,0 +1,175 @@
1
+ (function() {
2
+ // Technique from Juriy Zaytsev
3
+ // http://thinkweb2.com/projects/prototype/detecting-event-support-without-browser-sniffing/
4
+ function isEventSupported(eventName) {
5
+ var el = document.createElement('div');
6
+ eventName = 'on' + eventName;
7
+ var isSupported = (eventName in el);
8
+ if (!isSupported) {
9
+ el.setAttribute(eventName, 'return;');
10
+ isSupported = typeof el[eventName] == 'function';
11
+ }
12
+ el = null;
13
+ return isSupported;
14
+ }
15
+
16
+ function isForm(element) {
17
+ return Object.isElement(element) && element.nodeName.toUpperCase() == 'FORM'
18
+ }
19
+
20
+ function isInput(element) {
21
+ if (Object.isElement(element)) {
22
+ var name = element.nodeName.toUpperCase()
23
+ return name == 'INPUT' || name == 'SELECT' || name == 'TEXTAREA'
24
+ }
25
+ else return false
26
+ }
27
+
28
+ var submitBubbles = isEventSupported('submit'),
29
+ changeBubbles = isEventSupported('change')
30
+
31
+ if (!submitBubbles || !changeBubbles) {
32
+ // augment the Event.Handler class to observe custom events when needed
33
+ Event.Handler.prototype.initialize = Event.Handler.prototype.initialize.wrap(
34
+ function(init, element, eventName, selector, callback) {
35
+ init(element, eventName, selector, callback)
36
+ // is the handler being attached to an element that doesn't support this event?
37
+ if ( (!submitBubbles && this.eventName == 'submit' && !isForm(this.element)) ||
38
+ (!changeBubbles && this.eventName == 'change' && !isInput(this.element)) ) {
39
+ // "submit" => "emulated:submit"
40
+ this.eventName = 'emulated:' + this.eventName
41
+ }
42
+ }
43
+ )
44
+ }
45
+
46
+ if (!submitBubbles) {
47
+ // discover forms on the page by observing focus events which always bubble
48
+ document.on('focusin', 'form', function(focusEvent, form) {
49
+ // special handler for the real "submit" event (one-time operation)
50
+ if (!form.retrieve('emulated:submit')) {
51
+ form.on('submit', function(submitEvent) {
52
+ var emulated = form.fire('emulated:submit', submitEvent, true)
53
+ // if custom event received preventDefault, cancel the real one too
54
+ if (emulated.returnValue === false) submitEvent.preventDefault()
55
+ })
56
+ form.store('emulated:submit', true)
57
+ }
58
+ })
59
+ }
60
+
61
+ if (!changeBubbles) {
62
+ // discover form inputs on the page
63
+ document.on('focusin', 'input, select, texarea', function(focusEvent, input) {
64
+ // special handler for real "change" events
65
+ if (!input.retrieve('emulated:change')) {
66
+ input.on('change', function(changeEvent) {
67
+ input.fire('emulated:change', changeEvent, true)
68
+ })
69
+ input.store('emulated:change', true)
70
+ }
71
+ })
72
+ }
73
+
74
+ function handleRemote(element) {
75
+ var method, url, params;
76
+
77
+ var event = element.fire("ajax:before");
78
+ if (event.stopped) return false;
79
+
80
+ if (element.tagName.toLowerCase() === 'form') {
81
+ method = element.readAttribute('method') || 'post';
82
+ url = element.readAttribute('action');
83
+ params = element.serialize();
84
+ } else {
85
+ method = element.readAttribute('data-method') || 'get';
86
+ url = element.readAttribute('href');
87
+ params = {};
88
+ }
89
+
90
+ new Ajax.Request(url, {
91
+ method: method,
92
+ parameters: params,
93
+ evalScripts: true,
94
+
95
+ onComplete: function(request) { element.fire("ajax:complete", request); },
96
+ onSuccess: function(request) { element.fire("ajax:success", request); },
97
+ onFailure: function(request) { element.fire("ajax:failure", request); }
98
+ });
99
+
100
+ element.fire("ajax:after");
101
+ }
102
+
103
+ function handleMethod(element) {
104
+ var method = element.readAttribute('data-method'),
105
+ url = element.readAttribute('href'),
106
+ csrf_param = $$('meta[name=csrf-param]')[0],
107
+ csrf_token = $$('meta[name=csrf-token]')[0];
108
+
109
+ var form = new Element('form', { method: "POST", action: url, style: "display: none;" });
110
+ element.parentNode.insert(form);
111
+
112
+ if (method !== 'post') {
113
+ var field = new Element('input', { type: 'hidden', name: '_method', value: method });
114
+ form.insert(field);
115
+ }
116
+
117
+ if (csrf_param) {
118
+ var param = csrf_param.readAttribute('content'),
119
+ token = csrf_token.readAttribute('content'),
120
+ field = new Element('input', { type: 'hidden', name: param, value: token });
121
+ form.insert(field);
122
+ }
123
+
124
+ form.submit();
125
+ }
126
+
127
+
128
+ document.on("click", "*[data-confirm]", function(event, element) {
129
+ var message = element.readAttribute('data-confirm');
130
+ if (!confirm(message)) event.stop();
131
+ });
132
+
133
+ document.on("click", "a[data-remote]", function(event, element) {
134
+ if (event.stopped) return;
135
+ handleRemote(element);
136
+ event.stop();
137
+ });
138
+
139
+ document.on("click", "a[data-method]", function(event, element) {
140
+ if (event.stopped) return;
141
+ handleMethod(element);
142
+ event.stop();
143
+ });
144
+
145
+ document.on("submit", function(event) {
146
+ var element = event.findElement(),
147
+ message = element.readAttribute('data-confirm');
148
+ if (message && !confirm(message)) {
149
+ event.stop();
150
+ return false;
151
+ }
152
+
153
+ var inputs = element.select("input[type=submit][data-disable-with]");
154
+ inputs.each(function(input) {
155
+ input.disabled = true;
156
+ input.writeAttribute('data-original-value', input.value);
157
+ input.value = input.readAttribute('data-disable-with');
158
+ });
159
+
160
+ var element = event.findElement("form[data-remote]");
161
+ if (element) {
162
+ handleRemote(element);
163
+ event.stop();
164
+ }
165
+ });
166
+
167
+ document.on("ajax:after", "form", function(event, element) {
168
+ var inputs = element.select("input[type=submit][disabled=true][data-disable-with]");
169
+ inputs.each(function(input) {
170
+ input.value = input.readAttribute('data-original-value');
171
+ input.removeAttribute('data-original-value');
172
+ input.disabled = false;
173
+ });
174
+ });
175
+ })();
File without changes
data/lib/puffer/base.rb CHANGED
@@ -2,7 +2,7 @@ module Puffer
2
2
  class Base < ApplicationController
3
3
  unloadable
4
4
 
5
- respond_to :html
5
+ respond_to :html, :js
6
6
 
7
7
  include Puffer::Controller::Mutate
8
8
  include Puffer::Controller::Dsl
@@ -33,31 +33,30 @@ module Puffer
33
33
  end
34
34
 
35
35
  def index_fields
36
- puffer_fields[:index] || []
36
+ puffer_fields[:index] || Puffer::Fields.new
37
37
  end
38
38
 
39
39
  def show_fields
40
- puffer_fields[:show] || puffer_fields[:index] || []
40
+ puffer_fields[:show] || puffer_fields[:index] || Puffer::Fields.new
41
41
  end
42
42
 
43
43
  def form_fields
44
- puffer_fields[:form] || []
44
+ puffer_fields[:form] || Puffer::Fields.new
45
45
  end
46
46
 
47
47
  def create_fields
48
- puffer_fields[:create] || puffer_fields[:form] || []
48
+ puffer_fields[:create] || puffer_fields[:form] || Puffer::Fields.new
49
49
  end
50
50
 
51
51
  def update_fields
52
- puffer_fields[:update] || puffer_fields[:form] || []
52
+ puffer_fields[:update] || puffer_fields[:form] || Puffer::Fields.new
53
53
  end
54
54
 
55
55
  def field name, options = {}
56
- puffer_fields[@puffer_option] ||= []
57
- field = ::Puffer::Field.new(current_resource.model, name, options)
58
- generate_association_actions field if field.association?
59
- generate_change_actions field if field.toggable?
60
- puffer_fields[@puffer_option] << field
56
+ puffer_fields[@puffer_option] ||= Puffer::Fields.new
57
+ field = puffer_fields[@puffer_option].field(current_resource.model, name, options)
58
+ #generate_association_actions field if field.association?
59
+ #generate_change_actions field if field.toggable?
61
60
  end
62
61
 
63
62
  end
@@ -4,7 +4,7 @@ module Puffer
4
4
 
5
5
  def self.included base
6
6
  base.class_eval do
7
- helper_method :resource_session, :searchable_fields, :boolean_fields, :puffer_navigation
7
+ helper_method :resource_session, :puffer_navigation
8
8
  end
9
9
  end
10
10
 
@@ -21,14 +21,6 @@ module Puffer
21
21
  session[:resources][name]
22
22
  end
23
23
 
24
- def searchable_fields fields
25
- @searchable_fields ||= fields.map { |f| f if [:text, :string, :integer, :decimal, :float].include? f.type }.compact
26
- end
27
-
28
- def boolean_fields
29
- @boolean_fields ||= index_fields.map { |f| f if ['boolean'].include? f.type.to_s }.compact
30
- end
31
-
32
24
  end
33
25
  end
34
26
  end
@@ -37,10 +37,6 @@ module Puffer
37
37
  true
38
38
  end
39
39
 
40
- def configure &block
41
- block.bind(current_config).call
42
- end
43
-
44
40
  end
45
41
 
46
42
  end
@@ -0,0 +1,58 @@
1
+ module Puffer
2
+ class Fields
3
+ class Field
4
+
5
+ attr_accessor :resource, :field, :options
6
+
7
+ def initialize resource, field, options = {}
8
+ @resource = resource
9
+ @field = field.to_s
10
+ @options = options
11
+ end
12
+
13
+ def native?
14
+ model == resource
15
+ end
16
+
17
+ def name
18
+ field.split('.').last
19
+ end
20
+
21
+ def label
22
+ @label ||= options[:label] || model.human_attribute_name(name)
23
+ end
24
+
25
+ def order
26
+ @order ||= options[:order] || query_column
27
+ end
28
+
29
+ def type
30
+ @type ||= options[:type] ? options[:type].to_sym : (column ? column.type : :string)
31
+ end
32
+
33
+ def to_s
34
+ field
35
+ end
36
+
37
+ def model
38
+ unless @model
39
+ @model = resource
40
+ associations = field.split('.')
41
+ while @model.reflect_on_association(association = associations.shift.to_sym) do
42
+ @model = @model.reflect_on_association(association).klass
43
+ end
44
+ end
45
+ @model
46
+ end
47
+
48
+ def column
49
+ @column ||= model.columns_hash[name]
50
+ end
51
+
52
+ def query_column
53
+ "#{model.table_name}.#{name}" if column
54
+ end
55
+
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,17 @@
1
+ module Puffer
2
+ class Fields < Array
3
+
4
+ def field *args
5
+ push Field.new(*args)
6
+ end
7
+
8
+ def searchable
9
+ @searchable ||= reject { |f| ![:text, :string, :integer, :decimal, :float].include? f.type }
10
+ end
11
+
12
+ def boolean
13
+ @boolean ||= reject { |f| f.type != :boolean }
14
+ end
15
+
16
+ end
17
+ end
@@ -2,7 +2,13 @@ module Puffer
2
2
  class Resource
3
3
  module Scoping
4
4
 
5
+ def includes
5
6
 
7
+ end
8
+
9
+ def order
10
+
11
+ end
6
12
 
7
13
  end
8
14
  end
@@ -29,8 +29,8 @@ module Puffer
29
29
  params[:plural]
30
30
  end
31
31
 
32
- def human_name
33
- model_name.humanize
32
+ def human
33
+ model.model_name.human
34
34
  end
35
35
 
36
36
  def parent
@@ -92,7 +92,7 @@ module Puffer
92
92
 
93
93
  def collection
94
94
  scope = parent ? parent.member.send(model_name.pluralize) : model
95
- scope.paginate :page => params[:page]
95
+ scope.includes(includes).joins(includes).order(order).paginate :page => params[:page]
96
96
  end
97
97
 
98
98
  def member
@@ -112,7 +112,7 @@ module Puffer
112
112
  if plural?
113
113
  parent.member.send(model_name.pluralize).new attributes
114
114
  else
115
- parent.member.send("build_#{model_name}")
115
+ parent.member.send("build_#{model_name}", attributes)
116
116
  end
117
117
  else
118
118
  model.new attributes
@@ -131,7 +131,7 @@ module Puffer
131
131
  method = method.to_s
132
132
  if method.match(/path$/)
133
133
  options = args.extract_options!
134
- return send method.gsub(/path$/, 'url'), *(args << options.merge(:routing_type => :path))
134
+ return send method.gsub(/path$/, 'url'), *(args << options.merge(:routing_type => :path)) if defined? method.gsub(/path$/, 'url')
135
135
  end
136
136
  model.send method, *args, &block
137
137
  end
data/puffer.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{puffer}
8
- s.version = "0.0.3"
8
+ s.version = "0.0.4"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["pyromaniac"]
12
- s.date = %q{2010-12-30}
12
+ s.date = %q{2011-01-03}
13
13
  s.description = %q{In Soviet Russia puffer admins you}
14
14
  s.email = %q{kinwizard@gmail.com}
15
15
  s.extra_rdoc_files = [
@@ -43,6 +43,13 @@ Gem::Specification.new do |s|
43
43
  "lib/generators/puffer/controller/templates/controller.rb",
44
44
  "lib/generators/puffer/install/USAGE",
45
45
  "lib/generators/puffer/install/install_generator.rb",
46
+ "lib/generators/puffer/install/templates/puffer.rb",
47
+ "lib/generators/puffer/install/templates/puffer/javascripts/application.js",
48
+ "lib/generators/puffer/install/templates/puffer/javascripts/controls.js",
49
+ "lib/generators/puffer/install/templates/puffer/javascripts/dragdrop.js",
50
+ "lib/generators/puffer/install/templates/puffer/javascripts/effects.js",
51
+ "lib/generators/puffer/install/templates/puffer/javascripts/prototype.js",
52
+ "lib/generators/puffer/install/templates/puffer/javascripts/rails.js",
46
53
  "lib/puffer.rb",
47
54
  "lib/puffer/base.rb",
48
55
  "lib/puffer/controller/actions.rb",
@@ -54,7 +61,8 @@ Gem::Specification.new do |s|
54
61
  "lib/puffer/extensions/controller.rb",
55
62
  "lib/puffer/extensions/core.rb",
56
63
  "lib/puffer/extensions/mapper.rb",
57
- "lib/puffer/field.rb",
64
+ "lib/puffer/fields.rb",
65
+ "lib/puffer/fields/field.rb",
58
66
  "lib/puffer/railtie.rb",
59
67
  "lib/puffer/resource.rb",
60
68
  "lib/puffer/resource/routing.rb",
@@ -117,6 +125,7 @@ Gem::Specification.new do |s|
117
125
  "spec/fabricators/tags_fabricator.rb",
118
126
  "spec/fabricators/users_fabricator.rb",
119
127
  "spec/integration/navigation_spec.rb",
128
+ "spec/lib/fields_spec.rb",
120
129
  "spec/lib/params_spec.rb",
121
130
  "spec/lib/render_fallback_spec.rb",
122
131
  "spec/lib/resource/routing_spec.rb",
@@ -126,7 +135,7 @@ Gem::Specification.new do |s|
126
135
  ]
127
136
  s.homepage = %q{http://github.com/puffer/puffer}
128
137
  s.require_paths = ["lib"]
129
- s.rubygems_version = %q{1.3.7}
138
+ s.rubygems_version = %q{1.4.1}
130
139
  s.summary = %q{Admin interface builder}
131
140
  s.test_files = [
132
141
  "spec/dummy/app/controllers/admin/categories_controller.rb",
@@ -169,6 +178,7 @@ Gem::Specification.new do |s|
169
178
  "spec/fabricators/tags_fabricator.rb",
170
179
  "spec/fabricators/users_fabricator.rb",
171
180
  "spec/integration/navigation_spec.rb",
181
+ "spec/lib/fields_spec.rb",
172
182
  "spec/lib/params_spec.rb",
173
183
  "spec/lib/render_fallback_spec.rb",
174
184
  "spec/lib/resource/routing_spec.rb",
@@ -178,12 +188,11 @@ Gem::Specification.new do |s|
178
188
  ]
179
189
 
180
190
  if s.respond_to? :specification_version then
181
- current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
182
191
  s.specification_version = 3
183
192
 
184
193
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
185
194
  s.add_runtime_dependency(%q<rails>, ["~> 3.0.3"])
186
- s.add_runtime_dependency(%q<will_paginate>, ["~> 3.0.beta"])
195
+ s.add_runtime_dependency(%q<will_paginate>, ["~> 3.0.pre2"])
187
196
  s.add_development_dependency(%q<capybara>, [">= 0.4.0"])
188
197
  s.add_development_dependency(%q<sqlite3-ruby>, [">= 0"])
189
198
  s.add_development_dependency(%q<rspec-rails>, [">= 0"])
@@ -193,7 +202,7 @@ Gem::Specification.new do |s|
193
202
  s.add_development_dependency(%q<jeweler>, [">= 0"])
194
203
  else
195
204
  s.add_dependency(%q<rails>, ["~> 3.0.3"])
196
- s.add_dependency(%q<will_paginate>, ["~> 3.0.beta"])
205
+ s.add_dependency(%q<will_paginate>, ["~> 3.0.pre2"])
197
206
  s.add_dependency(%q<capybara>, [">= 0.4.0"])
198
207
  s.add_dependency(%q<sqlite3-ruby>, [">= 0"])
199
208
  s.add_dependency(%q<rspec-rails>, [">= 0"])
@@ -204,7 +213,7 @@ Gem::Specification.new do |s|
204
213
  end
205
214
  else
206
215
  s.add_dependency(%q<rails>, ["~> 3.0.3"])
207
- s.add_dependency(%q<will_paginate>, ["~> 3.0.beta"])
216
+ s.add_dependency(%q<will_paginate>, ["~> 3.0.pre2"])
208
217
  s.add_dependency(%q<capybara>, [">= 0.4.0"])
209
218
  s.add_dependency(%q<sqlite3-ruby>, [">= 0"])
210
219
  s.add_dependency(%q<rspec-rails>, [">= 0"])
@@ -1,3 +1,13 @@
1
1
  class Admin::CategoriesController < Puffer::Base
2
2
 
3
+ index do
4
+ field :id
5
+ field :title
6
+ end
7
+
8
+ form do
9
+ field :id
10
+ field :title
11
+ end
12
+
3
13
  end
@@ -1,3 +1,15 @@
1
1
  class Admin::PostsController < Puffer::Base
2
2
 
3
+ index do
4
+ field :user
5
+ field :title
6
+ field :body
7
+ end
8
+
9
+ form do
10
+ field :user
11
+ field :title
12
+ field :body
13
+ end
14
+
3
15
  end
@@ -1,3 +1,17 @@
1
1
  class Admin::ProfilesController < Puffer::Base
2
2
 
3
+ index do
4
+ field :user
5
+ field :name
6
+ field :surname
7
+ field :birth_date
8
+ end
9
+
10
+ form do
11
+ field :user
12
+ field :name
13
+ field :surname
14
+ field :birth_date
15
+ end
16
+
3
17
  end
@@ -1,3 +1,11 @@
1
1
  class Admin::TagsController < Puffer::Base
2
2
 
3
+ index do
4
+ field :name
5
+ end
6
+
7
+ form do
8
+ field :name
9
+ end
10
+
3
11
  end
@@ -0,0 +1,54 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Fields" do
4
+
5
+ end
6
+
7
+ describe "Field" do
8
+
9
+ it "#model" do
10
+ field = Puffer::Fields::Field.new Post, 'user.profile.name'
11
+ field.model.should == Profile
12
+ field = Puffer::Fields::Field.new Post, 'user.email'
13
+ field.model.should == User
14
+ field = Puffer::Fields::Field.new Post, 'title'
15
+ field.model.should == Post
16
+ end
17
+
18
+ it "#name" do
19
+ field = Puffer::Fields::Field.new Post, 'user.profile.name'
20
+ field.name.should == 'name'
21
+ end
22
+
23
+ it "#query_column" do
24
+ field = Puffer::Fields::Field.new Post, 'user.profile.name'
25
+ field.query_column.should == 'profiles.name'
26
+ field = Puffer::Fields::Field.new Post, 'user.email'
27
+ field.query_column.should == 'users.email'
28
+ field = Puffer::Fields::Field.new Post, 'user.full_name'
29
+ field.query_column.should == nil
30
+ end
31
+
32
+ it "#column" do
33
+ field = Puffer::Fields::Field.new Post, 'user.profile.name'
34
+ field.column.name.should == 'name'
35
+ field = Puffer::Fields::Field.new Post, 'user.full_name'
36
+ field.column.should == nil
37
+ end
38
+
39
+ it '#type' do
40
+ field = Puffer::Fields::Field.new Post, 'user.created_at'
41
+ field.type.should == :datetime
42
+ end
43
+
44
+ it '#type with virtual field' do
45
+ field = Puffer::Fields::Field.new Post, 'user.full_name'
46
+ field.type.should == :string
47
+ end
48
+
49
+ it '#type was specified' do
50
+ field = Puffer::Fields::Field.new Post, 'user.full_name', :type => :text
51
+ field.type.should == :text
52
+ end
53
+
54
+ end
@@ -23,7 +23,7 @@ describe "Params" do
23
23
  =end
24
24
 
25
25
  before :each do
26
- @resource = mock(Puffer::Resource, :collection => [], :member => nil, :template => {:nothing => true})
26
+ @resource = mock(Puffer::Resource, :collection => [], :member => nil, :template => {:nothing => true}, :includes => nil)
27
27
  Puffer::Resource.stub(:new) {@resource}
28
28
  end
29
29