hobofields 0.8.5 → 0.8.6
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.
- data/Rakefile +17 -2
- data/hobofields.gemspec +34 -134
- data/lib/hobo_fields/email_address.rb +1 -1
- data/lib/hobo_fields/field_spec.rb +14 -1
- data/lib/hobo_fields/migration_generator.rb +23 -21
- data/lib/hobo_fields/model_extensions.rb +3 -4
- data/lib/hobo_fields.rb +11 -5
- data/rails_generators/hobo_migration/hobo_migration_generator.rb +11 -10
- data/test/hobofields.rdoctest +22 -20
- data/test/hobofields_api.rdoctest +134 -113
- data/test/migration_generator.rdoctest +204 -174
- data/test/rich_types.rdoctest +134 -100
- data/test/test_hobofield_model_generator.rb +3 -2
- metadata +3 -3
|
@@ -1,41 +1,61 @@
|
|
|
1
|
-
# HoboFields API
|
|
2
1
|
|
|
3
|
-
|
|
2
|
+
# HoboFields API
|
|
4
3
|
|
|
5
4
|
In order for the API examples to run we need a connection to a database. You can ignore this if you're just looking for documentation.
|
|
5
|
+
{.hidden}
|
|
6
|
+
|
|
7
|
+
Our test requires rails:
|
|
8
|
+
{.hidden}
|
|
6
9
|
|
|
7
10
|
>> require 'rubygems'
|
|
8
11
|
>> require 'activesupport'
|
|
9
|
-
>> Dependencies.load_paths << '.'
|
|
10
|
-
>> Dependencies.mechanism = :require
|
|
11
12
|
>> require 'activerecord'
|
|
12
13
|
>> require 'action_controller'
|
|
13
|
-
|
|
14
|
+
{.hidden}
|
|
15
|
+
|
|
16
|
+
We need a database connection for this test:
|
|
17
|
+
{.hidden}
|
|
18
|
+
|
|
14
19
|
>> mysql_database = "hobofields_doctest"
|
|
20
|
+
>> system "mysqladmin --force drop #{mysql_database} 2> /dev/null"
|
|
15
21
|
>> system("mysqladmin create #{mysql_database}") or raise "could not create database"
|
|
16
22
|
>> ActiveRecord::Base.establish_connection(:adapter => "mysql",
|
|
17
23
|
:database => mysql_database,
|
|
18
24
|
:host => "localhost")
|
|
25
|
+
{.hidden}
|
|
26
|
+
|
|
27
|
+
Some load path manipulation you shouldn't need:
|
|
28
|
+
{.hidden}
|
|
29
|
+
|
|
30
|
+
>> $:.unshift File.join(File.expand_path(File.dirname(__FILE__)), '../../hobofields/lib')
|
|
31
|
+
>> $:.unshift File.join(File.expand_path(File.dirname(__FILE__)), '../../hobosupport/lib')
|
|
32
|
+
{.hidden}
|
|
33
|
+
|
|
34
|
+
Require the module we're testing:
|
|
35
|
+
{.hidden}
|
|
36
|
+
|
|
37
|
+
>> require 'hobosupport'
|
|
38
|
+
>> require 'hobofields'
|
|
19
39
|
|
|
20
40
|
## Example Models
|
|
21
41
|
|
|
22
42
|
Let's define some example models that we can use to demonstrate the API. With HoboFields we define the model's fields, with their name and type, directly in the model like so:
|
|
23
43
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
44
|
+
>>
|
|
45
|
+
class Advert < ActiveRecord::Base
|
|
46
|
+
fields do
|
|
47
|
+
title :string
|
|
48
|
+
body :text
|
|
49
|
+
contact_address :email_address
|
|
50
|
+
end
|
|
51
|
+
end
|
|
32
52
|
|
|
33
53
|
(Note: `:email_address` is an example of a "Rich Type" provided by HoboFields -- more on those later)
|
|
34
54
|
|
|
35
55
|
The migration generator uses this information to create a migration. The following creates and runs the migration so we're ready to go.
|
|
36
56
|
|
|
37
|
-
|
|
38
|
-
|
|
57
|
+
>> up, down = HoboFields::MigrationGenerator.run
|
|
58
|
+
>> ActiveRecord::Migration.class_eval up
|
|
39
59
|
|
|
40
60
|
We're now ready to start demonstrating the API
|
|
41
61
|
|
|
@@ -47,23 +67,23 @@ The main feature of HoboFields, aside from the migration generator, is the abili
|
|
|
47
67
|
|
|
48
68
|
Field values are returned as the type you specify.
|
|
49
69
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
70
|
+
>> a = Advert.new :body => "This is the body"
|
|
71
|
+
>> a.body.class
|
|
72
|
+
=> HoboFields::Text
|
|
53
73
|
|
|
54
74
|
This also works after a round-trip to the database
|
|
55
75
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
76
|
+
>> a.save
|
|
77
|
+
>> b = Advert.find(a.id)
|
|
78
|
+
>> b.body.class
|
|
79
|
+
=> HoboFields::Text
|
|
60
80
|
|
|
61
81
|
HoboFields::Text is a simple subclass of string. It's a "wrapper type", by which we mean you pass the underlying value to the constructor.
|
|
62
82
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
83
|
+
>> t = HoboFields::Text.new("hello")
|
|
84
|
+
=> "hello"
|
|
85
|
+
>> t.class
|
|
86
|
+
=> HoboFields::Text
|
|
67
87
|
|
|
68
88
|
If you define your own rich types, they need to support a one argument constructor in the same way.
|
|
69
89
|
|
|
@@ -74,21 +94,20 @@ Although the body of our advert is really just a string, it's very useful that i
|
|
|
74
94
|
|
|
75
95
|
In the `fields do ... end` block you can give the field-type either as a name (symbol) or a class. For example, we could have said
|
|
76
96
|
|
|
77
|
-
|
|
97
|
+
body HoboFields::Text
|
|
78
98
|
|
|
79
99
|
Obviously the symbol form is a nicer:
|
|
80
100
|
|
|
81
|
-
|
|
101
|
+
body :text
|
|
82
102
|
|
|
83
103
|
If you provide a class it must define the `COLUMN_TYPE` constant. This instructs the migration generator to create the appropriate underlying database column type. It should be a symbol that is a valid column type in a Rails migration.
|
|
84
104
|
|
|
85
|
-
|
|
86
|
-
|
|
105
|
+
>> HoboFields::Text::COLUMN_TYPE
|
|
106
|
+
=> :text
|
|
87
107
|
|
|
88
108
|
The full set of available symbolic names is
|
|
89
109
|
|
|
90
110
|
* `:integer`
|
|
91
|
-
* `:big_integer`
|
|
92
111
|
* `:float`
|
|
93
112
|
* `:string`
|
|
94
113
|
* `:text`
|
|
@@ -99,7 +118,7 @@ The full set of available symbolic names is
|
|
|
99
118
|
* `:textile`
|
|
100
119
|
* `:markdown`
|
|
101
120
|
* `:password`
|
|
102
|
-
* `:
|
|
121
|
+
* `:email_address`
|
|
103
122
|
|
|
104
123
|
You can add your own types too. More on that later.
|
|
105
124
|
|
|
@@ -112,33 +131,33 @@ HoboFields adds a few features to your models.
|
|
|
112
131
|
|
|
113
132
|
Returns the type (i.e. class) declared for a given field or attribute
|
|
114
133
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
134
|
+
>> Advert.attr_type :title
|
|
135
|
+
=> String
|
|
136
|
+
>> Advert.attr_type :body
|
|
137
|
+
=> HoboFields::Text
|
|
119
138
|
|
|
120
139
|
### `Model.column`
|
|
121
140
|
|
|
122
141
|
A shorthand for accessing column metadata
|
|
123
142
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
143
|
+
>> col = Advert.column :title
|
|
144
|
+
>> col.name
|
|
145
|
+
=> "title"
|
|
146
|
+
>> col.klass
|
|
147
|
+
>> String
|
|
129
148
|
|
|
130
149
|
### `Model.attr_accessor` with types
|
|
131
150
|
|
|
132
151
|
In your HoboFields models you can also give type information to "virtual fields" (i.e. regular Ruby attributes)
|
|
133
152
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
153
|
+
>>
|
|
154
|
+
class Advert
|
|
155
|
+
attr_accessor :my_attr, :type => :text
|
|
156
|
+
end
|
|
157
|
+
>> a = Advert.new
|
|
158
|
+
>> a.my_attr = "hello"
|
|
159
|
+
>> a.my_attr.class
|
|
160
|
+
=> HoboFields::Text
|
|
142
161
|
|
|
143
162
|
|
|
144
163
|
## Field validations
|
|
@@ -149,73 +168,73 @@ HoboFields gives you some shorthands for declaring some common validations right
|
|
|
149
168
|
|
|
150
169
|
The `:required` argument to a field gives a `validates_presence_of`:
|
|
151
170
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
171
|
+
>>
|
|
172
|
+
class Advert
|
|
173
|
+
fields do
|
|
174
|
+
title :string, :required
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
>> a = Advert.new
|
|
178
|
+
>> a.valid?
|
|
179
|
+
=> false
|
|
180
|
+
>> a.errors.full_messages
|
|
181
|
+
=> ["Title can't be blank"]
|
|
182
|
+
>> a.title = "Jimbo"
|
|
183
|
+
>> a.save
|
|
184
|
+
=> true
|
|
166
185
|
|
|
167
186
|
|
|
168
187
|
### Unique fields
|
|
169
188
|
|
|
170
189
|
The `:unique` argument in a field declaration gives `validates_uniqueness_of`:
|
|
171
190
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
191
|
+
>>
|
|
192
|
+
class Advert < ActiveRecord::Base
|
|
193
|
+
fields do
|
|
194
|
+
title :string, :unique
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
>> a = Advert.new :title => "Jimbo"
|
|
198
|
+
>> a.valid?
|
|
199
|
+
=> false
|
|
200
|
+
>> a.errors.full_messages
|
|
201
|
+
=> ["Title has already been taken"]
|
|
202
|
+
>> a.title = "Sambo"
|
|
203
|
+
>> a.save
|
|
204
|
+
=> true
|
|
186
205
|
|
|
187
206
|
Let's get back to the basic Advert class with no validations before we continue:
|
|
188
207
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
208
|
+
>> ActiveSupport::Dependencies.remove_constant "Advert"
|
|
209
|
+
>>
|
|
210
|
+
class Advert < ActiveRecord::Base
|
|
211
|
+
fields do
|
|
212
|
+
title :string
|
|
213
|
+
body :text
|
|
214
|
+
contact_address :email_address
|
|
215
|
+
end
|
|
216
|
+
end
|
|
198
217
|
|
|
199
218
|
|
|
200
219
|
### Type specific validations
|
|
201
220
|
|
|
202
221
|
Rich types can define there own validations by a `#validate` method. It should return an error message if the value is invalid, otherwise nil. We can call that method directly to show how it works:
|
|
203
222
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
223
|
+
>> a = Advert.new :contact_address => "not really an email address"
|
|
224
|
+
>> a.contact_address.class
|
|
225
|
+
=> HoboFields::EmailAddress
|
|
226
|
+
>> a.contact_address.validate
|
|
227
|
+
=> "is not valid"
|
|
209
228
|
|
|
210
229
|
But normally that method would be called for us during validation:
|
|
211
230
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
231
|
+
>> a.valid?
|
|
232
|
+
=> false
|
|
233
|
+
>> a.errors.full_messages
|
|
234
|
+
=> ["Contact address is not valid"]
|
|
235
|
+
>> a.contact_address = "me@me.com"
|
|
236
|
+
>> a.valid?
|
|
237
|
+
=> true
|
|
219
238
|
|
|
220
239
|
You can add this capability to your own rich types just by defining `#validate`
|
|
221
240
|
|
|
@@ -223,26 +242,28 @@ You can add this capability to your own rich types just by defining `#validate`
|
|
|
223
242
|
|
|
224
243
|
You can set the type of a virtual field to a rich type, e.g.
|
|
225
244
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
245
|
+
>>
|
|
246
|
+
class Advert
|
|
247
|
+
attr_accessor :alternative_email, :type => :email_address
|
|
248
|
+
end
|
|
230
249
|
|
|
231
250
|
By default, virtual fields are not subject to validation.
|
|
232
251
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
252
|
+
>> a = Advert.new :alternative_email => "woot!"
|
|
253
|
+
>> a.valid?
|
|
254
|
+
=> true
|
|
236
255
|
|
|
237
256
|
To have them validated use `validate_virtual_field`:
|
|
238
257
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
258
|
+
>>
|
|
259
|
+
class Advert
|
|
260
|
+
validate_virtual_field :alternative_email
|
|
261
|
+
end
|
|
262
|
+
>> a.valid?
|
|
263
|
+
=> false
|
|
245
264
|
|
|
246
|
-
|
|
265
|
+
Cleanup
|
|
266
|
+
{.hidden}
|
|
247
267
|
|
|
248
|
-
>> system "mysqladmin --force drop #{mysql_database}"
|
|
268
|
+
>> system "mysqladmin --force drop #{mysql_database} 2> /dev/null"
|
|
269
|
+
{.hidden}
|