motion_virtus 1.0.0.beta0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/README.md +445 -0
- data/lib/motion_virtus.rb +13 -0
- data/lib/project/attribute/accessor/builder.rb +69 -0
- data/lib/project/attribute/accessor/lazy_accessor.rb +39 -0
- data/lib/project/attribute/accessor.rb +100 -0
- data/lib/project/attribute/accessor_method.rb +73 -0
- data/lib/project/attribute/array.rb +24 -0
- data/lib/project/attribute/boolean.rb +52 -0
- data/lib/project/attribute/class.rb +23 -0
- data/lib/project/attribute/coercer.rb +43 -0
- data/lib/project/attribute/collection/coercible_writer.rb +83 -0
- data/lib/project/attribute/collection.rb +56 -0
- data/lib/project/attribute/date.rb +36 -0
- data/lib/project/attribute/date_time.rb +38 -0
- data/lib/project/attribute/decimal.rb +23 -0
- data/lib/project/attribute/default_value/from_callable.rb +37 -0
- data/lib/project/attribute/default_value/from_clonable.rb +37 -0
- data/lib/project/attribute/default_value/from_symbol.rb +37 -0
- data/lib/project/attribute/default_value.rb +49 -0
- data/lib/project/attribute/embedded_value/open_struct_coercer.rb +43 -0
- data/lib/project/attribute/embedded_value/struct_coercer.rb +42 -0
- data/lib/project/attribute/embedded_value.rb +69 -0
- data/lib/project/attribute/float.rb +30 -0
- data/lib/project/attribute/hash/coercible_writer.rb +78 -0
- data/lib/project/attribute/hash.rb +66 -0
- data/lib/project/attribute/integer.rb +27 -0
- data/lib/project/attribute/numeric.rb +25 -0
- data/lib/project/attribute/object.rb +13 -0
- data/lib/project/attribute/reader.rb +39 -0
- data/lib/project/attribute/set.rb +22 -0
- data/lib/project/attribute/string.rb +24 -0
- data/lib/project/attribute/symbol.rb +23 -0
- data/lib/project/attribute/time.rb +36 -0
- data/lib/project/attribute/writer/coercible.rb +45 -0
- data/lib/project/attribute/writer.rb +73 -0
- data/lib/project/attribute.rb +292 -0
- data/lib/project/attribute_set.rb +260 -0
- data/lib/project/class_inclusions.rb +41 -0
- data/lib/project/class_methods.rb +102 -0
- data/lib/project/configuration.rb +65 -0
- data/lib/project/const_missing_extensions.rb +16 -0
- data/lib/project/extensions.rb +101 -0
- data/lib/project/instance_methods.rb +165 -0
- data/lib/project/module_builder.rb +92 -0
- data/lib/project/module_extensions.rb +72 -0
- data/lib/project/stubs/date.rb +2 -0
- data/lib/project/stubs/date_time.rb +2 -0
- data/lib/project/stubs/decimal.rb +2 -0
- data/lib/project/stubs/ostruct.rb +149 -0
- data/lib/project/stubs/set.rb +767 -0
- data/lib/project/stubs.rb +5 -0
- data/lib/project/support/equalizer.rb +147 -0
- data/lib/project/support/options.rb +114 -0
- data/lib/project/support/type_lookup.rb +109 -0
- data/lib/project/value_object.rb +139 -0
- data/lib/project/version.rb +3 -0
- data/lib/project/virtus.rb +128 -0
- metadata +158 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
YjIyMGQ5NzI0ZDRiOTlhOGQwNzNmNTdlZTdhYTAxMTA1Zjg0OWUyZQ==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
ZTVhYWFmZTNkNjAxMTA1YjBjY2M0NDYzNTA1ZmFkOTE3ODczZDRmZA==
|
7
|
+
!binary "U0hBNTEy":
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
NDM2NzFmNjMwOTRkY2RhODhiYjQzMWNjMzU3NGI5NzJkNDQ5NTBkMmU4YmNm
|
10
|
+
MzJkZjNmMmFhZjIxODUyOGRhZDgxNDczNmNjNTZiNjI2ZjQzYTY4ZTYxZTRl
|
11
|
+
OTg0OGViMjEyMDMwY2I4MWQxYTA2NzNkNTVhZTU1YTFhNzRkYjQ=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
MzlkYTYwMTgxMjVmM2Q1N2NhMWQxODYwODIwOTQ2MGU3ZWFmMjA4YmUwOTNi
|
14
|
+
YzQ2YTNiMDBlYThlMTczNjdlYzJjMmFjM2QyMDc3NTBhYzhhM2ZhMmI3MDVi
|
15
|
+
YTE0MGI5YmFjNTQ0OGYwODU2ZDFlOTEwMWMyYWM1NDI5YTAxYWI=
|
data/README.md
ADDED
@@ -0,0 +1,445 @@
|
|
1
|
+
# motion_virtus
|
2
|
+
|
3
|
+
A RubyMotion port of [solnic's](https://github.com/solnic) [virtus](https://github.com/solnic/virtus) library.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'motion_virtus'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install motion_virtus
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
### Using Virtus with Classes
|
22
|
+
|
23
|
+
You can create classes extended with virtus and define attributes:
|
24
|
+
|
25
|
+
``` ruby
|
26
|
+
class User
|
27
|
+
include Virtus
|
28
|
+
|
29
|
+
attribute :name, String
|
30
|
+
attribute :age, Integer
|
31
|
+
attribute :birthday, DateTime
|
32
|
+
end
|
33
|
+
|
34
|
+
user = User.new(:name => 'Piotr', :age => 29)
|
35
|
+
user.attributes # => { :name => "Piotr", :age => 29 }
|
36
|
+
|
37
|
+
user.name # => "Piotr"
|
38
|
+
|
39
|
+
user.age = '29' # => 29
|
40
|
+
user.age.class # => Fixnum
|
41
|
+
|
42
|
+
user.birthday = 'November 18th, 1983' # => #<DateTime: 1983-11-18T00:00:00+00:00 (4891313/2,0/1,2299161)>
|
43
|
+
|
44
|
+
# mass-assignment
|
45
|
+
user.attributes = { :name => 'Jane', :age => 21 }
|
46
|
+
user.name # => "Jane"
|
47
|
+
user.age # => 21
|
48
|
+
```
|
49
|
+
|
50
|
+
### Using Virtus with Modules
|
51
|
+
|
52
|
+
You can create modules extended with virtus and define attributes for later
|
53
|
+
inclusion in your classes:
|
54
|
+
|
55
|
+
```ruby
|
56
|
+
module Name
|
57
|
+
include Virtus
|
58
|
+
|
59
|
+
attribute :name, String
|
60
|
+
end
|
61
|
+
|
62
|
+
module Age
|
63
|
+
include Virtus
|
64
|
+
|
65
|
+
attribute :age, Integer
|
66
|
+
end
|
67
|
+
|
68
|
+
class User
|
69
|
+
include Name, Age
|
70
|
+
end
|
71
|
+
|
72
|
+
user = User.new(:name => 'John', :age => '30')
|
73
|
+
```
|
74
|
+
|
75
|
+
### Dynamically Extending Instances
|
76
|
+
|
77
|
+
It's also possible to dynamically extend an object with Virtus:
|
78
|
+
|
79
|
+
```ruby
|
80
|
+
class User
|
81
|
+
# nothing here
|
82
|
+
end
|
83
|
+
|
84
|
+
user = User.new
|
85
|
+
user.extend(Virtus)
|
86
|
+
user.attribute :name, String
|
87
|
+
user.name = 'John'
|
88
|
+
user.name # => 'John'
|
89
|
+
```
|
90
|
+
|
91
|
+
### Default Values
|
92
|
+
|
93
|
+
``` ruby
|
94
|
+
class Page
|
95
|
+
include Virtus
|
96
|
+
|
97
|
+
attribute :title, String
|
98
|
+
|
99
|
+
# default from a singleton value (integer in this case)
|
100
|
+
attribute :views, Integer, :default => 0
|
101
|
+
|
102
|
+
# default from a singleton value (boolean in this case)
|
103
|
+
attribute :published, Boolean, :default => false
|
104
|
+
|
105
|
+
# default from a callable object (proc in this case)
|
106
|
+
attribute :slug, String, :default => lambda { |page, attribute| page.title.downcase.gsub(' ', '-') }
|
107
|
+
|
108
|
+
# default from a method name as symbol
|
109
|
+
attribute :editor_title, String, :default => :default_editor_title
|
110
|
+
|
111
|
+
def default_editor_title
|
112
|
+
published? ? title : "UNPUBLISHED: #{title}"
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
page = Page.new(:title => 'Virtus README')
|
117
|
+
page.slug # => 'virtus-readme'
|
118
|
+
page.views # => 0
|
119
|
+
page.published # => false
|
120
|
+
page.editor_title # => "UNPUBLISHED: Virtus README"
|
121
|
+
```
|
122
|
+
|
123
|
+
### Embedded Value
|
124
|
+
|
125
|
+
``` ruby
|
126
|
+
class City
|
127
|
+
include Virtus
|
128
|
+
|
129
|
+
attribute :name, String
|
130
|
+
end
|
131
|
+
|
132
|
+
class Address
|
133
|
+
include Virtus
|
134
|
+
|
135
|
+
attribute :street, String
|
136
|
+
attribute :zipcode, String
|
137
|
+
attribute :city, City
|
138
|
+
end
|
139
|
+
|
140
|
+
class User
|
141
|
+
include Virtus
|
142
|
+
|
143
|
+
attribute :name, String
|
144
|
+
attribute :address, Address
|
145
|
+
end
|
146
|
+
|
147
|
+
user = User.new(:address => {
|
148
|
+
:street => 'Street 1/2', :zipcode => '12345', :city => { :name => 'NYC' } })
|
149
|
+
|
150
|
+
user.address.street # => "Street 1/2"
|
151
|
+
user.address.city.name # => "NYC"
|
152
|
+
```
|
153
|
+
|
154
|
+
### Collection Member Coercions
|
155
|
+
|
156
|
+
``` ruby
|
157
|
+
# Support "primitive" classes
|
158
|
+
class Book
|
159
|
+
include Virtus
|
160
|
+
|
161
|
+
attribute :page_numbers, Array[Integer]
|
162
|
+
end
|
163
|
+
|
164
|
+
book = Book.new(:page_numbers => %w[1 2 3])
|
165
|
+
book.page_numbers # => [1, 2, 3]
|
166
|
+
|
167
|
+
# Support EmbeddedValues, too!
|
168
|
+
class Address
|
169
|
+
include Virtus
|
170
|
+
|
171
|
+
attribute :address, String
|
172
|
+
attribute :locality, String
|
173
|
+
attribute :region, String
|
174
|
+
attribute :postal_code, String
|
175
|
+
end
|
176
|
+
|
177
|
+
class PhoneNumber
|
178
|
+
include Virtus
|
179
|
+
|
180
|
+
attribute :number, String
|
181
|
+
end
|
182
|
+
|
183
|
+
class User
|
184
|
+
include Virtus
|
185
|
+
|
186
|
+
attribute :phone_numbers, Array[PhoneNumber]
|
187
|
+
attribute :addresses, Set[Address]
|
188
|
+
end
|
189
|
+
|
190
|
+
user = User.new(
|
191
|
+
:phone_numbers => [
|
192
|
+
{ :number => '212-555-1212' },
|
193
|
+
{ :number => '919-444-3265' } ],
|
194
|
+
:addresses => [
|
195
|
+
{ :address => '1234 Any St.', :locality => 'Anytown', :region => "DC", :postal_code => "21234" } ])
|
196
|
+
|
197
|
+
user.phone_numbers # => [#<PhoneNumber:0x007fdb2d3bef88 @number="212-555-1212">, #<PhoneNumber:0x007fdb2d3beb00 @number="919-444-3265">]
|
198
|
+
|
199
|
+
user.addresses # => #<Set: {#<Address:0x007fdb2d3be448 @address="1234 Any St.", @locality="Anytown", @region="DC", @postal_code="21234">}>
|
200
|
+
```
|
201
|
+
|
202
|
+
### Hash attributes coercion
|
203
|
+
|
204
|
+
``` ruby
|
205
|
+
class Package
|
206
|
+
include Virtus
|
207
|
+
|
208
|
+
attribute :dimensions, Hash[Symbol => Float]
|
209
|
+
end
|
210
|
+
|
211
|
+
package = Package.new(:dimensions => { 'width' => "2.2", :height => 2, "length" => 4.5 })
|
212
|
+
package.dimensions # => { :width => 2.2, :height => 2.0, :length => 4.5 }
|
213
|
+
```
|
214
|
+
|
215
|
+
### IMPORTANT note about member coercions
|
216
|
+
|
217
|
+
Virtus performs coercions only when a value is being assigned. If you mutate the value later on using its own
|
218
|
+
interfaces then coercion won't be triggered.
|
219
|
+
|
220
|
+
Here's an example:
|
221
|
+
|
222
|
+
``` ruby
|
223
|
+
class Book
|
224
|
+
include Virtus
|
225
|
+
|
226
|
+
attribute :title, String
|
227
|
+
end
|
228
|
+
|
229
|
+
class Library
|
230
|
+
include Virtus
|
231
|
+
|
232
|
+
attribute :books, Array[Book]
|
233
|
+
end
|
234
|
+
|
235
|
+
library = Library.new
|
236
|
+
|
237
|
+
# This will coerce Hash to a Book instance
|
238
|
+
library.books = [ { :title => 'Introduction to Virtus' } ]
|
239
|
+
|
240
|
+
# This WILL NOT COERCE the value because you mutate the books array with Array#<<
|
241
|
+
library.books << { :title => 'Another Introduction to Virtus' }
|
242
|
+
```
|
243
|
+
|
244
|
+
A suggested solution to this problem would be to introduce your own class instead of using Array and implement
|
245
|
+
mutation methods that perform coercions. For example:
|
246
|
+
|
247
|
+
``` ruby
|
248
|
+
class Book
|
249
|
+
include Virtus
|
250
|
+
|
251
|
+
attribute :title, String
|
252
|
+
end
|
253
|
+
|
254
|
+
class BookCollection < Array
|
255
|
+
def <<(book)
|
256
|
+
if book.kind_of?(Hash)
|
257
|
+
super(Book.new(book))
|
258
|
+
else
|
259
|
+
super
|
260
|
+
end
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
class Library
|
265
|
+
include Virtus
|
266
|
+
|
267
|
+
attribute :books, BookCollection[Book]
|
268
|
+
end
|
269
|
+
|
270
|
+
library = Library.new
|
271
|
+
library.books << { :title => 'Another Introduction to Virtus' }
|
272
|
+
```
|
273
|
+
|
274
|
+
### Value Objects
|
275
|
+
|
276
|
+
``` ruby
|
277
|
+
class GeoLocation
|
278
|
+
include Virtus::ValueObject
|
279
|
+
|
280
|
+
attribute :latitude, Float
|
281
|
+
attribute :longitude, Float
|
282
|
+
end
|
283
|
+
|
284
|
+
class Venue
|
285
|
+
include Virtus
|
286
|
+
|
287
|
+
attribute :name, String
|
288
|
+
attribute :location, GeoLocation
|
289
|
+
end
|
290
|
+
|
291
|
+
venue = Venue.new(
|
292
|
+
:name => 'Pub',
|
293
|
+
:location => { :latitude => 37.160317, :longitude => -98.437500 })
|
294
|
+
|
295
|
+
venue.location.latitude # => 37.160317
|
296
|
+
venue.location.longitude # => -98.4375
|
297
|
+
|
298
|
+
# Supports object's equality
|
299
|
+
|
300
|
+
venue_other = Venue.new(
|
301
|
+
:name => 'Other Pub',
|
302
|
+
:location => { :latitude => 37.160317, :longitude => -98.437500 })
|
303
|
+
|
304
|
+
venue.location === venue_other.location # => true
|
305
|
+
```
|
306
|
+
|
307
|
+
### Custom Coercions
|
308
|
+
|
309
|
+
``` ruby
|
310
|
+
require 'json'
|
311
|
+
|
312
|
+
# With a custom writer class
|
313
|
+
class JsonWriter < Virtus::Attribute::Writer::Coercible
|
314
|
+
def coerce(value)
|
315
|
+
value.is_a?(Hash) ? value : JSON.parse(value)
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
class User
|
320
|
+
include Virtus
|
321
|
+
|
322
|
+
attribute :info, Hash, :writer_class => JsonWriter
|
323
|
+
end
|
324
|
+
|
325
|
+
user = User.new
|
326
|
+
user.info = '{"email":"john@domain.com"}' # => {"email"=>"john@domain.com"}
|
327
|
+
user.info.class # => Hash
|
328
|
+
|
329
|
+
# With a custom attribute encapsulating coercion-specific configuration
|
330
|
+
class NoisyString < Virtus::Attribute::String
|
331
|
+
class UpperCase < Virtus::Attribute::Writer::Coercible
|
332
|
+
def coerce(value)
|
333
|
+
super.upcase
|
334
|
+
end
|
335
|
+
end
|
336
|
+
|
337
|
+
def self.writer_class(*)
|
338
|
+
UpperCase
|
339
|
+
end
|
340
|
+
end
|
341
|
+
|
342
|
+
class User
|
343
|
+
include Virtus
|
344
|
+
|
345
|
+
attribute :scream, NoisyString
|
346
|
+
end
|
347
|
+
|
348
|
+
user = User.new(:scream => 'hello world!')
|
349
|
+
user.scream # => "HELLO WORLD!"
|
350
|
+
```
|
351
|
+
|
352
|
+
### Private Attributes
|
353
|
+
|
354
|
+
``` ruby
|
355
|
+
class User
|
356
|
+
include Virtus
|
357
|
+
|
358
|
+
attribute :unique_id, String, :writer => :private
|
359
|
+
|
360
|
+
def set_unique_id(id)
|
361
|
+
self.unique_id = id
|
362
|
+
end
|
363
|
+
end
|
364
|
+
|
365
|
+
user = User.new(:unique_id => '1234-1234')
|
366
|
+
user.unique_id # => nil
|
367
|
+
|
368
|
+
user.unique_id = '1234-1234' # => NoMethodError: private method `unique_id='
|
369
|
+
|
370
|
+
user.set_unique_id('1234-1234')
|
371
|
+
user.unique_id # => '1234-1234'
|
372
|
+
```
|
373
|
+
|
374
|
+
Coercions
|
375
|
+
---------
|
376
|
+
|
377
|
+
Virtus uses [Coercible](https://github.com/solnic/coercible) for coercions. This
|
378
|
+
feature is turned on by default. You can turn it off for all attributes like that:
|
379
|
+
|
380
|
+
```ruby
|
381
|
+
# Turn coercions off globally
|
382
|
+
Virtus.coerce(false)
|
383
|
+
|
384
|
+
# ...or you can turn it off for a single attribute
|
385
|
+
class User
|
386
|
+
include Virtus
|
387
|
+
|
388
|
+
attribute :name, String, :coerce => false
|
389
|
+
end
|
390
|
+
```
|
391
|
+
|
392
|
+
You can configure coercers too:
|
393
|
+
|
394
|
+
```ruby
|
395
|
+
Virtus.coercer do |config|
|
396
|
+
config.string.boolean_map = { 'yup' => true, 'nope' => false }
|
397
|
+
end
|
398
|
+
|
399
|
+
# Virtus.coercer instance is used by default for all attributes.
|
400
|
+
# You *can* override it for a single attribute if you want:
|
401
|
+
|
402
|
+
my_cool_coercer = Coercible::Coercer.new do |config|
|
403
|
+
# some customization
|
404
|
+
end
|
405
|
+
|
406
|
+
class User
|
407
|
+
include Virtus
|
408
|
+
|
409
|
+
attribute :name, String, :coercer => my_cool_coercer
|
410
|
+
end
|
411
|
+
```
|
412
|
+
|
413
|
+
## Building modules with custom configuration
|
414
|
+
|
415
|
+
You can also build Virtus modules that contain their own configuration.
|
416
|
+
|
417
|
+
```ruby
|
418
|
+
YupNopeBooleans = Virtus.module { |mod|
|
419
|
+
mod.coerce = true
|
420
|
+
mod.string.boolean_map = { 'yup' => true, 'nope' => false }
|
421
|
+
}
|
422
|
+
|
423
|
+
class User
|
424
|
+
include YupNopeBooleans
|
425
|
+
|
426
|
+
attribute :name, String
|
427
|
+
attribute :admin, Boolean
|
428
|
+
end
|
429
|
+
|
430
|
+
# Or just include the module straight away ...
|
431
|
+
class User
|
432
|
+
include Virtus.module { |m| m.coerce = false }
|
433
|
+
|
434
|
+
attribute :name, String
|
435
|
+
attribute :admin, Boolean
|
436
|
+
end
|
437
|
+
```
|
438
|
+
|
439
|
+
## Contributing
|
440
|
+
|
441
|
+
1. Fork it
|
442
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
443
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
444
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
445
|
+
5. Create new Pull Request
|
@@ -0,0 +1,13 @@
|
|
1
|
+
unless defined?(Motion::Project::Config)
|
2
|
+
raise "This file must be required within a RubyMotion project Rakefile."
|
3
|
+
end
|
4
|
+
|
5
|
+
require 'motion_descendants_tracker'
|
6
|
+
require 'motion_coercible'
|
7
|
+
|
8
|
+
require 'motion-require'
|
9
|
+
Motion::Require.all(Dir.glob(File.expand_path('../project/**/*.rb', __FILE__)))
|
10
|
+
|
11
|
+
Motion::Project::App.setup do |app|
|
12
|
+
#configuration
|
13
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module Virtus
|
2
|
+
class Attribute
|
3
|
+
class Accessor
|
4
|
+
|
5
|
+
class Builder
|
6
|
+
|
7
|
+
def self.call(*args)
|
8
|
+
builder = new(*args)
|
9
|
+
builder.accessor
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(name, type, options = {})
|
13
|
+
@name = name
|
14
|
+
@type = type
|
15
|
+
@options = options
|
16
|
+
@primitive = options.fetch(:primitive, ::Object)
|
17
|
+
@visibility = determine_visibility
|
18
|
+
end
|
19
|
+
|
20
|
+
def accessor
|
21
|
+
accessor_class.new(reader, writer)
|
22
|
+
end
|
23
|
+
|
24
|
+
def accessor_class
|
25
|
+
@options[:lazy] ? Accessor::LazyAccessor : Accessor
|
26
|
+
end
|
27
|
+
|
28
|
+
def reader
|
29
|
+
reader_class.new(@name, reader_options)
|
30
|
+
end
|
31
|
+
|
32
|
+
def writer
|
33
|
+
writer_class.new(@name, writer_options)
|
34
|
+
end
|
35
|
+
|
36
|
+
def reader_class
|
37
|
+
@options.fetch(:reader_class) {
|
38
|
+
@type.reader_class(@primitive, @options)
|
39
|
+
}
|
40
|
+
end
|
41
|
+
|
42
|
+
def writer_class
|
43
|
+
@options.fetch(:writer_class) {
|
44
|
+
@type.writer_class(@primitive, @options)
|
45
|
+
}
|
46
|
+
end
|
47
|
+
|
48
|
+
def reader_options
|
49
|
+
@type.reader_options(@options).update(:visibility => @visibility[:reader])
|
50
|
+
end
|
51
|
+
|
52
|
+
def writer_options
|
53
|
+
@type.writer_options(@options).update(:visibility => @visibility[:writer])
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def determine_visibility
|
59
|
+
default_accessor = @options.fetch(:accessor, :public)
|
60
|
+
reader_visibility = @options.fetch(:reader, default_accessor)
|
61
|
+
writer_visibility = @options.fetch(:writer, default_accessor)
|
62
|
+
{ :reader => reader_visibility, :writer => writer_visibility }
|
63
|
+
end
|
64
|
+
|
65
|
+
end # class Builder
|
66
|
+
|
67
|
+
end # class Accessor
|
68
|
+
end # class Attribute
|
69
|
+
end # module Virtus
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Virtus
|
2
|
+
class Attribute
|
3
|
+
class Accessor
|
4
|
+
|
5
|
+
# Lazy accessor provides evaluating default values on first read
|
6
|
+
#
|
7
|
+
# @api private
|
8
|
+
class LazyAccessor < self
|
9
|
+
|
10
|
+
# Read attribute value and set default value if attribute is not set yet
|
11
|
+
#
|
12
|
+
# @see Accessor#get
|
13
|
+
#
|
14
|
+
# @return [Object]
|
15
|
+
#
|
16
|
+
# @api private
|
17
|
+
def get(instance)
|
18
|
+
if instance.instance_variable_defined?(reader.instance_variable_name)
|
19
|
+
super
|
20
|
+
else
|
21
|
+
value = writer.default_value.call(instance, self)
|
22
|
+
writer.call(instance, value)
|
23
|
+
value
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# Return if the accessor is lazy
|
28
|
+
#
|
29
|
+
# @return [TrueClass]
|
30
|
+
#
|
31
|
+
# @api private
|
32
|
+
def lazy?
|
33
|
+
true
|
34
|
+
end
|
35
|
+
end # LazyAccessor
|
36
|
+
|
37
|
+
end # class Accessor
|
38
|
+
end # class Attribute
|
39
|
+
end # module Virtus
|
@@ -0,0 +1,100 @@
|
|
1
|
+
module Virtus
|
2
|
+
class Attribute
|
3
|
+
|
4
|
+
# Accessor object providing reader and writer methods
|
5
|
+
#
|
6
|
+
# @api private
|
7
|
+
class Accessor
|
8
|
+
#include Adamantium::Flat
|
9
|
+
|
10
|
+
# Return reader
|
11
|
+
#
|
12
|
+
# @return [Reader]
|
13
|
+
#
|
14
|
+
# @api private
|
15
|
+
attr_reader :reader
|
16
|
+
|
17
|
+
# Return writer
|
18
|
+
#
|
19
|
+
# @return [Writer]
|
20
|
+
#
|
21
|
+
# @api private
|
22
|
+
attr_reader :writer
|
23
|
+
|
24
|
+
# Build an accessor instance
|
25
|
+
#
|
26
|
+
# @param [Symbol] name
|
27
|
+
#
|
28
|
+
# @param [Class] type
|
29
|
+
#
|
30
|
+
# @param [Hash] options
|
31
|
+
#
|
32
|
+
# @return [Accessor]
|
33
|
+
#
|
34
|
+
# @api private
|
35
|
+
def self.build(*args)
|
36
|
+
Builder.call(*args)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Initialize a new accessor instance
|
40
|
+
#
|
41
|
+
# @param [Reader]
|
42
|
+
#
|
43
|
+
# @param [Writer]
|
44
|
+
#
|
45
|
+
# @return [undefined]
|
46
|
+
#
|
47
|
+
# @api private
|
48
|
+
def initialize(reader, writer)
|
49
|
+
@reader, @writer = reader, writer
|
50
|
+
end
|
51
|
+
|
52
|
+
# Get a variable value from an object
|
53
|
+
#
|
54
|
+
# @return [Object]
|
55
|
+
#
|
56
|
+
# @api private
|
57
|
+
def get(instance)
|
58
|
+
reader.call(instance)
|
59
|
+
end
|
60
|
+
|
61
|
+
# Set a variable on an object
|
62
|
+
#
|
63
|
+
# @return [Object]
|
64
|
+
#
|
65
|
+
# @api private
|
66
|
+
def set(*args)
|
67
|
+
writer.call(*args)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Return if reader method is public
|
71
|
+
#
|
72
|
+
# @return [Boolean]
|
73
|
+
#
|
74
|
+
# @api private
|
75
|
+
def public_reader?
|
76
|
+
reader.public?
|
77
|
+
end
|
78
|
+
|
79
|
+
# Return if writer method is public
|
80
|
+
#
|
81
|
+
# @return [Boolean]
|
82
|
+
#
|
83
|
+
# @api private
|
84
|
+
def public_writer?
|
85
|
+
writer.public?
|
86
|
+
end
|
87
|
+
|
88
|
+
# Return if this accessor is lazy
|
89
|
+
#
|
90
|
+
# @return [FalseClass]
|
91
|
+
#
|
92
|
+
# @api private
|
93
|
+
def lazy?
|
94
|
+
false
|
95
|
+
end
|
96
|
+
|
97
|
+
end # class Accessor
|
98
|
+
|
99
|
+
end # class Attribute
|
100
|
+
end # module Virtus
|