fast_attributes 0.6.0 → 0.7.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 156410fa3391841eaf2b70060f777f96e70b17cb
4
- data.tar.gz: d272b8684e3b06afe7de0562a06f116f9de9f168
3
+ metadata.gz: 714fe623705696e372c8b188c47dd28e59a79462
4
+ data.tar.gz: 36390f23832e5c3c2253a0f2f54628254fb9faa6
5
5
  SHA512:
6
- metadata.gz: 5770eaeb6359e8fe89fe78301fc6e8dc4242c02edcf99b132def5c27fed34b3e30d2c270c33a4b8ad2f01695d48789ae93d40ef35f8abdef86acaf1984db035d
7
- data.tar.gz: 087d805216430a6cb94d7dd8bce84b72f8fdc644f1853f957aefb39941109f054f0f0ff1c985902a2d7f018a8f863698ec097a81cb8c87494367b7303888182c
6
+ metadata.gz: 758076d95b01e4b721ccfe1674708514584024ce84f94e37fcaf9eb09019c4851ee5b2ba3ea8dbe3dca8ea4119e9a31806252e2cef6f1e6db011d7d4fd91f797
7
+ data.tar.gz: c433c6a45235e96011f55b6314abc087038d1872d8a6f08ac1159d1282f651d53139b351c6622115f0f346adf8183b61e3c66301bce1878cb91be49744aaaa79
data/CHANGELOG.md CHANGED
@@ -1,3 +1,63 @@
1
+ **0.7.0 (August 31, 2014)**
2
+ * Support `boolean` data type as a lenient type.
3
+ ```ruby
4
+ class Product
5
+ extend FastAttributes
6
+ attribute :active, :boolean
7
+ end
8
+
9
+ product = Product.new
10
+ product.active = 1
11
+ product.active # true
12
+ ```
13
+
14
+ * Add support of lenient data types. It allows to define attribute which doesn't correspond to a specific ruby class
15
+ ```ruby
16
+ FastAttributes.type_cast :lenient_attribute do
17
+ from '"yes"', to: 'true'
18
+ from '"no"', to: 'false'
19
+ otherwise 'nil'
20
+ end
21
+
22
+ class LenientAttributes
23
+ extend FastAttributes
24
+ attribute :terms_of_service, :lenient_attribute
25
+ end
26
+
27
+ lenient = LenientAttribute.new
28
+ lenient.terms_of_service = 'yes'
29
+ lenient.terms_of_service # true
30
+ ```
31
+
32
+ * Allow to define default data types using class or symbol.
33
+ ```ruby
34
+ class Book
35
+ extend FastAttributes
36
+
37
+ attribute :title String
38
+ attribute :pages, Integer
39
+ attribute :price, BigDecimal
40
+ attribute :authors, Array
41
+ attribute :published, Date
42
+ attribute :sold, Time
43
+ attribute :finished, DateTime
44
+ attribute :rate, Float
45
+ end
46
+
47
+ class LenientBook
48
+ extend FastAttributes
49
+
50
+ attribute :title, :string
51
+ attribute :pages, :integer
52
+ attribute :price, :big_decimal
53
+ attribute :authors, :array
54
+ attribute :published, :date
55
+ attribute :sold, :time
56
+ attribute :finished, :date_time
57
+ attribute :rate, :float
58
+ end
59
+ ```
60
+
1
61
  **0.6.0 (July 20, 2014)**
2
62
  * Throw custom `FastAttributes::TypeCast::InvalidValueError` exception when value has invalid type.
3
63
  How auto-generated method looks like:
data/README.md CHANGED
@@ -175,7 +175,7 @@ end
175
175
  # end
176
176
  # end
177
177
  ```
178
- Notice, placeholder `%a` represents method name.
178
+ Notice, placeholder `%a` represents method name. Also, `set_type_casting` method generates lenient date type. See [Lenient Data Types](https://github.com/applift/fast_attributes/blob/lenient_data_types/README.md#lenient-data-types) section.
179
179
 
180
180
  If you need to conrol the whole type casting process, you can use the following DSL:
181
181
  ```ruby
@@ -192,6 +192,51 @@ FastAttributes.type_cast String do # begin
192
192
  end # end
193
193
  ```
194
194
 
195
+ ## Lenient Data Types
196
+ It's also possible to define a lenient data type which doesn't correspond to any of ruby classes:
197
+ ```ruby
198
+ FastAttributes.type_cast :yes_no do
199
+ from '"yes"', to: 'true'
200
+ from '"no"', to: 'false'
201
+ otherwise 'nil'
202
+ end
203
+
204
+ class Order
205
+ extend FastAttributes
206
+
207
+ attribute :terms_of_service, :yes_no
208
+ end
209
+
210
+ order = Order.new
211
+ order.terms_of_service = 'yes'
212
+ order.terms_of_service
213
+ # => true
214
+ order.terms_of_service = 'no'
215
+ order.terms_of_service
216
+ # => false
217
+ order.terms_of_service = 42
218
+ order.terms_of_service
219
+ # => nil
220
+ ```
221
+
222
+ All default data types have lenient notation:
223
+ ```ruby
224
+ class Book
225
+ extend FastAttributes
226
+
227
+ attribute :title, :string
228
+ attribute :pages, :integer
229
+ attribute :price, :big_decimal
230
+ attribute :authors, :array
231
+ attribute :published, :date
232
+ attribute :sold, :time
233
+ attribute :finished, :date_time
234
+ attribute :rate, :float
235
+ attribute :active, :boolean
236
+ end
237
+ ```
238
+ Notice, `Boolean` attribute can be defined only via symbol, `fast_attribute` doesn't create `Boolean` class.
239
+
195
240
  ## Extensions
196
241
  * [fast_attributes-uuid](https://github.com/applift/fast_attributes-uuid) - adds support of `UUID` to `fast_attributes`
197
242
 
@@ -12,7 +12,7 @@ module FastAttributes
12
12
 
13
13
  def attribute(*attributes, type)
14
14
  unless FastAttributes.type_exists?(type)
15
- raise UnsupportedTypeError, %(Unsupported attribute type "#{type.name}")
15
+ raise UnsupportedTypeError, %(Unsupported attribute type "#{type.inspect}")
16
16
  end
17
17
 
18
18
  @attributes << [attributes, type]
@@ -1,3 +1,3 @@
1
1
  module FastAttributes
2
- VERSION = '0.6.0'
2
+ VERSION = '0.7.0'
3
3
  end
@@ -6,6 +6,9 @@ require 'fast_attributes/builder'
6
6
  require 'fast_attributes/type_cast'
7
7
 
8
8
  module FastAttributes
9
+ TRUE_VALUES = {true => nil, 1 => nil, '1' => nil, 't' => nil, 'T' => nil, 'true' => nil, 'TRUE' => nil, 'on' => nil, 'ON' => nil}
10
+ FALSE_VALUES = {false => nil, 0 => nil, '0' => nil, 'f' => nil, 'F' => nil, 'false' => nil, 'FALSE' => nil, 'off' => nil, 'OFF' => nil}
11
+
9
12
  class << self
10
13
  def type_casting
11
14
  @type_casting ||= {}
@@ -16,7 +19,8 @@ module FastAttributes
16
19
  end
17
20
 
18
21
  def set_type_casting(klass, casting)
19
- type_cast klass do
22
+ symbol = klass.name.gsub(/([a-z])([A-Z])/, '\1_\2').downcase.to_sym # DateTime => :date_time
23
+ type_cast symbol, klass do
20
24
  from 'nil', to: 'nil'
21
25
  from klass.name, to: '%s'
22
26
  otherwise casting
@@ -31,10 +35,12 @@ module FastAttributes
31
35
  type_casting.has_key?(klass)
32
36
  end
33
37
 
34
- def type_cast(klass, &block)
35
- type_cast = TypeCast.new(klass)
36
- type_cast.instance_eval(&block)
37
- type_casting[klass] = type_cast
38
+ def type_cast(*types_or_classes, &block)
39
+ types_or_classes.each do |type_or_class|
40
+ type_cast = TypeCast.new(type_or_class)
41
+ type_cast.instance_eval(&block)
42
+ type_casting[type_or_class] = type_cast
43
+ end
38
44
  end
39
45
  end
40
46
 
@@ -58,4 +64,18 @@ module FastAttributes
58
64
  set_type_casting Time, 'Time.parse(%s)'
59
65
  set_type_casting DateTime, 'DateTime.parse(%s)'
60
66
  set_type_casting BigDecimal, 'Float(%s);BigDecimal(%s.to_s)'
67
+
68
+ type_cast :boolean do
69
+ otherwise <<-EOS
70
+ if FastAttributes::TRUE_VALUES.has_key?(%s)
71
+ true
72
+ elsif FastAttributes::FALSE_VALUES.has_key?(%s)
73
+ false
74
+ elsif %s.nil?
75
+ nil
76
+ else
77
+ raise FastAttributes::TypeCast::InvalidValueError, %(Invalid value "\#{%s}" for attribute "%a" of type ":boolean")
78
+ end
79
+ EOS
80
+ end
61
81
  end
@@ -53,9 +53,10 @@ describe FastAttributes do
53
53
 
54
54
  describe '#attribute' do
55
55
  it 'raises an exception when type is not supported' do
56
- type = Class.new(Object) { def self.name; 'CustomType' end }
56
+ type = Class.new(Object) { def self.inspect; 'CustomType' end }
57
57
  klass = Class.new(Object) { extend FastAttributes }
58
58
  expect{klass.attribute(:name, type)}.to raise_error(FastAttributes::UnsupportedTypeError, 'Unsupported attribute type "CustomType"')
59
+ expect{klass.attribute(:name, :type)}.to raise_error(FastAttributes::UnsupportedTypeError, 'Unsupported attribute type ":type"')
59
60
  end
60
61
 
61
62
  it 'generates getter methods' do
@@ -113,7 +114,7 @@ describe FastAttributes do
113
114
  expect(book.title).to eq('123')
114
115
  expect(book.name).to eq('456')
115
116
  expect(book.pages).to be(250)
116
- expect(book.price).to eq(BigDecimal.new("2.55"))
117
+ expect(book.price).to eq(BigDecimal.new('2.55'))
117
118
  expect(book.authors).to eq(%w[Jobs])
118
119
  expect(book.published).to eq(Date.new(2014, 6, 21))
119
120
  expect(book.sold).to eq(Time.new(2014, 6, 21, 20, 45, 15))
@@ -126,7 +127,7 @@ describe FastAttributes do
126
127
  book.title = title = 'One'
127
128
  book.name = name = 'Two'
128
129
  book.pages = pages = 250
129
- book.price = price = BigDecimal.new("2.55")
130
+ book.price = price = BigDecimal.new('2.55')
130
131
  book.authors = authors = %w[Jobs]
131
132
  book.published = published = Date.new(2014, 06, 21)
132
133
  book.sold = sold = Time.new(2014, 6, 21, 20, 45, 15)
@@ -149,7 +150,7 @@ describe FastAttributes do
149
150
  book.title = 'One'
150
151
  book.name = 'Two'
151
152
  book.pages = 250
152
- book.price = BigDecimal.new("2.55")
153
+ book.price = BigDecimal.new('2.55')
153
154
  book.authors = %w[Jobs]
154
155
  book.published = Date.new(2014, 06, 21)
155
156
  book.sold = Time.new(2014, 6, 21, 20, 45, 15)
@@ -208,6 +209,113 @@ describe FastAttributes do
208
209
  placeholder.title = 'attribute name 2'
209
210
  expect(placeholder.title).to eq('title%a%title%title!')
210
211
  end
212
+
213
+ it 'generates lenient attributes which do not correspond to a particular data type' do
214
+ lenient_attribute = LenientAttributes.new
215
+ expect(lenient_attribute.terms_of_service).to be(nil)
216
+
217
+ lenient_attribute.terms_of_service = 'yes'
218
+ expect(lenient_attribute.terms_of_service).to be(true)
219
+
220
+ lenient_attribute.terms_of_service = 'no'
221
+ expect(lenient_attribute.terms_of_service).to be(false)
222
+
223
+ lenient_attribute.terms_of_service = 42
224
+ expect(lenient_attribute.terms_of_service).to be(nil)
225
+ end
226
+
227
+ it 'allows to define attributes using symbols as a data type' do
228
+ book = DefaultLenientAttributes.new
229
+ book.title = title = 'One'
230
+ book.pages = pages = 250
231
+ book.price = price = BigDecimal.new('2.55')
232
+ book.authors = authors = %w[Jobs]
233
+ book.published = published = Date.new(2014, 06, 21)
234
+ book.sold = sold = Time.new(2014, 6, 21, 20, 45, 15)
235
+ book.finished = finished = DateTime.new(2014, 05, 20, 21, 35, 20)
236
+ book.rate = rate = 4.1
237
+
238
+ expect(book.title).to be(title)
239
+ expect(book.pages).to be(pages)
240
+ expect(book.price).to eq(price)
241
+ expect(book.authors).to be(authors)
242
+ expect(book.published).to be(published)
243
+ expect(book.sold).to be(sold)
244
+ expect(book.finished).to be(finished)
245
+ expect(book.rate).to be(rate)
246
+ end
247
+
248
+ context 'boolean attribute' do
249
+ let(:object) { DefaultLenientAttributes.new }
250
+
251
+ context 'when value is not set' do
252
+ it 'return nil' do
253
+ expect(object.active).to be(nil)
254
+ end
255
+ end
256
+
257
+ context 'when value represents true' do
258
+ it 'returns true' do
259
+ object.active = true
260
+ expect(object.active).to be(true)
261
+
262
+ object.active = 1
263
+ expect(object.active).to be(true)
264
+
265
+ object.active = '1'
266
+ expect(object.active).to be(true)
267
+
268
+ object.active = 't'
269
+ expect(object.active).to be(true)
270
+
271
+ object.active = 'T'
272
+ expect(object.active).to be(true)
273
+
274
+ object.active = 'true'
275
+ expect(object.active).to be(true)
276
+
277
+ object.active = 'TRUE'
278
+ expect(object.active).to be(true)
279
+
280
+ object.active = 'on'
281
+ expect(object.active).to be(true)
282
+
283
+ object.active = 'ON'
284
+ expect(object.active).to be(true)
285
+ end
286
+ end
287
+
288
+ context 'when value represents false' do
289
+ it 'returns false' do
290
+ object.active = false
291
+ expect(object.active).to be(false)
292
+
293
+ object.active = 0
294
+ expect(object.active).to be(false)
295
+
296
+ object.active = '0'
297
+ expect(object.active).to be(false)
298
+
299
+ object.active = 'f'
300
+ expect(object.active).to be(false)
301
+
302
+ object.active = 'F'
303
+ expect(object.active).to be(false)
304
+
305
+ object.active = 'false'
306
+ expect(object.active).to be(false)
307
+
308
+ object.active = 'FALSE'
309
+ expect(object.active).to be(false)
310
+
311
+ object.active = 'off'
312
+ expect(object.active).to be(false)
313
+
314
+ object.active = 'OFF'
315
+ expect(object.active).to be(false)
316
+ end
317
+ end
318
+ end
211
319
  end
212
320
 
213
321
  describe '#define_attributes' do
@@ -87,3 +87,29 @@ class PlaceholderClass
87
87
  attribute :value, Placeholder
88
88
  attribute :title, Placeholder
89
89
  end
90
+
91
+ FastAttributes.type_cast :lenient_attribute do
92
+ from '"yes"', to: 'true'
93
+ from '"no"', to: 'false'
94
+ otherwise 'nil'
95
+ end
96
+
97
+ class LenientAttributes
98
+ extend FastAttributes
99
+
100
+ attribute :terms_of_service, :lenient_attribute
101
+ end
102
+
103
+ class DefaultLenientAttributes
104
+ extend FastAttributes
105
+
106
+ attribute :title, :string
107
+ attribute :pages, :integer
108
+ attribute :price, :big_decimal
109
+ attribute :authors, :array
110
+ attribute :published, :date
111
+ attribute :sold, :time
112
+ attribute :finished, :date_time
113
+ attribute :rate, :float
114
+ attribute :active, :boolean
115
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fast_attributes
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kostiantyn Stepaniuk
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-07-20 00:00:00.000000000 Z
11
+ date: 2014-08-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -136,3 +136,4 @@ test_files:
136
136
  - spec/fast_attributes_spec.rb
137
137
  - spec/fixtures/classes.rb
138
138
  - spec/spec_helper.rb
139
+ has_rdoc: