hobo 0.9.103 → 0.9.104

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