hobo_fields 1.3.0.RC

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. data/CHANGES.txt +38 -0
  2. data/LICENSE.txt +22 -0
  3. data/README.txt +8 -0
  4. data/Rakefile +36 -0
  5. data/VERSION +1 -0
  6. data/bin/hobofields +19 -0
  7. data/hobo_fields.gemspec +31 -0
  8. data/lib/generators/hobo/migration/USAGE +47 -0
  9. data/lib/generators/hobo/migration/migration_generator.rb +162 -0
  10. data/lib/generators/hobo/migration/migrator.rb +445 -0
  11. data/lib/generators/hobo/migration/templates/migration.rb.erb +9 -0
  12. data/lib/generators/hobo/model/USAGE +19 -0
  13. data/lib/generators/hobo/model/model_generator.rb +11 -0
  14. data/lib/generators/hobo/model/templates/model_injection.rb.erb +18 -0
  15. data/lib/hobo_fields/extensions/active_record/attribute_methods.rb +48 -0
  16. data/lib/hobo_fields/extensions/active_record/fields_declaration.rb +21 -0
  17. data/lib/hobo_fields/field_declaration_dsl.rb +33 -0
  18. data/lib/hobo_fields/model/field_spec.rb +121 -0
  19. data/lib/hobo_fields/model/index_spec.rb +47 -0
  20. data/lib/hobo_fields/model.rb +226 -0
  21. data/lib/hobo_fields/railtie.rb +13 -0
  22. data/lib/hobo_fields/sanitize_html.rb +23 -0
  23. data/lib/hobo_fields/types/email_address.rb +26 -0
  24. data/lib/hobo_fields/types/enum_string.rb +101 -0
  25. data/lib/hobo_fields/types/html_string.rb +15 -0
  26. data/lib/hobo_fields/types/lifecycle_state.rb +16 -0
  27. data/lib/hobo_fields/types/markdown_string.rb +15 -0
  28. data/lib/hobo_fields/types/password_string.rb +15 -0
  29. data/lib/hobo_fields/types/raw_html_string.rb +13 -0
  30. data/lib/hobo_fields/types/raw_markdown_string.rb +13 -0
  31. data/lib/hobo_fields/types/serialized_object.rb +15 -0
  32. data/lib/hobo_fields/types/text.rb +16 -0
  33. data/lib/hobo_fields/types/textile_string.rb +22 -0
  34. data/lib/hobo_fields.rb +94 -0
  35. data/test/api.rdoctest +244 -0
  36. data/test/doc-only.rdoctest +96 -0
  37. data/test/generators.rdoctest +53 -0
  38. data/test/interactive_primary_key.rdoctest +54 -0
  39. data/test/migration_generator.rdoctest +639 -0
  40. data/test/migration_generator_comments.rdoctest +75 -0
  41. data/test/prepare_testapp.rb +8 -0
  42. data/test/rich_types.rdoctest +394 -0
  43. metadata +140 -0
@@ -0,0 +1,8 @@
1
+ require 'fileutils'
2
+ system %(rake test:prepare_testapp)
3
+ TESTAPP_PATH = '/tmp/hobo_fields_testapp'
4
+ FileUtils.chdir TESTAPP_PATH
5
+ require "#{TESTAPP_PATH}/config/environment"
6
+ require 'rails/generators'
7
+ Rails::Generators.configure!
8
+
@@ -0,0 +1,394 @@
1
+ # HoboFields -- Rich Types
2
+
3
+ This doctest describes the rich types bundled with HoboFields, and the process by which you can create and register your own types.
4
+
5
+ Our test requires to prepare the testapp:
6
+ {.hidden}
7
+
8
+ doctest_require: 'prepare_testapp'
9
+
10
+ >>
11
+ ActiveRecord::Migration.create_table :articles do |t|
12
+ t.text :body
13
+ t.string :status
14
+ end
15
+ >>
16
+
17
+ {.hidden}
18
+
19
+ ## `to_html` method
20
+
21
+ The rich types provide a `to_html` method. If you are using the full Hobo stack you don't need to be aware of this unless you're defining your own rich types -- the `<view>` tag uses `to_html` to render a rich type. If you are not using DRYML and Rapid, you can simply call `to_html` in your views, e.g.
22
+
23
+ <div class="post-body"><%= @post.body.to_html %></div>
24
+
25
+ If you ever decide to change from, say, plain text to markdown formatted, your view won't need to change.
26
+
27
+ ## Defining your own Rich Type
28
+
29
+ Defining a rich type is very simple. We'll show an example here before we go through the bundled types so that you start with the idea that there's really nothing sophisticated going on here.
30
+
31
+ This class defines the methods `to_html` to customize the way the type is rendered, and `validate` to provide a custom validation. It also defined the `COLUMN_TYPE` constant to tell the migration generator what underlying type should represent these values in the database.
32
+
33
+ # Loud text always renderd in caps.
34
+ # It's rude to shout too much so it's not allowed to be
35
+ # longer than 100 characters
36
+ >>
37
+ class LoudText < String
38
+
39
+ COLUMN_TYPE = :string
40
+
41
+ HoboFields.register_type(:loud, self)
42
+
43
+ def validate
44
+ "is too long (you shouldn't shout that much)" if length > 100
45
+ end
46
+
47
+ def format
48
+ # make sure we have enough exclamation marks
49
+ self =~ /!!!$/ ? self + "!!!" : self
50
+ end
51
+
52
+ def to_html(xmldoctype = true)
53
+ upcase
54
+ end
55
+
56
+ end
57
+ >>
58
+
59
+ >> LoudText.new("foO<BAa").to_html
60
+ => "FOO<BAA"
61
+ >> LoudText.new("foO<BAa").to_html.html_safe?
62
+ => false
63
+
64
+ If you place this class in `app/rich_types/loud_text.rb`, Hobo will load it automatically.
65
+
66
+ That's all there is to it. Defining `to_html`, `format` and `validate` are optional, defining `COLUMN_TYPE` and calling `HoboFields.register_type` are not.
67
+
68
+ We inherit from String in this instance. This does not work for all types -- for example, with Fixnum, you should inherit from `DelegateClass(Fixnum)` instead.
69
+
70
+ The format function is added as a before\_validation hook. Ensure that multiple invocations of your format function is safe -- shout.format.format should return the same as shout.format.
71
+
72
+ You can use `LoudText` in your model with
73
+
74
+ fields do
75
+ shout :loud
76
+ end
77
+
78
+ ## Bundled types
79
+
80
+ Here we'll give a quick overview of the bundled types. Remember that along with the specific features the type provides (e.g. validation), the simple fact that the type exists is also useful in the other layers of Hobo. For example `HoboFields::Types::PasswordString` doesn't add any features to `String`, but the fact that a specific type exists for passwords means that the view layer can automatically render an `<input type="password">`.
81
+
82
+
83
+ ### `HoboFields::Types::EmailAddress`
84
+
85
+ Provides validation of correct email address format.
86
+
87
+ >> good = HoboFields::Types::EmailAddress.new("foo@baa.com")
88
+ >> bad = HoboFields::Types::EmailAddress.new("foo.baa.com")
89
+ >> !!good.valid?
90
+ => true
91
+ >> good.validate
92
+ => nil
93
+ >> !!bad.valid?
94
+ => false
95
+ >> bad.validate
96
+ => "is invalid"
97
+
98
+ >> nasty = HoboFields::Types::EmailAddress.new("foo<nasty>&lt;nasty&gt;@baa.com")
99
+ >> nasty.to_html
100
+ => "foo&lt;nasty&gt;&lt;nasty&gt; at baa dot com"
101
+ >> nasty.to_html.html_safe?
102
+ => true
103
+
104
+ ### `HoboFields::Types::HtmlString`
105
+
106
+ `HtmlString` provides no special behavior. The main reason for using this type is that the `to_html` method does not do any html-escaping. Use this for columns that store raw HTML in the database.
107
+
108
+ # no safety treatments are done by `to_html`.
109
+ # even if `nasty.to_html` is actually unsafe, it is marked as html_safe.
110
+ >> nasty = HoboFields::Types::HtmlString.new("p1<p>p2</p>p3<nasty>p4</nasty>p5&lt;script&gt;p6<script>p7</script>p8")
111
+ >> nasty.to_html
112
+ => "p1<p>p2</p>p3<nasty>p4</nasty>p5&lt;script&gt;p6<script>p7</script>p8"
113
+ >> nasty.to_html.html_safe?
114
+ => true
115
+
116
+ >>
117
+ class Article < ActiveRecord::Base
118
+ fields do
119
+ body HoboFields::Types::HtmlString
120
+ end
121
+ end
122
+ >> article = Article.create!(:body => "</div>>>p1<p>p2</p>p3<nasty>p4</nasty>p5&lt;script&gt;p6<script>p7</script>p8")
123
+ # some unsafe html fragements are removed on save,
124
+ # but there's no guarantees that it is well-formed
125
+ >> article.body
126
+ => "</div>>>p1<p>p2</p>p3p4p5&lt;script&gt;p6p8"
127
+ >> article.body == article.body.to_html
128
+ => true
129
+ >> article.body.to_html.html_safe?
130
+ => true
131
+
132
+
133
+ ### `HoboFields::Types::MarkdownString`
134
+
135
+ `HoboFields::Types::MarkdownString` provides a `to_html` that renders markdown syntax into html. It requires the bluecloth gem.
136
+
137
+ >> require 'bluecloth'
138
+ >> markdown = HoboFields::Types::MarkdownString.new %(
139
+ This is a heading
140
+ =================
141
+
142
+ And text can be *emphasised*
143
+ )
144
+ >> markdown.to_html
145
+ => "<h1>This is a heading</h1>\n\n<p>And text can be <em>emphasised</em></p>"
146
+ >> markdown.to_html.html_safe?
147
+ => true
148
+
149
+ # some unsafe html fragements are removed by `to_html`,
150
+ # but there's no guarantees that it is well-formed
151
+ >> markdown = HoboFields::Types::MarkdownString.new("</div>>>p1<script>p2")
152
+ >> markdown.to_html
153
+ => "<p></div>>>p1</p>"
154
+ >> markdown.to_html.html_safe?
155
+ => true
156
+
157
+ ### `HoboFields::Types::TextileString`
158
+
159
+ `HoboFields::Types::TextileString` provides a `to_html` that renders markdown syntax into html. It requires the redcloth gem.
160
+
161
+ >> require 'redcloth'
162
+ >> textile = HoboFields::Types::TextileString.new %(
163
+ Text can be _emphasised_
164
+ )
165
+ >> textile.to_html
166
+ => "<p>Text can be <em>emphasised</em></p>"
167
+ >> textile.to_html.html_safe?
168
+ => true
169
+
170
+ # some unsafe html fragements are removed by `to_html`,
171
+ # but there's no guarantees that it is well-formed
172
+ >> textile = HoboFields::Types::TextileString.new("</div>>>p1<script>p2")
173
+ >> textile.to_html
174
+ => "<p></div>&gt;&gt;p1</p>"
175
+ >> textile.to_html.html_safe?
176
+ => true
177
+
178
+ ### `HoboFields::Types::Text`
179
+
180
+ `HoboFields::Types::Text` provides a `to_html` method with HTML escaping and conversion of newlines to `<br />` tags.
181
+
182
+ >> text = HoboFields::Types::Text.new %(Tom & Jerry
183
+
184
+ Cat & Mouse)
185
+ >> text.to_html
186
+ => "Tom &amp; Jerry<br />\n<br />\n Cat &amp; Mouse"
187
+ >> text.to_html.html_safe?
188
+ => true
189
+
190
+ # `to_html` always returns actually html-safe string
191
+ >> text = HoboFields::Types::Text.new("</div>>>p1<script>p2")
192
+ >> text.to_html
193
+ => "&lt;/div&gt;&gt;&gt;p1&lt;script&gt;p2"
194
+ >> text.to_html.html_safe?
195
+ => true
196
+
197
+ ### `HoboFields::Types::PasswordString`
198
+
199
+ `HoboFields::Types::PasswordString` provides a simple `to_html` to prevent accidental display of a password. It simply returns "`[password hidden]`". The type is also used to indicate the need for an `<input type='password'>`
200
+
201
+ >> HoboFields::Types::PasswordString.new("pass<word>").to_html
202
+ => "[password hidden]"
203
+ >> HoboFields::Types::PasswordString.new("pass<word>").to_html.html_safe?
204
+ => true
205
+
206
+ ### `HoboFields::Serialized`
207
+
208
+ This type lets you store an arbitrary object as serialized text into
209
+ the database column. You can optionally pass the :class option to
210
+ restrict the column type.
211
+
212
+ >>
213
+ >>
214
+ def migrate(renames={})
215
+ up, down = Generators::Hobo::Migration::Migrator.run(renames)
216
+ ActiveRecord::Migration.class_eval(up)
217
+ ActiveRecord::Base.send(:descendants).each { |model| model.reset_column_information }
218
+ [up, down]
219
+ end
220
+ {.hidden}
221
+
222
+ >>
223
+ class Vault1 < ActiveRecord::Base
224
+ fields do
225
+ content :serialized
226
+ end
227
+ end
228
+
229
+ >> migrate
230
+ >> Vault1.create!(:content => {:key => "in Vault"})
231
+ >> Vault1.first.content
232
+ => {:key => "in Vault"}
233
+
234
+ >>
235
+ class Vault2 < ActiveRecord::Base
236
+ fields do
237
+ content :serialized, :class => Hash
238
+ end
239
+ end
240
+
241
+ >> migrate
242
+ >> Vault2.create!(:content => {:key => "in Vault"})
243
+ >> Vault2.first.content
244
+ => {:key => "in Vault"}
245
+ >> Vault2.create!(:content => 17) rescue ActiveRecord::SerializationTypeMismatch
246
+ >> Vault2.all.size
247
+ => 1 # second record not created because of type mismatch
248
+
249
+ ## Enum Strings
250
+
251
+ `HoboFields::Types::EnumString` is not a rich type that you use directly. It's a "type generator", rather like Ruby's `Struct`. It's used for the common situation in database driven apps that you want an enumerated type, but it's not worth going to the extra bother of a separate table enumerating the values. For example you could create a type to represent the status of an article:
252
+
253
+ >> ArticleStatus = HoboFields::Types::EnumString.for('', :draft, :approved, :published)
254
+ => ArticleStatus
255
+
256
+ Note that, like all dynamically created classes in Ruby, the class is anonymous until assigned to a constant:
257
+
258
+ >> klass = HoboFields::Types::EnumString.for(:draft, :approved, :published)
259
+ => #<EnumString draft approved published>
260
+ >> AritcleStatus = klass
261
+ >> ArticleStatus
262
+ => ArticleStatus
263
+
264
+ The values in the enum are available as class constants:
265
+
266
+ >> ArticleStatus::DRAFT
267
+ => "draft"
268
+ >> ArticleStatus::DRAFT.class
269
+ => ArticleStatus
270
+
271
+ There are also instance methods to check for each of the values:
272
+
273
+ >> a = ArticleStatus::APPROVED
274
+ >> a.is_draft?
275
+ => false
276
+ >> a.is_approved?
277
+ => true
278
+
279
+ They can be constructed from strings:
280
+
281
+ >> a = ArticleStatus.new("approved")
282
+ >> a.is_approved?
283
+ => true
284
+
285
+ Equality is string equality, with symbols first converted to strings:
286
+
287
+ >> a == "approved"
288
+ => true
289
+ >> a == :approved
290
+ => true
291
+
292
+
293
+ Note that every enum you create is a subclass of HoboFields::Types::EnumString:
294
+
295
+ >> a.is_a?(HoboFields::Types::EnumString)
296
+ => true
297
+
298
+ EnumString's have a `validate` function defined to fit in with the
299
+ ActiveRecord validation framework.
300
+
301
+ >> a.validate
302
+ => nil
303
+ >> ArticleStatus.new("junked").validate
304
+ => "must be one of '', draft, approved, published"
305
+
306
+ ### Using EnumString in your models
307
+
308
+ `HoboFields::Types::EnumString` extends the field declaration DSL with a shorthand for creating enum types:
309
+
310
+ >>
311
+ class Article < ActiveRecord::Base
312
+ fields do
313
+ status enum_string(:draft, :approved, :published)
314
+ end
315
+ end
316
+ >> Article.attr_type :status
317
+ #<EnumString draft approved published>
318
+
319
+ Sometimes it's nice to have a proper type name. Here's one way you might go about it:
320
+
321
+ >>
322
+ class Article < ActiveRecord::Base
323
+ Status = HoboFields::Types::EnumString.for(:draft, :approved, :published)
324
+ fields do
325
+ status Article::Status
326
+ end
327
+ end
328
+ >> Article.attr_type :status
329
+ => Article::Status
330
+
331
+ >> Article::Status::PUBLISHED.to_html
332
+ => "published"
333
+ >> Article::Status::PUBLISHED.to_html.html_safe?
334
+ => true
335
+
336
+ ### Translating EnumString's
337
+
338
+ Named EnumString's may be translated. Here is an example fr.yml:
339
+
340
+ fr:
341
+ article/statuses:
342
+ draft: "esquisse"
343
+ approved: "approuvé"
344
+ published: "publiés"
345
+
346
+ Alternatively, the translations may be supplied in ruby:
347
+
348
+ >> Article::Status.translated_values = { "draft"=>"esquisse", "approved"=>"approuvé", "published"=>"publiés" }
349
+
350
+ EnumString's can be created with the translated values. Internally,
351
+ they always store the native version.
352
+
353
+ >> a=Article::Status.new("esquisse")
354
+ => "draft"
355
+ >> a.is_draft?
356
+ => true
357
+
358
+ The translated value is available via `to_html`:
359
+
360
+ >> Article::Status::PUBLISHED.to_html
361
+ => "publiés"
362
+ >> Article::Status::PUBLISHED.to_html.html_safe?
363
+ => true
364
+
365
+ Translations only work with named EnumString's. The recommended way of naming the EnumString is to assign it to a constant, but if you do not wish to do this, you can supply the name in an option:
366
+
367
+ >> HoboFields::Types::EnumString.for('one', 'two', 'three', :name => "Count").inspect
368
+ => "Count"
369
+
370
+ `tableize` will be called on your name to provide the translation key.
371
+
372
+
373
+ ###
374
+
375
+ ### `HoboFields::Types::RawHtmlString`
376
+
377
+ # no safety treatments are done by `to_html`.
378
+ # even if `nasty.to_html` is actually unsafe, it is marked as html_safe.
379
+ >> nasty = HoboFields::Types::RawHtmlString.new("p1<p>p2</p>p3<nasty>p4</nasty>p5&lt;script&gt;p6<script>p7</script>p8")
380
+ >> nasty.to_html
381
+ => "p1<p>p2</p>p3<nasty>p4</nasty>p5&lt;script&gt;p6<script>p7</script>p8"
382
+ >> nasty.to_html.html_safe?
383
+ => true
384
+
385
+ ### `HoboFields::Types::RawMarkdownString`
386
+
387
+ # no safety treatments are done by `to_html`.
388
+ # even if `markdown.to_html` is actually unsafe, it is marked as html_safe.
389
+ >> markdown = HoboFields::Types::RawMarkdownString.new("</div>>>p1<script>p2")
390
+ >> markdown.to_html
391
+ => "<p></div>>>p1<script>p2</p>"
392
+ >> markdown.to_html.html_safe?
393
+ => true
394
+
metadata ADDED
@@ -0,0 +1,140 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hobo_fields
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: 6
5
+ version: 1.3.0.RC
6
+ platform: ruby
7
+ authors:
8
+ - Tom Locke
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-05-10 00:00:00 -04:00
14
+ default_executable:
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: hobo_support
18
+ prerelease: false
19
+ requirement: &id001 !ruby/object:Gem::Requirement
20
+ none: false
21
+ requirements:
22
+ - - "="
23
+ - !ruby/object:Gem::Version
24
+ version: 1.3.0.RC
25
+ type: :runtime
26
+ version_requirements: *id001
27
+ - !ruby/object:Gem::Dependency
28
+ name: rubydoctest
29
+ prerelease: false
30
+ requirement: &id002 !ruby/object:Gem::Requirement
31
+ none: false
32
+ requirements:
33
+ - - ">="
34
+ - !ruby/object:Gem::Version
35
+ version: "0"
36
+ type: :development
37
+ version_requirements: *id002
38
+ - !ruby/object:Gem::Dependency
39
+ name: redcloth
40
+ prerelease: false
41
+ requirement: &id003 !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: "0"
47
+ type: :development
48
+ version_requirements: *id003
49
+ - !ruby/object:Gem::Dependency
50
+ name: bluecloth
51
+ prerelease: false
52
+ requirement: &id004 !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: "0"
58
+ type: :development
59
+ version_requirements: *id004
60
+ description: Rich field types and migration generator for Rails
61
+ email: tom@tomlocke.com
62
+ executables:
63
+ - hobofields
64
+ extensions: []
65
+
66
+ extra_rdoc_files: []
67
+
68
+ files:
69
+ - CHANGES.txt
70
+ - LICENSE.txt
71
+ - README.txt
72
+ - Rakefile
73
+ - VERSION
74
+ - bin/hobofields
75
+ - hobo_fields.gemspec
76
+ - lib/generators/hobo/migration/USAGE
77
+ - lib/generators/hobo/migration/migration_generator.rb
78
+ - lib/generators/hobo/migration/migrator.rb
79
+ - lib/generators/hobo/migration/templates/migration.rb.erb
80
+ - lib/generators/hobo/model/USAGE
81
+ - lib/generators/hobo/model/model_generator.rb
82
+ - lib/generators/hobo/model/templates/model_injection.rb.erb
83
+ - lib/hobo_fields.rb
84
+ - lib/hobo_fields/extensions/active_record/attribute_methods.rb
85
+ - lib/hobo_fields/extensions/active_record/fields_declaration.rb
86
+ - lib/hobo_fields/field_declaration_dsl.rb
87
+ - lib/hobo_fields/model.rb
88
+ - lib/hobo_fields/model/field_spec.rb
89
+ - lib/hobo_fields/model/index_spec.rb
90
+ - lib/hobo_fields/railtie.rb
91
+ - lib/hobo_fields/sanitize_html.rb
92
+ - lib/hobo_fields/types/email_address.rb
93
+ - lib/hobo_fields/types/enum_string.rb
94
+ - lib/hobo_fields/types/html_string.rb
95
+ - lib/hobo_fields/types/lifecycle_state.rb
96
+ - lib/hobo_fields/types/markdown_string.rb
97
+ - lib/hobo_fields/types/password_string.rb
98
+ - lib/hobo_fields/types/raw_html_string.rb
99
+ - lib/hobo_fields/types/raw_markdown_string.rb
100
+ - lib/hobo_fields/types/serialized_object.rb
101
+ - lib/hobo_fields/types/text.rb
102
+ - lib/hobo_fields/types/textile_string.rb
103
+ - test/api.rdoctest
104
+ - test/doc-only.rdoctest
105
+ - test/generators.rdoctest
106
+ - test/interactive_primary_key.rdoctest
107
+ - test/migration_generator.rdoctest
108
+ - test/migration_generator_comments.rdoctest
109
+ - test/prepare_testapp.rb
110
+ - test/rich_types.rdoctest
111
+ has_rdoc: true
112
+ homepage: http://hobocentral.net
113
+ licenses: []
114
+
115
+ post_install_message:
116
+ rdoc_options:
117
+ - --charset=UTF-8
118
+ require_paths:
119
+ - lib
120
+ required_ruby_version: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ">="
124
+ - !ruby/object:Gem::Version
125
+ version: "0"
126
+ required_rubygems_version: !ruby/object:Gem::Requirement
127
+ none: false
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: 1.3.6
132
+ requirements: []
133
+
134
+ rubyforge_project: hobo
135
+ rubygems_version: 1.5.0
136
+ signing_key:
137
+ specification_version: 3
138
+ summary: Rich field types and migration generator for Rails
139
+ test_files: []
140
+