hobo 0.9.103 → 0.9.104

Sign up to get free protection for your applications and to get access to all the features.
@@ -14,8 +14,45 @@ likely to cause conflicts, so it is highly recommended that you have
14
14
  your code backed up and in a change control system such as git or
15
15
  subversion.
16
16
 
17
- === Hobo 0.9.103 (AKA 1.0.RC2) ===
17
+ === Hobo 0.9.104 (AKA 1.0RC3) ===
18
+
19
+ [#604](https://hobo.lighthouseapp.com/projects/8324/tickets/604):
20
+
21
+ The new input-many introduced in 0.9.103 had issues with >10 elements,
22
+ several issues running with IE7 and an issue with its javascript
23
+ callbacks.
24
+
25
+ [#537](https://hobo.lighthouseapp.com/projects/8324/tickets/537):
26
+
27
+ `x._?.to_s` now returns nil rather than a blank string
28
+
29
+ [#592](https://hobo.lighthouseapp.com/projects/8324/tickets/592):
30
+
31
+ If you previously had a snippet such as this:
32
+
33
+ <table fields="this, date, account.login">
34
+ <login-view:>
35
+ ...
36
+ </login-view:>
37
+ </table>
18
38
 
39
+ You now have to use:
40
+
41
+ <table fields="this, date, account.login">
42
+ <account-login-view:>
43
+ ...
44
+ </account-login-view:>
45
+ </table>
46
+
47
+ The same change has been applied to `<field-list>`
48
+
49
+ [#568](https://hobo.lighthouseapp.com/projects/8324/tickets/568):
50
+
51
+ `hobo_index` now supports the `:scope` option
52
+
53
+ See also the [git log](http://github.com/tablatom/hobo/commits/v0.9.104)
54
+
55
+ === Hobo 0.9.103 (AKA 1.0.RC2) ===
19
56
 
20
57
  ### Warning
21
58
 
@@ -24,6 +61,8 @@ please check out bug
24
61
  [#574](https://hobo.lighthouseapp.com/projects/8324/tickets/574-rails-235-b0rks-our-rake-tasks-running-on-edge-hobo)
25
62
  for a workaround you need to apply to your Rakefile.
26
63
 
64
+ NOTE: fixed in 0.9.104
65
+
27
66
  ### Bugs
28
67
 
29
68
  This release fixes a couple of serious bugs:
data/Rakefile CHANGED
@@ -10,7 +10,7 @@ $:.unshift File.join(File.expand_path(File.dirname(__FILE__)), '/../hobosupport/
10
10
  require 'hobo'
11
11
 
12
12
  RUBY = File.join(Config::CONFIG['bindir'], Config::CONFIG['ruby_install_name']).sub(/.*\s.*/m, '"\&"')
13
- RUBYDOCTEST = ENV['RUBYDOCTEST'] || "#{RUBY} `which rubydoctest`"
13
+ RUBYDOCTEST = ENV['RUBYDOCTEST'] || "#{RUBY} -S rubydoctest"
14
14
 
15
15
  desc "Default Task"
16
16
  task :default => [ :test ]
@@ -28,8 +28,9 @@ Rake::TestTask.new(:test) { |t|
28
28
  namespace "test" do
29
29
  desc "Run the doctests"
30
30
  task :doctest do |t|
31
+ files=Dir['doctest/*.rdoctest'].map {|f| File.expand_path(f)}.join(' ')
31
32
  # note, tests in doctest/hobo/ are out of date
32
- exit(1) if !system("#{RUBYDOCTEST} doctest/*.rdoctest")
33
+ exit(1) if !system("#{RUBYDOCTEST} #{files}")
33
34
  end
34
35
  end
35
36
 
@@ -61,7 +62,8 @@ Jeweler::Tasks.new do |gemspec|
61
62
  gemspec.add_dependency("rails", [">= 2.2.2"])
62
63
  gemspec.add_dependency("will_paginate", [">= 2.3.11"])
63
64
  gemspec.add_dependency("hobosupport", ["= #{Hobo::VERSION}"])
64
- gemspec.add_dependency("hobofields", ["= #{Hobo::VERSION}"])
65
+ gemspec.add_dependency("hobofields", ["= #{Hobo::VERSION}"])
66
+ gemspec.files.include %w(tasks/environments.rake tasks/hobo_tasks.rake)
65
67
  end
66
68
  Jeweler::GemcutterTasks.new
67
69
  Jeweler::RubyforgeTasks.new do |rubyforge|
@@ -0,0 +1,332 @@
1
+ Hobo's Miscellaneous Model Extensions
2
+ {.document-title}
3
+
4
+ This chapter of the Hobo Manual describes Hobo's model extensions,
5
+ with the exception of [HoboFields](../hobofields) and
6
+ [Permissions](../permissions),
7
+ [Accessible Associations](../multi_model_forms) and [Scopes](../scopes) each of
8
+ which have their own chapters in this manual. This chapter should
9
+ describe everything else that Hobo provides to your models.
10
+
11
+ Contents
12
+ {.contents-heading}
13
+
14
+ - contents
15
+ {:toc}
16
+
17
+ >> require 'rubygems'
18
+ >> require 'active_support'
19
+ >> require 'active_record'
20
+ >> require 'action_pack'
21
+ >> require 'action_view'
22
+ >> require 'action_controller'
23
+ >> mysql_adapter = defined?(JRUBY_VERSION) ? 'jdbcmysql' : 'mysql'
24
+ >> mysql_user = 'root'; mysql_password = ''
25
+ >> mysql_login = "-u #{mysql_user} --password='#{mysql_password}'"
26
+ >> mysql_database = "hobofields_doctest"
27
+ >> system "mysqladmin #{mysql_login} --force drop #{mysql_database} 2> /dev/null"
28
+ >> system("mysqladmin #{mysql_login} create #{mysql_database}") or raise "could not create database"
29
+ >> ActiveRecord::Base.establish_connection(:adapter => mysql_adapter,
30
+ :database => mysql_database,
31
+ :host => "localhost",
32
+ :username => mysql_user,
33
+ :password => mysql_password)
34
+ >> $:.unshift File.join(File.expand_path(File.dirname(__FILE__)), '../../hobofields/lib')
35
+ >> $:.unshift File.join(File.expand_path(File.dirname(__FILE__)), '../../hobosupport/lib')
36
+ >> $:.unshift File.join(File.expand_path(File.dirname(__FILE__)), '../../hobo/lib')
37
+ >> require 'will_paginate'
38
+ >> require 'will_paginate/finder'
39
+ >> require 'hobosupport'
40
+ >> require 'hobofields'
41
+ >> require 'hobo'
42
+ >> Hobo::Model.enable
43
+ >> HoboFields.enable
44
+ >>
45
+ def migrate(renames={})
46
+ up, down = HoboFields::MigrationGenerator.run(renames)
47
+ puts up
48
+ ActiveRecord::Migration.class_eval(up)
49
+ ActiveRecord::Base.send(:subclasses).each { |model| model.reset_column_information }
50
+ [up, down]
51
+ end
52
+ {.hidden}
53
+
54
+ # Special Attributes
55
+
56
+ Rails provides one "special" attribute to your model: `primary_key`
57
+
58
+ >> class Foo < ActiveRecord::Base; end
59
+ >> Foo.primary_key
60
+ => "id"
61
+
62
+ `primary_key` references one of the columns in your table. Rails
63
+ provides a default, but you can change it.
64
+
65
+ In the same fashion, Hobo provides several special attributes that
66
+ generally correspond to columns in your database table.
67
+
68
+ In Hobo, these attributes are specified by passing options to their
69
+ declaration:
70
+
71
+ >> class User < ActiveRecord::Base; end
72
+ >>
73
+ class Post < ActiveRecord::Base
74
+ hobo_model
75
+
76
+ fields do
77
+ title :string, :name => true, :index => true
78
+ content :text, :primary_content => true
79
+ end
80
+
81
+ belongs_to :poster, :class_name => "User", :creator => true
82
+
83
+ set_search_columns :title, :content
84
+ never_show :poster
85
+
86
+ end
87
+ >> migrate
88
+
89
+ In the above example, `name`, `creator` and `primary_content` specify
90
+ attributes that have meaning to Hobo. `set_search_columns` and
91
+ `never_show` also allow you to tag columns for Hobo.
92
+
93
+ ## name
94
+
95
+ Many models have a column in their table that names the object. A
96
+ good example would be the title column in a blog object.
97
+
98
+ fields do
99
+ title :string, :name => true, :index => true
100
+ end
101
+
102
+ >> Post.name_attribute
103
+ => :title
104
+
105
+
106
+ Rapid makes extensive use of this column, both directly and
107
+ indirectly. The `<name>` tag uses it, `<table-plus>` uses it as the
108
+ default sort column, to give two examples of direct uses.
109
+
110
+ Indirectly it is used much more often via the default `to_s` function
111
+ that Hobo::Model provides.
112
+
113
+ >> post = Post.new(:title => "Hello")
114
+ >> post.to_s
115
+ => "Hello"
116
+
117
+ Hobo::Model also provides a default `to_param` function to provide
118
+ human readable URL's:
119
+
120
+ >> post.id = 17
121
+ >> post.to_param
122
+ => "17-hello"
123
+
124
+ You are of course welcome to provide your own `to_s` and `to_param`
125
+ functions, but in most cases the Hobo::Model definitions do well.
126
+
127
+ Hobo::Model also provides a default finder, `named`:
128
+
129
+ >> post.save!
130
+ >> Post.named("Hello").title
131
+ => "Hello"
132
+
133
+ If you are going to be using this finder, it is recommended that you
134
+ also provide an index for your name column:
135
+
136
+ fields do
137
+ title :string, :name => true, :index => true
138
+ end
139
+
140
+ Sometimes a single column does not do a good job of naming the
141
+ object. In this case, you can provide your own name method instead:
142
+
143
+ >>
144
+ class Person < ActiveRecord::Base
145
+ hobo_model
146
+ fields do
147
+ first_name :string
148
+ last_name :string
149
+ end
150
+
151
+ def name
152
+ first_name + ' ' + last_name
153
+ end
154
+ end
155
+ >> migrate
156
+ >> person = Person.new(:first_name => "John", :last_name => "Brown")
157
+ >> person.to_s
158
+ => "John Brown"
159
+
160
+ If you use a composite name, you do lose a couple of features that
161
+ require direct database access: the `named` finder, and the ability to
162
+ use it as a sort column without loading the entire table.
163
+
164
+ ## primary\_content
165
+
166
+ `primary_content` works very similarly to `name`, except that it
167
+ provides the "description" of the row. This is not used in very many
168
+ places. Currently it is only used in the generated `<card>` and
169
+ `<show-page>` for your views, but it may be used in more places in the
170
+ future.
171
+
172
+ fields do
173
+ content :text, :primary_content => true
174
+ end
175
+
176
+ If you do not explicitly set `primary_content`, Hobo::Model will look
177
+ for a method or attribute named `description`, `body`, `content`,
178
+ or `profile` and use that.
179
+
180
+ >>
181
+ class Person < ActiveRecord::Base
182
+ def profile
183
+ "Boring"
184
+ end
185
+ end
186
+
187
+ >> Person.primary_content_attribute
188
+ => "profile"
189
+
190
+ ## login
191
+
192
+ This field is only used in User models. This attribute specifies
193
+ the field that uniquely identifies a user. Unsurprisingly, it's
194
+ primary use is for the `login` field on the signup form, but it is
195
+ used elsewhere.
196
+
197
+ >>
198
+ class User < ActiveRecord::Base
199
+ hobo_user_model
200
+ fields do
201
+ email_address :string, :login => true, :unique => true
202
+ end
203
+ end
204
+
205
+ >> User.login_attribute
206
+ => :email_address
207
+
208
+ ## creator
209
+
210
+ If you specify the `creator` option on one of your fields, Hobo will
211
+ set it to contain the current user when creating the object.
212
+
213
+ Normally this is specified on a belongs\_to:
214
+
215
+ >>
216
+ class Post < ActiveRecord::Base
217
+ belongs_to :poster, :class_name => "User", :creator => true
218
+ end
219
+
220
+ >> Post.creator_attribute
221
+ => :poster
222
+
223
+ However, it may also be added as an option to a string field, in which
224
+ case the `login_attribute` is saved to the field:
225
+
226
+ >>
227
+ class Foo2 < ActiveRecord::Base
228
+ hobo_model
229
+ fields do
230
+ creator_login :string, :creator => true
231
+ end
232
+ end
233
+
234
+ >> Foo2.creator_attribute
235
+ => :creator_login
236
+
237
+ Creator may also be specified via `attr_accessor` if you wish it to
238
+ be set without being saved to the database:
239
+
240
+ >>
241
+ class Foo3 < ActiveRecord::Base
242
+ hobo_model
243
+ attr_accessor :created_by, :creator => true
244
+ end
245
+
246
+ >> Foo3.creator_attribute
247
+ => :created_by
248
+
249
+ ## set\_search\_columns
250
+
251
+ Using the `set_search_columns` class function, you may specify which
252
+ columns are searched by the rapid tags that provide searching
253
+ capabilities.
254
+
255
+ >>
256
+ class Post < ActiveRecord::Base
257
+ set_search_columns :title, :content
258
+ end
259
+
260
+ >> Post.search_columns
261
+ => ["title", "content"]
262
+
263
+ If you do not provide the search columns, Hobo defaults to `%w(name
264
+ title body description content profile)`.
265
+
266
+ ## never\_show
267
+
268
+ `never_show` columns are not displayed in any views that Rapid
269
+ creates.
270
+
271
+ >>
272
+ class Post < ActiveRecord::Base
273
+ never_show :poster
274
+ end
275
+
276
+ >> Post.never_show?(:poster)
277
+ => true
278
+
279
+ # typed\_id
280
+
281
+ >> post.typed_id
282
+ => "post:17"
283
+
284
+ `typed_id` is a method added to Hobo models that uniquely identifies
285
+ the model in your database. This is very useful when coupled with
286
+ `Hobo::Model.find_by_typed_id`:
287
+
288
+ >> Hobo::Model.find_by_typed_id("post:17")
289
+ => #<Post id: 17, title: "Hello", content: nil, poster_id: nil>
290
+
291
+ This is the mechanism that is used to store the current user in the
292
+ session. It is also used throughout Rapid.
293
+
294
+ # set\_default\_order
295
+
296
+ set_default_order :name
297
+ set_default_order "name DESC"
298
+
299
+ This sets the :order option on the finder for the class.
300
+
301
+ Note that Rails 2.3 has
302
+ [default\_scope](http://ryandaigle.com/articles/2008/11/18/what-s-new-in-edge-rails-default-scoping).
303
+ This may be used instead of `set_default_order`, although currently
304
+ there are many bugs open against `default_scope` in Rails. See [ticket #395](
305
+ https://hobo.lighthouseapp.com/projects/8324/tickets/395-remove-default_order-once-were-on-rails-23)
306
+ for more information on this issue.
307
+
308
+ # reverse\_reflection
309
+
310
+ This is the mechanism that Hobo uses to find a matching association on
311
+ the other model.
312
+
313
+ >>
314
+ class Post < ActiveRecord::Base
315
+ belongs_to :poster, :class_name => "User", :creator => true
316
+ end
317
+ >>
318
+ class User < ActiveRecord::Base
319
+ has_many :posts, :foreign_key => "poster_id"
320
+ end
321
+ >> migrate
322
+
323
+ >> Post.reverse_reflection(:poster).name
324
+ => :posts
325
+
326
+ # view\_hints
327
+
328
+ This provides a shortcut to the corresponding
329
+ [ViewHints](../viewhints) object.
330
+
331
+ >> Post.view_hints
332
+ => PostHints
@@ -0,0 +1,273 @@
1
+ Accessible Associations
2
+ {.document-title}
3
+
4
+ This chapter describes Hobo's support for nested models in forms.
5
+ This is mostly technical background -- beginners should not have to
6
+ read more than the introduction.
7
+
8
+ Contents
9
+ {.contents-heading}
10
+
11
+ - contents
12
+ {:toc}
13
+
14
+ >> require 'rubygems'
15
+ >> require 'active_support'
16
+ >> require 'active_record'
17
+ >> require 'action_pack'
18
+ >> require 'action_view'
19
+ >> require 'action_controller'
20
+ >> mysql_adapter = defined?(JRUBY_VERSION) ? 'jdbcmysql' : 'mysql'
21
+ >> mysql_user = 'root'; mysql_password = ''
22
+ >> mysql_login = "-u #{mysql_user} --password='#{mysql_password}'"
23
+ >> mysql_database = "hobofields_doctest"
24
+ >> system "mysqladmin #{mysql_login} --force drop #{mysql_database} 2> /dev/null"
25
+ >> system("mysqladmin #{mysql_login} create #{mysql_database}") or raise "could not create database"
26
+ >> ActiveRecord::Base.establish_connection(:adapter => mysql_adapter,
27
+ :database => mysql_database,
28
+ :host => "localhost",
29
+ :username => mysql_user,
30
+ :password => mysql_password)
31
+ >> $:.unshift File.join(File.expand_path(File.dirname(__FILE__)), '../../hobofields/lib')
32
+ >> $:.unshift File.join(File.expand_path(File.dirname(__FILE__)), '../../hobosupport/lib')
33
+ >> $:.unshift File.join(File.expand_path(File.dirname(__FILE__)), '../../hobo/lib')
34
+ >> require 'will_paginate'
35
+ >> require 'will_paginate/finder'
36
+ >> require 'hobosupport'
37
+ >> require 'hobofields'
38
+ >> require 'hobo'
39
+ >> Hobo::Model.enable
40
+ >> HoboFields.enable
41
+ >> ActiveRecord::Base.logger = ActiveSupport::BufferedLogger.new(STDOUT)
42
+ >>
43
+ def migrate(renames={})
44
+ up, down = HoboFields::MigrationGenerator.run(renames)
45
+ puts up
46
+ ActiveRecord::Migration.class_eval(up)
47
+ ActiveRecord::Base.send(:subclasses).each { |model| model.reset_column_information }
48
+ [up, down]
49
+ end
50
+ {.hidden}
51
+
52
+ # Introduction
53
+
54
+ Using multi-model forms in Hobo is very straightforward:
55
+
56
+ has_many :posts, :accessible => true
57
+
58
+ Once you've done that, the default forms that Hobo builds will use the
59
+ [input-many](/api_tag_defs/input-many) tag.
60
+
61
+ `:accessible => true` works for `has_many`, `has_many :through` and
62
+ `belongs_to`, but does not work for `has_one` or
63
+ `has_and_belongs_to_many`.
64
+
65
+ It's quite common to also add the `:dependent => :destroy` flag to
66
+ accessible associations. This also used to trigger magic in Hobo, but
67
+ this additional magic has been removed and replaced with [View
68
+ Hints](/manual/viewhints). See the [Rails
69
+ rdoc](http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html)
70
+ for more information on `:dependent => :destroy`.
71
+
72
+ # Model Support
73
+
74
+ We'll use rubydoctest to provide our examples for this section. Here
75
+ are the models:
76
+
77
+ >>
78
+ class Foo < ActiveRecord::Base
79
+ hobo_model
80
+ fields { name :string }
81
+ has_many :bars, :accessible => true
82
+ end
83
+ >>
84
+ class Bar < ActiveRecord::Base
85
+ hobo_model
86
+ fields { name :string }
87
+ belongs_to :foo
88
+ end
89
+ >> migrate
90
+
91
+ The `:accessible => true` option patches in
92
+ `Hobo::AccessibleAssociations` to your ActiveRecord model. It
93
+ modifies the `bars=` writer function to support assigning an array of
94
+ records, an array of hashes, an array of ids, or an empty string.
95
+
96
+ ## Assigning an array of records
97
+
98
+ The whole array must be assigned -- any records that are not assigned
99
+ are deleted from your association.
100
+
101
+ >> bar1 = Bar.new(:name => "bar1")
102
+ >> bar2 = Bar.new(:name => "bar2")
103
+ >> foo = Foo.new(:name => "foo1")
104
+ >> foo.bars = [bar1, bar2]
105
+ >> foo.bars.*.name
106
+ => ["bar1", "bar2"]
107
+ >> foo.save!
108
+
109
+ >> foo.bars = [bar2]
110
+ >> foo.bars.*.name
111
+ => ["bar2"]
112
+ >> foo.save!
113
+
114
+ >> bar2.foo.name
115
+ => "foo1"
116
+ >> bar1.reload
117
+ >> bar1.foo
118
+ => nil
119
+
120
+ If `:dependent => :destroy` had been set on `has_many :bars`, bar1
121
+ would now be deleted from the database. Since it hasn't, it still
122
+ exists in the database but has become orphaned.
123
+
124
+ ## Assigning an array of hashes
125
+
126
+ Assigning an array of hashes maps nicely with how Rails deconstructs
127
+ your URI encoded query string. For example, your form can return
128
+
129
+ foo[bars][0][name]=bar1&foo[bars][0][name]=bar2
130
+
131
+ which Rails will decode into your params hash as
132
+
133
+ >> params = {"foo" => {"bars" => [ {"name" => "bar3"}, {"name" => "bar4"}]}}
134
+
135
+ With Hobo's accessible associations, the params hash may be directly
136
+ assigned.
137
+
138
+ >> foo.attributes = params["foo"]
139
+ >> foo.bars.*.name
140
+ => ["bar3", "bar4"]
141
+ >> foo.save!
142
+
143
+ Because these parameters did not include an ID, Hobo created new bar
144
+ models. If you include an ID, Hobo looks up the existing record in
145
+ the database and modifies it with the parameters assigned.
146
+
147
+ >> params = {"foo" => {"bars" => [ {:name => "bar3_mod", :id => "#{foo.bars[0].id}"}]}}
148
+
149
+ >> old_bar3_id = foo.bars[0].id
150
+ >> foo.attributes = params["foo"]
151
+ >> foo.save!
152
+ >> foo.bars.*.name
153
+ => ["bar3_mod"]
154
+ >> foo.bars[0].id == old_bar3_id
155
+ => true
156
+
157
+ ## Assigning an array of IDs
158
+
159
+ While [input-many](/api_tag_defs/input-many) returns an array of
160
+ hashes, [select-many](/api_tag_defs/select-many) returns an array of
161
+ ids. These ids must have an "@" prepended.
162
+
163
+ >> params = {"foo" => {"bars" => ["@#{bar1.id}", "@#{bar2.id}"]}}
164
+ >> foo.attributes = params["foo"]
165
+ >> foo.save!
166
+ >> foo.bars.*.name
167
+ => ["bar1", "bar2"]
168
+
169
+ ## Assigning an empty string
170
+
171
+ You can remove all elements from the association by assigning an empty
172
+ array:
173
+
174
+ >> foo.bars = []
175
+ >> foo.bars
176
+ => []
177
+
178
+ However, there is no way to format a URI query string to make Rails
179
+ construct an empty array in its params hash, so Hobo adds a useful
180
+ shortcut to it's accessible associations:
181
+
182
+ >> foo.bars = ""
183
+ >> foo.bars
184
+ => []
185
+
186
+ # View Support
187
+
188
+ The Rapid tags [input-many](/api_tag_defs/input-many),
189
+ [input-all](/api_tag_defs/input-all),
190
+ [select-many](/api_tag_defs/select-many) and
191
+ [check-many](/api_tag_defs/check-many) all require accessible
192
+ associations.
193
+
194
+ `input-many` may even be used in a nested fashion:
195
+
196
+ <form>
197
+ <field-list:>
198
+ <foos-view:>
199
+ <input-many>
200
+ <field-list:>
201
+ <bars-view:>
202
+ <input-many>
203
+ </input-many>
204
+ </bars-view:>
205
+ </field-list:>
206
+ </input-many>
207
+ </foos-view:>
208
+ </field-list:>
209
+ </form>
210
+
211
+ You do not need to use the accessible association tags -- standard
212
+ inputs acquire the correct `name` for use with accessible associations
213
+ when called from the appropriate context. Here's an example form that
214
+ will work with the example given above in *Model Support*
215
+
216
+ <form>
217
+ <field-list:>
218
+ <bars-view:>
219
+ <repeat>
220
+ <input:name/>
221
+ </repeat>
222
+ </bars-view:>
223
+ <field-list:>
224
+ </form>
225
+
226
+ # Controller Support
227
+
228
+ No special code is required in your controllers to support accessible
229
+ associations, even if you aren't using a Hobo controller.
230
+
231
+ # Validations
232
+
233
+ Validations simply work as you'd expect. The only thing to note is
234
+ that validation errors in a child object will cause the parent
235
+ object to receive an error message of "..." on the association.
236
+
237
+ # Transactions
238
+
239
+ Hobo's accessible associations do not do any explicit saves so any new
240
+ child objects are not saved until the parent object is saved. Rails
241
+ wraps this save in a transaction, so any save is an all or nothing
242
+ deal even though parents and children are saved via different SQL
243
+ statements.
244
+
245
+ # Rails 2.3 nested models
246
+
247
+ Rails 2.3 includes a functionality similar to Hobo's accessible
248
+ associations. The Hobo version is based on an early version of the
249
+ Rails functionality, but unfortunately the Rails version changed
250
+ significantly between the time that the Hobo version was released and
251
+ when Rails 2.3 was released.
252
+
253
+ For more information on Rails 2.3's `accept_nested_attributes_for`,
254
+ see [the Ruby on Rails
255
+ blog](http://weblog.rubyonrails.org/2009/1/26/nested-model-forms) or
256
+ [Ryan Daigle's blog](http://ryandaigle.com/articles/2009/2/1/what-s-new-in-edge-rails-nested-attributes).
257
+
258
+ The two versions use a different model: in Hobo the whole array is
259
+ assigned, allowing add, delete or update via a single mechanism. In
260
+ rails, there's a different mechanism for adding or deleting objects
261
+ from the association, and there's no method for update.
262
+
263
+ Each version have their pluses and minuses. The Hobo version is
264
+ conceptually simpler, but it starts to get unwieldy if there are a
265
+ large number of elements in the association.
266
+
267
+ Both mechanisms are compatible and may be enabled simultaneously.
268
+
269
+ It's certainly possible that Rapid >=1.1 will acquire tags that will
270
+ require `accept_nested_attributes_for`. However, it's unlikely that
271
+ Hobo will drop support for accessible associations unless ActiveRecord
272
+ itself changes significantly.
273
+
@@ -80,6 +80,7 @@ And we'll require hobo:
80
80
  >> require 'hobofields'
81
81
  >> require 'hobo'
82
82
  >> Hobo::Model.enable
83
+ >> HoboFields.enable
83
84
  {.hidden}
84
85
 
85
86
  Let's set up a few models for our testing:
@@ -22,7 +22,7 @@ show_title = !show_link && (name_attribute || !has_body)
22
22
  model_key = model.name.pluralize.underscore
23
23
  -%>
24
24
  <def tag="card" for="<%= model.name %>">
25
- <card class="<%= model_name :dashed %>" param="default" merge>
25
+ <card class="<%= model_class %>" param="default" merge>
26
26
  <% if name_attribute || show_link || has_actions || !has_body -%>
27
27
  <header: param>
28
28
  <% if show_link || show_title -%>
@@ -21,7 +21,7 @@ model_key = model.name.tableize
21
21
  -%>
22
22
 
23
23
  <def tag="index-page" for="<%= model.name %>">
24
- <page merge title="#{ht '<%= model_key %>.index.title', :default=>['<%= model_name :plural %>'] }">
24
+ <page merge title="#{ht '<%= model_key %>.index.title', :default=>['<%= sq_escape(model_name :plural) %>'] }">
25
25
  <body: class="index-page <%= model_class %>" param/>
26
26
 
27
27
  <content: param>
@@ -77,7 +77,7 @@ model_key = model.name.tableize
77
77
 
78
78
 
79
79
  <def tag="new-page" for="<%= model.name %>">
80
- <page merge title="#{ht '<%= model_key %>.new.title', :default=>['New <%=model_name %>'] }">
80
+ <page merge title="#{ht '<%= model_key %>.new.title', :default=>[' New <%= sq_escape(model_name) %>'] }">
81
81
  <body: class="new-page <%= model_class %>" param/>
82
82
 
83
83
  <content: param>
@@ -91,7 +91,7 @@ model_key = model.name.tableize
91
91
 
92
92
  <section param="content-body">
93
93
  <form param>
94
- <submit: label="#{ht '<%= model_key %>.actions.create', :default=>['Create <%= model_name %>']}"/>
94
+ <submit: label="#{ht '<%= model_key %>.actions.create', :default=>['Create <%= sq_escape(model_name) %>']}"/>
95
95
  </form>
96
96
  </section>
97
97
  </content:>
@@ -126,7 +126,7 @@ unless model.view_hints.secondary_children.empty?
126
126
  end
127
127
  -%>
128
128
  <def tag="show-page" for="<%= model.name %>">
129
- <page merge title="#{ht '<%=model_key %>.show.title', :default=>['<%=model_name %>'] }">
129
+ <page merge title="#{ht '<%=model_key %>.show.title', :default=>['<%=sq_escape model_name %>'] }">
130
130
 
131
131
  <body: class="show-page <%= model_class %>" param/>
132
132
 
@@ -202,7 +202,7 @@ end
202
202
  </h3>
203
203
  <form with="&@<%= collection_class.name.underscore %> || new_for_current_user(@<%= model.name.underscore %>.<%= collection %>)" owner="<%= owner %>" without-cancel param>
204
204
  <field-list: skip="<%= owner %>"/>
205
- <submit: label="#{ht '<%= collection.to_s.pluralize %>.actions.add', :default=>['Add'] }"/>
205
+ <submit: label="#{ht '<%= sq_escape collection.to_s.pluralize %>.actions.add', :default=>['Add'] }"/>
206
206
  </form>
207
207
  </section>
208
208
  <% end -%>
@@ -251,7 +251,7 @@ end
251
251
  name_attribute = model.name_attribute
252
252
  -%>
253
253
  <def tag="edit-page" for="<%= model.name %>">
254
- <page merge title="#{ht '<%= model_key %>.edit.title', :default=>['Edit <%=model_name %>'] }">
254
+ <page merge title="#{ht '<%= model_key %>.edit.title', :default=>['Edit <%= sq_escape model_name %>'] }">
255
255
 
256
256
  <body: class="edit-page <%= model_class %>" param/>
257
257
 
@@ -262,7 +262,7 @@ name_attribute = model.name_attribute
262
262
  Edit <type-name/>
263
263
  </ht>
264
264
  </h2>
265
- <delete-button label="#{ht '<%= model_key %>.actions.delete', :default=>['Remove This <%= model_name %>']}" param/>
265
+ <delete-button label="#{ht '<%= model_key %>.actions.delete', :default=>['Remove This <%= sq_escape model_name %>']}" param/>
266
266
  </section>
267
267
 
268
268
  <section param="content-body">
@@ -290,7 +290,7 @@ new_link = :new.in?(actions)
290
290
  -%>
291
291
  <def tag="index-for-<%= owner.dasherize %>-page" polymorphic/>
292
292
  <def tag="index-for-<%= owner.dasherize %>-page" for="<%= model.name %>">
293
- <page merge title="#{ht '<%= model_key %>.index_for_owner.title', :default=>['<%=model_name :plural %> for']} #{name :with => @<%= owner %>, :no_wrapper => true}">
293
+ <page merge title="#{ht '<%= model_key %>.index_for_owner.title', :default=>['<%= sq_escape(model_name :plural) %> for']} #{name :with => @<%= owner %>, :no_wrapper => true}">
294
294
  <body: class="index-for-owner-page <%= owner.dasherize %> <%= model_class %>" param/>
295
295
  <content: param>
296
296
  <header param="content-header">
@@ -348,7 +348,7 @@ new_link = :new.in?(actions)
348
348
  <% if :new.in? actions -%>
349
349
  <def tag="new-for-<%= owner.dasherize %>-page" polymorphic/>
350
350
  <def tag="new-for-<%= owner.dasherize %>-page" for="<%= model.name %>">
351
- <page merge title="#{ht '<%=model_key %>.new_for_owner.title', :default=>['New <%=model_name %> for']} #{name :with => @<%= owner %>}">
351
+ <page merge title="#{ht '<%=model_key %>.new_for_owner.title', :default=>['New <%= sq_escape model_name %> for']} #{name :with => @<%= owner %>}">
352
352
  <body: class="new-for-owner-page <% owner.dasherize %> <%= model_class %>" param/>
353
353
 
354
354
  <content: param>
@@ -369,7 +369,7 @@ new_link = :new.in?(actions)
369
369
  <section param="content-body">
370
370
  <form owner="<%= owner %>" method="post" param>
371
371
  <field-list: skip="<%= owner %>"/>
372
- <submit: label="#{ht '<%=model_key %>.actions.create', :default=>['Create <%= model_name %>']}"/>
372
+ <submit: label="#{ht '<%=model_key %>.actions.create', :default=>['Create <%= sq_escape model_name %>']}"/>
373
373
  </form>
374
374
  </section>
375
375
  </content:>
@@ -383,7 +383,7 @@ new_link = :new.in?(actions)
383
383
  <def tag="<%= creator.dasherize %>-page" polymorphic/>
384
384
  <def tag="<%= creator.dasherize %>-page" for="<%= model.name %>">
385
385
 
386
- <page title="#{ht '<%=model_key %>.<%= creator.underscore %>.title', :default=>['<%= creator.titleize %>']}" merge>
386
+ <page title="#{ht '<%=model_key %>.<%= creator.underscore %>.title', :default=>['<%= sq_escape creator.titleize %>']}" merge>
387
387
 
388
388
  <body: class="lifecycle-start-page <%= creator.dasherize %>-page" param/>
389
389
 
@@ -25,6 +25,16 @@ module ActiveRecord
25
25
  record
26
26
  end
27
27
 
28
+ # DO NOT call super here - AssociationProxy's version loads the collection, and that's bad.
29
+ # TODO: this really belongs in Rails; migrate it there ASAP
30
+ def respond_to?(*args)
31
+ proxy_respond_to?(*args) || Array.new.respond_to?(*args)
32
+ end
33
+
34
+ # TODO: send this patch into Rails. There's no reason to load the collection just to find out it acts like an array.
35
+ def is_a?(klass)
36
+ Array.is_a?(klass)
37
+ end
28
38
 
29
39
  def member_class
30
40
  proxy_reflection.klass
@@ -16,7 +16,7 @@ class HoboError < RuntimeError; end
16
16
 
17
17
  module Hobo
18
18
 
19
- VERSION = "0.9.103"
19
+ VERSION = "0.9.104"
20
20
 
21
21
  class PermissionDeniedError < RuntimeError; end
22
22
 
@@ -220,4 +220,3 @@ module ::Enumerable
220
220
  alias_method_chain :group_by, :metadata
221
221
  end
222
222
 
223
- Hobo.enable if defined?(Rails)
@@ -161,14 +161,19 @@ module Hobo
161
161
  def model_name(*options)
162
162
  name = :plural.in?(options) ? model.view_hints.model_name_plural : model.view_hints.model_name
163
163
  name = name.titleize.downcase if :lowercase.in?(options)
164
- name = name.underscore.gsub('_', '-').gsub('/', '--') if :dashed.in?(options)
165
164
  name = name.camelize if :camel.in?(options)
166
165
  name
167
166
  end
167
+
168
+ # escape single quotes and backslashes for use in a single
169
+ # quoted string
170
+ def sq_escape(s)
171
+ s.gsub(/[\\]/, "\\\\\\\\").gsub(/'/, "\\\\'")
172
+ end
168
173
 
169
174
 
170
175
  def model_class
171
- model_name(:dashed)
176
+ model.name.underscore.gsub('_', '-').gsub('/', '--')
172
177
  end
173
178
 
174
179
 
@@ -397,7 +397,7 @@ module Hobo::Dryml
397
397
  end
398
398
  end
399
399
 
400
- if param_name == :default && overriding_proc
400
+ if param_name == :default && overriding_proc && overriding_proc.arity>0
401
401
  # :default content is handled specially
402
402
 
403
403
  call_tag_parameter_with_default_content(the_tag, attributes, parameters[:default], overriding_proc)
@@ -449,6 +449,8 @@ module Hobo
449
449
  def find_or_paginate(finder, options)
450
450
  options = options.reverse_merge(:paginate => request_requires_pagination?)
451
451
  do_pagination = options.delete(:paginate) && finder.respond_to?(:paginate)
452
+ finder = Array.wrap(options.delete(:scope)).inject(finder) { |a, v| a.send(*Array.wrap(v).flatten) }
453
+
452
454
  options[:order] = :default unless options[:order] || finder.send(:scope, :find)._?[:order]
453
455
 
454
456
  if do_pagination
@@ -43,6 +43,7 @@ module Hobo
43
43
  attr_accessor :current_password, :password, :password_confirmation, :type => :password
44
44
 
45
45
  before_save :encrypt_password
46
+ after_save :stash_current_password
46
47
 
47
48
  never_show *AUTHENTICATION_FIELDS
48
49
 
@@ -148,6 +149,13 @@ module Hobo
148
149
  self.crypted_password = encrypt(password)
149
150
  end
150
151
 
152
+ # after filter that sets current_password so we can pass
153
+ # validate_current_password_when_changing_password if you save
154
+ # again. See
155
+ # https://hobo.lighthouseapp.com/projects/8324-hobo/tickets/590
156
+ def stash_current_password
157
+ @current_password ||= password
158
+ end
151
159
 
152
160
  def changing_password?
153
161
  !new_record? && !lifecycle_changing_password? &&
@@ -0,0 +1,10 @@
1
+ module ::Hobo
2
+ class << self
3
+ attr_accessor :rails_initializer
4
+ end
5
+ end
6
+ ::Hobo.rails_initializer = initializer
7
+
8
+ require File.dirname(__FILE__) + "/../lib/hobo"
9
+ require 'rails_generator'
10
+ Hobo.enable
@@ -1 +1,2 @@
1
1
  Hobo::ModelRouter.reload_routes_on_every_request = true
2
+ # Hobo::Dryml.precompile_taglibs if File.basename($0) != "rake" && Rails.env.production?
@@ -481,9 +481,9 @@ Element.findContaining = function(el, tag) {
481
481
  return null;
482
482
  }
483
483
 
484
- Element.prototype.childWithClass = function(klass) {
484
+ Element.Methods.childWithClass = function(el, klass) {
485
485
  var ret=null;
486
- this.childElements().each(function(el2) {
486
+ el.childElements().each(function(el2) {
487
487
  if(ret==null && el2.hasClassName(klass)) ret=el2;
488
488
  });
489
489
  return ret;
@@ -570,6 +570,14 @@ new HoboBehavior("ul.input-many", {
570
570
  },
571
571
 
572
572
  initialize: function(ul) {
573
+ /* the second clause should be sufficient, but it isn't in IE7. See bug 603 */
574
+ $$(".input-many-template input:hidden, .input-many-template select:hidden, .input-many-template textarea:hidden, .input-many-template button:hidden").each(function(input) {
575
+ if(!input.disabled) {
576
+ input.disabled = true;
577
+ input.addClassName("input_many_template_input");
578
+ }
579
+ });
580
+
573
581
  // disable all elements inside our template, and mark them so we can find them later.
574
582
  $$(".input-many-template input:enabled, .input-many-template select:enabled, .input-many-template textarea:enabled, .input-many-template button:enabled").each(function(input) {
575
583
  input.disabled = true;
@@ -611,7 +619,7 @@ new HoboBehavior("ul.input-many", {
611
619
 
612
620
  // given this==an input-many item, get the submit index
613
621
  getIndex: function() {
614
- return Number(this.id.match(/\[([0-9])+\]$/)[1]);
622
+ return Number(this.id.match(/\[([-0-9]+)\]$/)[1]);
615
623
  },
616
624
 
617
625
  /* For some reason, select() and down() and all those useful functions aren't working for us. Roll our own replacement. */
@@ -627,6 +635,11 @@ new HoboBehavior("ul.input-many", {
627
635
  Event.stop(ev);
628
636
  var ul = el.up('ul.input-many'), li = el.up('li.input-many-li');
629
637
 
638
+ if(li.id.search(/\[-1\]/)>=0) {
639
+ /* if(console) console.log("IE7 messed up again (bug 605)"); */
640
+ return;
641
+ }
642
+
630
643
  var template = ul.down("li.input-many-template");
631
644
  var clone = $(template.cloneNode(true));
632
645
  clone.removeClassName("input-many-template");
@@ -652,7 +665,12 @@ new HoboBehavior("ul.input-many", {
652
665
  // do the add with anim
653
666
  clone.setStyle("display", "none")
654
667
  li.insert({after: clone});
655
- new Effect.BlindDown(clone, {duration: 0.3})
668
+ new Effect.BlindDown(clone, {duration: 0.3, afterFinish: function(ef) {
669
+ Event.addBehavior.reload();
670
+
671
+ ul.fire("rapid:add", { element: clone });
672
+ ul.fire("rapid:change", { element: clone });
673
+ }});
656
674
 
657
675
  // visibility
658
676
  if(li.hasClassName("empty")) {
@@ -664,11 +682,6 @@ new HoboBehavior("ul.input-many", {
664
682
  li.childWithClass("buttons").childWithClass("add-item").addClassName("hidden");
665
683
  }
666
684
 
667
- Event.addBehavior.reload();
668
-
669
- ul.fire("rapid:add", { element: clone })
670
- ul.fire("rapid:change", { element: clone })
671
-
672
685
  return;
673
686
  },
674
687
 
@@ -678,7 +691,12 @@ new HoboBehavior("ul.input-many", {
678
691
  var ul = el.up('ul.input-many'), li = el.up('li.input-many-li')
679
692
  var minimum = parseInt(Hobo.getClassData(ul, 'minimum'));
680
693
 
681
- ul.fire("rapid:remove", { element: li })
694
+ if(li.id.search(/\[-1\]/)>=0) {
695
+ /* if(console) console.log("IE7 messed up again (bug 605)"); */
696
+ return;
697
+ }
698
+
699
+ if(ul.fire("rapid:remove", { element: li }).stopped) return;
682
700
 
683
701
  // rename everybody from me onwards
684
702
  var i=this.getIndex.call(li)
@@ -708,11 +726,11 @@ new HoboBehavior("ul.input-many", {
708
726
  }
709
727
 
710
728
  new Effect.BlindUp(li, { duration: 0.3, afterFinish: function (ef) {
729
+ ul.fire("rapid:change")
711
730
  li.remove()
712
731
  } });
713
732
 
714
- ul.fire("rapid:change")
715
- },
733
+ }
716
734
 
717
735
 
718
736
 
@@ -36,11 +36,11 @@
36
36
  input_attrs = {:no_edit => no_edit} if tag == "input"
37
37
  -%>
38
38
  <labelled-item unless="&tag == 'input' && no_edit == 'skip' && !can_edit?">
39
- <item-label param="#{this_field.to_s.sub('?', '')}-label" unless="&field_name.blank?">
39
+ <item-label param="#{scope.field_name.to_s.sub('?', '').sub('.', '-')}-label" unless="&field_name.blank?">
40
40
  <do param="label"><%= field_name %></do>
41
41
  </item-label>
42
- <item-value param="#{this_field.to_s.sub('?', '')}-view" colspan="&2 if field_name.blank?">
43
- <do param="view"><call-tag tag="&tag" param="#{this_field.to_s.sub('?', '')}-tag" merge-attrs="&input_attrs"/></do>
42
+ <item-value param="#{scope.field_name.to_s.sub('?', '').sub('.', '-')}-view" colspan="&2 if field_name.blank?">
43
+ <do param="view"><call-tag tag="&tag" param="#{scope.field_name.to_s.sub('?', '').sub('.', '-')}-tag" merge-attrs="&input_attrs"/></do>
44
44
  <div param="input-help" if="&tag.to_sym == :input && !this_field_help.blank?"><%= this_field_help %></div>
45
45
  </item-value>
46
46
  </labelled-item>
@@ -119,7 +119,7 @@ This will use `<input/>` as the tag in each table cell instead of `<view/>`
119
119
  class="#{scope.even_odd} #{this_type.name.underscore} #{model_id_class}">
120
120
  <if test="&fields">
121
121
  <with-fields merge-attrs="&all_attributes & attrs_for(:with_fields)" force-all>
122
- <td param="#{this_field.to_s.sub('?', '').gsub('.', '-')}-view"><call-tag tag="&field_tag"/></td>
122
+ <td param="#{scope.field_name.to_s.sub('?', '').gsub('.', '-')}-view"><call-tag tag="&field_tag"/></td>
123
123
  </with-fields>
124
124
  <td class="controls" param="controls" if="&all_parameters[:controls]">
125
125
  <a param="edit-link" action="edit" if="&can_edit?"><ht key="hobo.action.edit">Edit</ht></a>
@@ -616,5 +616,6 @@ The context should be a user object. If `this == current_user` the "you" form is
616
616
  first-option="Guest" options="&user.all(:limit => 30).*.login"
617
617
  onchange="location.href = '#{dev_support_path}/set_current_user?login=' + this.options[this.selectedIndex].value"
618
618
  selected="#{current_user.login}"
619
- class="dev-user-changer"/>
619
+ class="dev-user-changer"
620
+ merge-attrs/>
620
621
  </def>
@@ -118,7 +118,21 @@ AJAX based submission can be enabled by simply adding an `update` attribute. e.g
118
118
  - reset-form: Clear the form after submission (only makes sense for ajax forms)
119
119
 
120
120
  - refocus-form: Refocus the first form-field after submission (only makes sense for ajax forms)
121
-
121
+
122
+ ### Parameters
123
+
124
+ The standard form tag does not have any parameters, nor does it have any default content. However, Hobo does autogenerate polymorphic form tags for each of your models into `app/views/taglibs/auto/rapid/forms.dryml`. These forms have the following parameters:
125
+
126
+ - error-messages
127
+
128
+ - field-list
129
+
130
+ - actions
131
+
132
+ - submit
133
+
134
+ - cancel
135
+
122
136
  -->
123
137
  <def tag="form" polymorphic attrs="update, hidden-fields, action, method, web-method, lifecycle, owner, multipart"><%=
124
138
  ajax_attrs, html_attrs = attributes.partition_hash(Hobo::RapidHelper::AJAX_ATTRS)
@@ -436,7 +450,7 @@ procedure call.
436
450
  The URL that the call is POSTed to is the `object_url` of `this`, plus the method name
437
451
 
438
452
  `<remote-method-button>` supports all of the standard ajax attributes (see the main taglib documention for Rapid
439
- Forms). If any ajax attributes are given, the button becomes an ajax button, if not,
453
+ Forms). If any ajax attributes are given, the button becomes an ajax button, if not, Rails' `button_to` is used, which behaves similarly to a standard link.
440
454
 
441
455
  ### Attributes
442
456
 
@@ -910,8 +924,33 @@ A fully worked up example of nested hjq-input-many's may be found in [agility/jq
910
924
 
911
925
  - `template`: the default values for new items. Normally this functionality is better provided by Model.new, but it's here if you need it.
912
926
 
927
+ ### Events
928
+
929
+ - `rapid:add`: fired after the element is inserted. `memo.element`
930
+ is set to the new element inserted.
931
+
932
+ - `rapid:remove`: fired before the element is
933
+ inserted. `memo.element` is set to the element to be deleted. The
934
+ removal may be cancelled by stopping the event.
935
+
936
+ - `rapid:change`: fired after an element has been removed or
937
+ inserted. `memo.element` set as above.
938
+
939
+ Example javascript:
940
+
941
+ var last_added;
942
+ var last_removed;
943
+ Event.addBehavior({
944
+ '.stories:rapid:add' : function(ev) {
945
+ last_added = ev.memo.element;
946
+ },
947
+ '.stories:rapid:remove' : function(ev) {
948
+ if(!confirm("really?")) ev.stop();
949
+ }
950
+ });
951
+
913
952
  -->
914
- <def tag="input-many" attrs="minimum, fields, skip, template, add-hook, remove-hook" polymorphic >
953
+ <def tag="input-many" attrs="minimum, fields, skip, template" polymorphic >
915
954
  <%
916
955
  # helper function to create id's on buttons to facilitate testing
917
956
  def underize(s)
@@ -922,7 +961,7 @@ end
922
961
  <% template ||= this.try.new_candidate || this.member_class.new %>
923
962
  <% minimum ||= 0 ; minimum = minimum.to_i %>
924
963
  <% skip ||= this.proxy_reflection.klass.reflect_on_all_associations.detect {|p| p.primary_key_name==this.proxy_reflection.primary_key_name}.try.name.to_s if this.respond_to? :proxy_reflection %>
925
- <ul class="input-many #{this_field.dasherize} #{css_data :input_many_prefix, param_name_for_this} #{css_data(:minimum, minimum)} #{css_data(:add_hook, add_hook) if add_hook} #{css_data(:remove_hook, remove_hook) if remove_hook}">
964
+ <ul class="input-many #{this_field.dasherize} #{css_data :input_many_prefix, param_name_for_this} #{css_data(:minimum, minimum)}">
926
965
  <fake-field-context fake-field="-1" context="&template">
927
966
  <li class="input-many-li input-many-template" id="#{param_name_for_this}">
928
967
  <div class="input-many-item" param="default">
@@ -129,8 +129,7 @@
129
129
 
130
130
  <!-- repeats on the plugins used by the application -->
131
131
  <def tag="with-plugins">
132
- <% fi = Hobo::FakeInitializer.new(Rails.configuration)
133
- plugins = Rails.configuration.plugin_loader.new(fi).plugins %>
132
+ <% plugins = Rails.configuration.plugin_loader.new(Hobo.rails_initializer).plugins %>
134
133
  <repeat with="&plugins">
135
134
  <do param="default" />
136
135
  </repeat>
@@ -44,11 +44,13 @@ This tag is in need of a review - it's a bit funky.
44
44
  field_names -= comma_split(skip) if skip
45
45
  field_names = field_names.select {|f| can_view?(this, f)} unless force_all
46
46
  field_names.each do |field|
47
+ %><set-scoped field-name="&field"><%
47
48
  if field == "this"
48
49
  %><do param="default"/><%
49
50
  else
50
51
  %><with field="&field"><do param="default"/></with><%
51
52
  end
53
+ %></set-scoped><%
52
54
  end
53
55
  %></def>
54
56
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hobo
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.103
4
+ version: 0.9.104
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tom Locke
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-12-10 00:00:00 -05:00
12
+ date: 2010-01-21 00:00:00 -05:00
13
13
  default_executable: hobo
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -40,7 +40,7 @@ dependencies:
40
40
  requirements:
41
41
  - - "="
42
42
  - !ruby/object:Gem::Version
43
- version: 0.9.103
43
+ version: 0.9.104
44
44
  version:
45
45
  - !ruby/object:Gem::Dependency
46
46
  name: hobofields
@@ -50,7 +50,7 @@ dependencies:
50
50
  requirements:
51
51
  - - "="
52
52
  - !ruby/object:Gem::Version
53
- version: 0.9.103
53
+ version: 0.9.104
54
54
  version:
55
55
  description:
56
56
  email: tom@tomlocke.com
@@ -68,11 +68,12 @@ files:
68
68
  - bin/hobo
69
69
  - doctest/hobo/hobo_helper.rdoctest
70
70
  - doctest/hobo/lifecycles.rdoctest
71
+ - doctest/model.rdoctest
72
+ - doctest/multi_model_forms.rdoctest
71
73
  - doctest/scopes.rdoctest
72
74
  - dryml_generators/rapid/cards.dryml.erb
73
75
  - dryml_generators/rapid/forms.dryml.erb
74
76
  - dryml_generators/rapid/pages.dryml.erb
75
- - init.rb
76
77
  - lib/action_view_extensions/helpers/tag_helper.rb
77
78
  - lib/active_record/association_collection.rb
78
79
  - lib/active_record/association_proxy.rb
@@ -104,7 +105,6 @@ files:
104
105
  - lib/hobo/dryml/template.rb
105
106
  - lib/hobo/dryml/template_environment.rb
106
107
  - lib/hobo/dryml/template_handler.rb
107
- - lib/hobo/fake_initializer.rb
108
108
  - lib/hobo/find_for.rb
109
109
  - lib/hobo/generator.rb
110
110
  - lib/hobo/guest.rb
@@ -135,6 +135,7 @@ files:
135
135
  - lib/hobo/user.rb
136
136
  - lib/hobo/user_controller.rb
137
137
  - lib/hobo/view_hints.rb
138
+ - rails/init.rb
138
139
  - rails_generators/hobo/USAGE
139
140
  - rails_generators/hobo/hobo_generator.rb
140
141
  - rails_generators/hobo/templates/application.css
data/init.rb DELETED
@@ -1,2 +0,0 @@
1
- require File.dirname(__FILE__) + "/lib/hobo"
2
- require 'rails_generator'
@@ -1,14 +0,0 @@
1
- # really what we want is a reference to the Initializer used in
2
- # config/boot.rb. But since we can't monkey patch that file, we'll
3
- # use a fake instead.
4
-
5
- # this is used by the rapid_summary tag with_plugins
6
- module Hobo
7
- class FakeInitializer
8
- attr_reader :configuration
9
-
10
- def initialize(config = Rails.configuration)
11
- @configuration = config
12
- end
13
- end
14
- end