hobo_fields 1.3.0.RC

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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,639 @@
1
+ # HoboFields - Migration Generator
2
+
3
+ Our test requires to prepare the testapp:
4
+ {.hidden}
5
+
6
+ doctest_require: 'prepare_testapp'
7
+
8
+ {.hidden}
9
+
10
+ ## The migration generator -- introduction
11
+
12
+ The migration generator works by:
13
+
14
+ * Loading all of the models in your Rails app
15
+ * Using the Rails schema-dumper to extract information about the current state of the database.
16
+ * Calculating the changes that are required to bring the database into sync with your application.
17
+
18
+ Normally you would run the migration generator as a regular Rails generator. You would type
19
+
20
+ $ script/generator hobo_migration
21
+
22
+ in your Rails app, and the migration file would be created in `db/migrate`.
23
+
24
+ In order to demonstrate the generator in this doctest script however, we'll be using the Ruby API instead. The method `Generators::Hobo::Migration::Migrator.run` returns a pair of strings -- the up migration and the down migration.
25
+
26
+ At the moment the database is empty and no ActiveRecord models exist, so the generator is going to tell us there is nothing to do.
27
+
28
+ >> Generators::Hobo::Migration::Migrator.run
29
+ => ["", ""]
30
+
31
+
32
+ ### Models without `fields do` are ignored
33
+
34
+ The migration generator only takes into account classes that use HoboFields, i.e. classes with a `fields do` declaration. Models without this are ignored:
35
+
36
+ >> class Advert < ActiveRecord::Base; end
37
+ >> Generators::Hobo::Migration::Migrator.run
38
+ => ["", ""]
39
+
40
+ You can also tell HoboFields to ignore additional tables. You can place this command in your environment.rb or elsewhere:
41
+
42
+ >> Generators::Hobo::Migration::Migrator.ignore_tables = ["green_fishes"]
43
+
44
+ ### Create the table
45
+
46
+ Here we see a simple `create_table` migration along with the `drop_table` down migration
47
+
48
+ >>
49
+ class Advert < ActiveRecord::Base
50
+ fields do
51
+ name :string
52
+ end
53
+ end
54
+ >> up, down = Generators::Hobo::Migration::Migrator.run
55
+ >> up
56
+ =>
57
+ "create_table :adverts do |t|
58
+ t.string :name
59
+ end"
60
+ >> down
61
+ => "drop_table :adverts"
62
+
63
+ Normally we would run the generated migration with `rake db:create`. We can achieve the same effect directly in Ruby like this:
64
+
65
+ >> ActiveRecord::Migration.class_eval up
66
+ >> Advert.columns.*.name
67
+ => ["id", "name"]
68
+
69
+ We'll define a method to make that easier next time
70
+
71
+ >>
72
+ def migrate(renames={})
73
+ up, down = Generators::Hobo::Migration::Migrator.run(renames)
74
+ ActiveRecord::Migration.class_eval(up)
75
+ ActiveRecord::Base.send(:descendants).each { |model| model.reset_column_information }
76
+ [up, down]
77
+ end
78
+
79
+ We'll have a look at the migration generator in more detail later, first we'll have a look at the extra features HoboFields has added to the model.
80
+
81
+
82
+ ### Add fields
83
+
84
+ If we add a new field to the model, the migration generator will add it to the database.
85
+
86
+ >>
87
+ class Advert
88
+ fields do
89
+ name :string
90
+ body :text
91
+ published_at :datetime
92
+ end
93
+ end
94
+ >> up, down = migrate
95
+ >> up
96
+ =>
97
+ "add_column :adverts, :body, :text
98
+ add_column :adverts, :published_at, :datetime"
99
+ >> down
100
+ =>
101
+ "remove_column :adverts, :body
102
+ remove_column :adverts, :published_at"
103
+ >>
104
+
105
+ ### Remove fields
106
+
107
+ If we remove a field from the model, the migration generator removes the database column. Note that we have to explicitly clear the known fields to achieve this in rdoctest -- in a Rails context you would simply edit the file
108
+
109
+ >> Advert.field_specs.clear # not normally needed
110
+ class Advert < ActiveRecord::Base
111
+ fields do
112
+ name :string
113
+ body :text
114
+ end
115
+ end
116
+ >> up, down = migrate
117
+ >> up
118
+ => "remove_column :adverts, :published_at"
119
+ >> down
120
+ => "add_column :adverts, :published_at, :datetime"
121
+
122
+ ### Rename a field
123
+
124
+ Here we rename the `name` field to `title`. By default the generator sees this as removing `name` and adding `title`.
125
+
126
+ >> Advert.field_specs.clear # not normally needed
127
+ class Advert < ActiveRecord::Base
128
+ fields do
129
+ title :string
130
+ body :text
131
+ end
132
+ end
133
+ >> # Just generate - don't run the migration:
134
+ >> up, down = Generators::Hobo::Migration::Migrator.run
135
+ >> up
136
+ =>
137
+ "add_column :adverts, :title, :string
138
+ remove_column :adverts, :name"
139
+ >> down
140
+ =>""
141
+ remove_column :adverts, :title
142
+ add_column :adverts, :name, :string
143
+ >>
144
+
145
+ When run as a generator, the migration-generator won't make this assumption. Instead it will prompt for user input to resolve the ambiguity. When using the Ruby API, we can ask for a rename instead of an add + drop by passing in a hash:
146
+
147
+ >> up, down = Generators::Hobo::Migration::Migrator.run(:adverts => { :name => :title })
148
+ >> up
149
+ => "rename_column :adverts, :name, :title"
150
+ >> down
151
+ => "rename_column :adverts, :title, :name"
152
+
153
+ Let's apply that change to the database
154
+
155
+ >> migrate
156
+
157
+
158
+ ### Change a type
159
+
160
+ >> Advert.attr_type :title
161
+ => String
162
+ >>
163
+ class Advert
164
+ fields do
165
+ title :text
166
+ body :text
167
+ end
168
+ end
169
+ >> up, down = Generators::Hobo::Migration::Migrator.run
170
+ >> up
171
+ => "change_column :adverts, :title, :text, :limit => nil"
172
+ >> down
173
+ => "change_column :adverts, :title, :string"
174
+
175
+
176
+ ### Add a default
177
+
178
+ >>
179
+ class Advert
180
+ fields do
181
+ title :string, :default => "Untitled"
182
+ body :text
183
+ end
184
+ end
185
+ >> up, down = migrate
186
+ >> up.split(',').slice(0,3).join(',')
187
+ => 'change_column :adverts, :title, :string'
188
+ >> up.split(',').slice(3,2).sort.join(',')
189
+ => ' :default => "Untitled", :limit => 255'
190
+ >> down
191
+ => "change_column :adverts, :title, :string"
192
+
193
+
194
+ ### Limits
195
+
196
+ >>
197
+ class Advert
198
+ fields do
199
+ price :integer, :limit => 2
200
+ end
201
+ end
202
+ >> up, down = Generators::Hobo::Migration::Migrator.run
203
+ >> up
204
+ => "add_column :adverts, :price, :integer, :limit => 2"
205
+
206
+ Note that limit on a decimal column is ignored (use :scale and :precision)
207
+
208
+ >>
209
+ class Advert
210
+ fields do
211
+ price :decimal, :limit => 4
212
+ end
213
+ end
214
+ >> up, down = Generators::Hobo::Migration::Migrator.run
215
+ >> up
216
+ => "add_column :adverts, :price, :decimal"
217
+
218
+ Cleanup
219
+ {.hidden}
220
+
221
+ >> Advert.field_specs.delete :price
222
+ {.hidden}
223
+
224
+
225
+ ### Foreign Keys
226
+
227
+ HoboFields extends the `belongs_to` macro so that it also declares the
228
+ foreign-key field. It also generates an index on the field.
229
+
230
+ >>
231
+ class Advert
232
+ belongs_to :category
233
+ end
234
+ >> up, down = Generators::Hobo::Migration::Migrator.run
235
+ >> up
236
+ =>
237
+ "add_column :adverts, :category_id, :integer
238
+
239
+ add_index :adverts, [:category_id]"
240
+ >> down
241
+ =>
242
+ "remove_column :adverts, :category_id
243
+
244
+ remove_index :adverts, :name => :index_adverts_on_category_id rescue ActiveRecord::StatementInvalid"
245
+
246
+ Cleanup:
247
+ {.hidden}
248
+
249
+ >> Advert.field_specs.delete(:category_id)
250
+ >> Advert.index_specs.delete_if {|spec| spec.fields==["category_id"]}
251
+ {.hidden}
252
+
253
+ If you specify a custom foreign key, the migration generator observes that:
254
+
255
+ >>
256
+ class Advert
257
+ belongs_to :category, :foreign_key => "c_id"
258
+ end
259
+ >> up, down = Generators::Hobo::Migration::Migrator.run
260
+ >> up
261
+ =>
262
+ "add_column :adverts, :c_id, :integer
263
+
264
+ add_index :adverts, [:c_id]"
265
+
266
+ Cleanup:
267
+ {.hidden}
268
+
269
+ >> Advert.field_specs.delete(:c_id)
270
+ >> Advert.index_specs.delete_if {|spec| spec.fields==["c_id"]}
271
+ {.hidden}
272
+
273
+ You can avoid generating the index by specifying `:index => false`
274
+
275
+ >>
276
+ class Advert
277
+ belongs_to :category, :index => false
278
+ end
279
+ >> up, down = Generators::Hobo::Migration::Migrator.run
280
+ >> up
281
+ => "add_column :adverts, :category_id, :integer"
282
+
283
+ Cleanup:
284
+ {.hidden}
285
+
286
+ >> Advert.field_specs.delete(:category_id)
287
+ >> Advert.index_specs.delete_if {|spec| spec.fields==["category_id"]}
288
+ {.hidden}
289
+
290
+ You can specify the index name with :index
291
+
292
+ >>
293
+ class Advert
294
+ belongs_to :category, :index => 'my_index'
295
+ end
296
+ >> up, down = Generators::Hobo::Migration::Migrator.run
297
+ >> up
298
+ =>
299
+ "add_column :adverts, :category_id, :integer
300
+
301
+ add_index :adverts, [:category_id], :name => 'my_index'"
302
+
303
+ Cleanup:
304
+ {.hidden}
305
+
306
+ >> Advert.field_specs.delete(:category_id)
307
+ >> Advert.index_specs.delete_if {|spec| spec.fields==["category_id"]}
308
+ {.hidden}
309
+
310
+ ### Timestamps
311
+
312
+ `updated_at` and `created_at` can be declared with the shorthand `timestamps`
313
+
314
+ >>
315
+ class Advert
316
+ fields do
317
+ timestamps
318
+ end
319
+ end
320
+ >> up, down = Generators::Hobo::Migration::Migrator.run
321
+ >> up
322
+ =>
323
+ "add_column :adverts, :created_at, :datetime
324
+ add_column :adverts, :updated_at, :datetime"
325
+ >> down
326
+ =>
327
+ "remove_column :adverts, :created_at
328
+ remove_column :adverts, :updated_at"
329
+ >>
330
+
331
+ Cleanup:
332
+ {.hidden}
333
+
334
+ >> Advert.field_specs.delete(:updated_at)
335
+ >> Advert.field_specs.delete(:created_at)
336
+ {.hidden}
337
+
338
+ ### Indices
339
+
340
+ You can add an index to a field definition
341
+
342
+ >>
343
+ class Advert
344
+ fields do
345
+ title :string, :index => true
346
+ end
347
+ end
348
+ >> up, down = Generators::Hobo::Migration::Migrator.run
349
+ >> up.split("\n")[2]
350
+ => 'add_index :adverts, [:title]'
351
+
352
+ Cleanup:
353
+ {.hidden}
354
+
355
+ >> Advert.index_specs.delete_if {|spec| spec.fields==["title"]}
356
+ {.hidden}
357
+
358
+ You can ask for a unique index
359
+
360
+ >>
361
+ class Advert
362
+ fields do
363
+ title :string, :index => true, :unique => true
364
+ end
365
+ end
366
+ >> up, down = Generators::Hobo::Migration::Migrator.run
367
+ >> up.split("\n")[2]
368
+ => 'add_index :adverts, [:title], :unique => true'
369
+
370
+ Cleanup:
371
+ {.hidden}
372
+
373
+ >> Advert.index_specs.delete_if {|spec| spec.fields==["title"]}
374
+ {.hidden}
375
+
376
+ You can specify the name for the index
377
+
378
+ >>
379
+ class Advert
380
+ fields do
381
+ title :string, :index => 'my_index'
382
+ end
383
+ end
384
+ >> up, down = Generators::Hobo::Migration::Migrator.run
385
+ >> up.split("\n")[2]
386
+ => "add_index :adverts, [:title], :name => 'my_index'"
387
+
388
+ Cleanup:
389
+ {.hidden}
390
+
391
+ >> Advert.index_specs.delete_if {|spec| spec.fields==["title"]}
392
+ {.hidden}
393
+
394
+ You can ask for an index outside of the fields block
395
+
396
+ >>
397
+ class Advert
398
+ index :title
399
+ end
400
+ >> up, down = Generators::Hobo::Migration::Migrator.run
401
+ >> up.split("\n")[2]
402
+ => "add_index :adverts, [:title]"
403
+
404
+ Cleanup:
405
+ {.hidden}
406
+
407
+ >> Advert.index_specs.delete_if {|spec| spec.fields==["title"]}
408
+ {.hidden}
409
+
410
+ The available options for the index function are `:unique` and `:name`
411
+
412
+ >>
413
+ class Advert
414
+ index :title, :unique => true, :name => 'my_index'
415
+ end
416
+ >> up, down = Generators::Hobo::Migration::Migrator.run
417
+ >> up.split("\n")[2]
418
+ => "add_index :adverts, [:title], :unique => true, :name => 'my_index'"
419
+
420
+ Cleanup:
421
+ {.hidden}
422
+
423
+ >> Advert.index_specs.delete_if {|spec| spec.fields==["title"]}
424
+ {.hidden}
425
+
426
+ You can create an index on more than one field
427
+
428
+ >>
429
+ class Advert
430
+ index [:title, :category_id]
431
+ end
432
+ >> up, down = Generators::Hobo::Migration::Migrator.run
433
+ >> up.split("\n")[2]
434
+ => "add_index :adverts, [:title, :category_id]"
435
+
436
+ Cleanup:
437
+ {.hidden}
438
+
439
+ >> Advert.index_specs.delete_if {|spec| spec.fields==["title", "category_id"]}
440
+ {.hidden}
441
+
442
+ Finally, you can specify that the migration generator should completely ignore an index by passing its name to ignore_index in the model. This is helpful for preserving indices that can't be automatically generated, such as prefix indices in MySQL.
443
+
444
+ ### Rename a table
445
+
446
+ The migration generator respects the `set_table_name` declaration, although as before, we need to explicitly tell the generator that we want a rename rather than a create and a drop.
447
+
448
+ >>
449
+ class Advert
450
+ set_table_name "ads"
451
+ fields do
452
+ title :string, :default => "Untitled"
453
+ body :text
454
+ end
455
+ end
456
+ >> up, down = Generators::Hobo::Migration::Migrator.run(:adverts => :ads)
457
+ >> up
458
+ => "rename_table :adverts, :ads"
459
+ >> down
460
+ => "rename_table :ads, :adverts"
461
+
462
+ Set the table name back to what it should be and confirm we're in sync:
463
+
464
+ >> class Advert; set_table_name "adverts"; end
465
+ >> Generators::Hobo::Migration::Migrator.run
466
+ => ["", ""]
467
+
468
+ ### Rename a table
469
+
470
+ As with renaming columns, we have to tell the migration generator about the rename. Here we create a new class 'Advertisement', and tell ActiveRecord to forget about the Advert class. This requires code that shouldn't be shown to impressionable children.
471
+ {.hidden}
472
+
473
+ >>
474
+ def nuke_model_class(klass)
475
+ ActiveSupport::DescendantsTracker.instance_eval do
476
+ class_variable_get('@@direct_descendants')[ActiveRecord::Base].delete(klass)
477
+ end
478
+ Object.instance_eval { remove_const klass.name.to_sym }
479
+ end
480
+ >> nuke_model_class(Advert)
481
+ {.hidden}
482
+
483
+ >>
484
+ class Advertisement < ActiveRecord::Base
485
+ fields do
486
+ title :string, :default => "Untitled"
487
+ body :text
488
+ end
489
+ end
490
+ >> up, down = Generators::Hobo::Migration::Migrator.run(:adverts => :advertisements)
491
+ >> up
492
+ => "rename_table :adverts, :advertisements"
493
+ >> down
494
+ => "rename_table :advertisements, :adverts"
495
+
496
+ ### Drop a table
497
+
498
+ >> nuke_model_class(Advertisement)
499
+ {.hidden}
500
+
501
+ If you delete a model, the migration generator will create a `drop_table` migration.
502
+
503
+ Dropping tables is where the automatic down-migration really comes in handy:
504
+
505
+ >> up, down = Generators::Hobo::Migration::Migrator.run
506
+ >> up
507
+ => "drop_table :adverts"
508
+ >> down
509
+ =>
510
+ "create_table "adverts", :force => true do |t|
511
+ t.text "body"
512
+ t.string "title", :default => "Untitled"
513
+ end"
514
+
515
+ ## STI
516
+
517
+ ### Adding an STI subclass
518
+
519
+ Adding a subclass or two should introduce the 'type' column and no other changes
520
+
521
+ >>
522
+ class Advert < ActiveRecord::Base
523
+ fields do
524
+ body :text
525
+ title :string, :default => "Untitled"
526
+ end
527
+ end
528
+ class FancyAdvert < Advert
529
+ end
530
+ class SuperFancyAdvert < FancyAdvert
531
+ end
532
+ >> up, down = Generators::Hobo::Migration::Migrator.run
533
+ >> up
534
+ =>
535
+ "add_column :adverts, :type, :string
536
+
537
+ add_index :adverts, [:type]"
538
+ >> down
539
+ =>
540
+ "remove_column :adverts, :type
541
+
542
+ remove_index :adverts, :name => :index_adverts_on_type rescue ActiveRecord::StatementInvalid"
543
+
544
+ Cleanup
545
+ {.hidden}
546
+
547
+ >> Advert.field_specs.delete(:type)
548
+ >> nuke_model_class(SuperFancyAdvert)
549
+ >> nuke_model_class(FancyAdvert)
550
+ >> Advert.index_specs.delete_if {|spec| spec.fields==["type"]}
551
+ {.hidden}
552
+
553
+
554
+ ## Coping with multiple changes
555
+
556
+ The migration generator is designed to create complete migrations even if many changes to the models have taken place.
557
+
558
+ First let's confirm we're in a known state. One model, 'Advert', with a string 'title' and text 'body':
559
+
560
+ >> Advert.connection.tables
561
+ => ["adverts"]
562
+ >> Advert.columns.*.name
563
+ => ["id", "body", "title"]
564
+ >> Generators::Hobo::Migration::Migrator.run
565
+ => ["", ""]
566
+
567
+
568
+ ### Rename a column and change the default
569
+
570
+ >> Advert.field_specs.clear
571
+ >>
572
+ class Advert
573
+ fields do
574
+ name :string, :default => "No Name"
575
+ body :text
576
+ end
577
+ end
578
+ >> up, down = Generators::Hobo::Migration::Migrator.run(:adverts => {:title => :name})
579
+ >> up
580
+ =>
581
+ "rename_column :adverts, :title, :name
582
+ change_column :adverts, :name, :string, :default => "No Name", :limit => 255"
583
+ >> down
584
+ =>
585
+ 'rename_column :adverts, :name, :title
586
+ change_column :adverts, :title, :string, :default => "Untitled"'
587
+
588
+
589
+ ### Rename a table and add a column
590
+
591
+ >> nuke_model_class(Advert)
592
+ {.hidden}
593
+
594
+ >>
595
+ class Ad < ActiveRecord::Base
596
+ fields do
597
+ title :string, :default => "Untitled"
598
+ body :text
599
+ created_at :datetime
600
+ end
601
+ end
602
+ >> up, down = Generators::Hobo::Migration::Migrator.run(:adverts => :ads)
603
+ >> up
604
+ =>
605
+ "rename_table :adverts, :ads
606
+
607
+ add_column :ads, :created_at, :datetime"
608
+
609
+ >>
610
+ class Advert < ActiveRecord::Base
611
+ fields do
612
+ body :text
613
+ title :string, :default => "Untitled"
614
+ end
615
+ end
616
+ {.hidden}
617
+
618
+ ## Legacy Keys
619
+
620
+ HoboFields has some support for legacy keys.
621
+
622
+ >> Advert.field_specs.clear
623
+ >>
624
+ class Advert
625
+ fields do
626
+ name :string, :default => "No Name"
627
+ body :text
628
+ end
629
+ set_primary_key "advert_id"
630
+ end
631
+ >> up, down = Generators::Hobo::Migration::Migrator.run(:adverts => {:id => :advert_id})
632
+ >> up
633
+ =>
634
+ "rename_column :adverts, :id, :advert_id
635
+
636
+ >> nuke_model_class(Advert)
637
+ >> nuke_model_class(Ad)
638
+ >> ActiveRecord::Base.connection.execute "drop table `adverts`;"
639
+ {.hidden}
@@ -0,0 +1,75 @@
1
+ # HoboFields - Migration Generator Comments
2
+
3
+ Our test requires to prepare the testapp for a different environment:
4
+ {.hidden}
5
+
6
+ doctest_require: ENV["RAILS_ENV"] = 'mysql_test'; 'prepare_testapp'
7
+
8
+ >> system "cd #{TESTAPP_PATH} && rake db:setup"
9
+ => true
10
+
11
+ >> p Rails.env
12
+ >>
13
+ def nuke_model_class(klass)
14
+ ActiveSupport::DescendantsTracker.instance_eval do
15
+ class_variable_get('@@direct_descendants')[ActiveRecord::Base].delete(klass)
16
+ end
17
+ Object.instance_eval { remove_const klass.name.to_sym }
18
+ end
19
+
20
+ {.hidden}
21
+
22
+
23
+ ## Comments
24
+
25
+ Comments can be added to tables and fields with HoboFields.
26
+
27
+ >>
28
+ class Product < ActiveRecord::Base
29
+ fields do
30
+ name :string, :comment => "short name"
31
+ description :string
32
+ end
33
+ end
34
+ >> Rails.env
35
+ => "mysql_test"
36
+ >> Rails::Generators.invoke 'hobo:migration', %w(-n -m)
37
+
38
+ These comments will be saved to your schema if you have the [column_comments](http://github.com/bryanlarsen/column_comments) plugin installed. If you do not have this plugin installed, the comments will be available by querying `field_specs`:
39
+
40
+ >> Product.field_specs["name"].comment
41
+ => "short name"
42
+
43
+ The plugin [activerecord-comments](http://github.com/bryanlarsen/activerecord-comments) may be used to get the comments from the database directly. If the plugin is installed, use this instead:
44
+
45
+ Product.column("name").comment
46
+
47
+ Because it will be quite common for people not to have both [column_comments](http://github.com/bryanlarsen/column_comments) and [activerecord-comments](http://github.com/bryanlarsen/activerecord-comments) installed, it is impossible for HoboFields to determine the difference between no previous comment and a previously missing plugin. Therefore, HoboFields will not generate a migration if the only change was to add a comment. HoboFields will generate a migration for a comment change, but only if the plugin is installed.
48
+
49
+ >> require 'activerecord-comments'
50
+
51
+ >> # manually add comment as the column_comments plugin would
52
+ >> Product.connection.execute "alter table `products` modify `name` varchar(255) default null comment 'short name';"
53
+
54
+ >>
55
+ class Product < ActiveRecord::Base
56
+ fields do
57
+ name :string, :comment => "Short namex"
58
+ description :string, :comment => "Long name"
59
+ end
60
+ end
61
+ >> up, down = Generators::Hobo::Migration::Migrator.run
62
+ >> up
63
+ >> up.split(',').slice(0,3).join(',')
64
+ => 'change_column :products, :name, :string'
65
+ >> up.split(',').slice(3,2).sort.join(',')
66
+ => " :comment => \"Short namex\", :limit => 255"
67
+
68
+
69
+ Cleanup
70
+ {.hidden}
71
+
72
+ >> nuke_model_class(Product)
73
+ >> ActiveRecord::Base.connection.execute "drop table `products`;"
74
+ >> system "cd #{TESTAPP_PATH} && rake db:drop RAILS_ENV=mysql_test"
75
+ {.hidden}