model_attribute 0.0.1 → 2.0.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 +8 -8
- data/.rspec +2 -0
- data/CHANGELOG.md +34 -0
- data/Gemfile +1 -0
- data/Guardfile +47 -0
- data/LICENSE.txt +5 -1
- data/README.md +165 -5
- data/Rakefile +0 -1
- data/lib/model_attribute.rb +198 -1
- data/lib/model_attribute/errors.rb +14 -0
- data/lib/model_attribute/json.rb +27 -0
- data/lib/model_attribute/version.rb +1 -1
- data/model_attribute.gemspec +8 -3
- data/performance_comparison.rb +66 -0
- data/spec/model_attributes_spec.rb +611 -0
- data/spec/spec_helper.rb +92 -0
- metadata +84 -4
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
ZjY2MDhkZDI2ZjA0YjRjM2NkY2E5MTg0NWYxZmJhY2JmZTEyYWUzYg==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
MWRhZjRhOTUzNDQ5MzRhMWU5NTQwOTdkMmJjYmJlMDA1OTJhMzFiMA==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
ZjZmMjY3NzMzM2EyODAyMDM1MTA2NTgxNGRhOWJhNGNmMGJmZTA4NDVmMzJi
|
10
|
+
NWQ0MzEyYTFlZDNjZmI5N2I5Y2FjMDE3NWE0MDIyMDBlNWJmMzNkNzNmMjBh
|
11
|
+
YTQ2Y2MzMjU2ZGI4MzNhODE5NDJkMjEyMmZmNjU3NDUwYjAzNTI=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
ODg1MWVhYjI3N2ZmNDM0MWY1ZGE1NWVmYWM3YzI4ODdiZTEzMGY0MDQ5YjRh
|
14
|
+
YjA3MWNmMjlmYTJmNTU4ZjJjNzk2ZmUwOWRjZjhhMGJkYTUzNGZlZDZmZGNh
|
15
|
+
N2VhZGJjYWE1MzgxYjY4ZDQwNWI4YmE5ZGY0OWNjZjk4YmNjMGI=
|
data/.rspec
ADDED
data/CHANGELOG.md
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# Change Log
|
2
|
+
|
3
|
+
All notable changes to this project will be documented in this file.
|
4
|
+
|
5
|
+
## 2.0.0
|
6
|
+
|
7
|
+
- **Breaking change**: Rename to `ModelAttribute` (no trailing 's') to avoid name
|
8
|
+
clash with another gem.
|
9
|
+
|
10
|
+
## 1.4.0
|
11
|
+
|
12
|
+
- **New method**: #changes_for_json Returns a hash from attribute name to its
|
13
|
+
new value, suitable for serialization to a JSON string. Easily generate the
|
14
|
+
payload to send in an HTTP PUT to a web service.
|
15
|
+
|
16
|
+
- **New attribute type: json** Store an array/hash/etc. built using the basic
|
17
|
+
JSON data types: nil, numeric, string, boolean, hash and array.
|
18
|
+
|
19
|
+
## 1.3.0
|
20
|
+
|
21
|
+
- **Breaking change**: Parsing an integer to a time attribute, the integer is
|
22
|
+
treated as the number of milliseconds since the epoch (not the number of
|
23
|
+
seconds). `attributes_as_json` emits integers for time attributes.
|
24
|
+
|
25
|
+
## 1.2.0
|
26
|
+
|
27
|
+
- **Breaking change**: `attributes_as_json` removed; replaced with
|
28
|
+
`attributes_for_json`. You will have to serialize this yourself:
|
29
|
+
`Oj.dump(attributes_for_json, mode: :strict)`. This allows you to modify the
|
30
|
+
returned hash before serializing it.
|
31
|
+
|
32
|
+
## 1.1.0
|
33
|
+
|
34
|
+
- Initial release
|
data/Gemfile
CHANGED
data/Guardfile
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
# A sample Guardfile
|
2
|
+
# More info at https://github.com/guard/guard#readme
|
3
|
+
|
4
|
+
## Uncomment and set this to only include directories you want to watch
|
5
|
+
# directories %w(app lib config test spec feature)
|
6
|
+
|
7
|
+
## Uncomment to clear the screen before every task
|
8
|
+
# clearing :on
|
9
|
+
|
10
|
+
## Make Guard exit when config is changed so it can be restarted
|
11
|
+
#
|
12
|
+
## Note: if you want Guard to automatically start up again, run guard in a
|
13
|
+
## shell loop, e.g.:
|
14
|
+
#
|
15
|
+
# $ while bundle exec guard; do echo "Restarting Guard..."; done
|
16
|
+
#
|
17
|
+
## Note: if you are using the `directories` clause above and you are not
|
18
|
+
## watching the project directory ('.'), the you will want to move the Guardfile
|
19
|
+
## to a watched dir and symlink it back, e.g.
|
20
|
+
#
|
21
|
+
# $ mkdir config
|
22
|
+
# $ mv Guardfile config/
|
23
|
+
# $ ln -s config/Guardfile .
|
24
|
+
#
|
25
|
+
# and, you'll have to watch "config/Guardfile" instead of "Guardfile"
|
26
|
+
#
|
27
|
+
watch ("Guardfile") do
|
28
|
+
UI.info "Exiting because Guard must be restarted for changes to take effect"
|
29
|
+
exit 0
|
30
|
+
end
|
31
|
+
|
32
|
+
guard :rspec, cmd: "bundle exec rspec --format=Nc --format=documentation" do
|
33
|
+
require "guard/rspec/dsl"
|
34
|
+
dsl = Guard::RSpec::Dsl.new(self)
|
35
|
+
|
36
|
+
# RSpec files
|
37
|
+
rspec = dsl.rspec
|
38
|
+
watch(rspec.spec_helper) { rspec.spec_dir }
|
39
|
+
watch(rspec.spec_support) { rspec.spec_dir }
|
40
|
+
watch(rspec.spec_files)
|
41
|
+
|
42
|
+
# Ruby files
|
43
|
+
ruby = dsl.ruby
|
44
|
+
dsl.watch_spec_files_for(ruby.lib_files)
|
45
|
+
|
46
|
+
watch(%r{lib/*}) { 'spec' }
|
47
|
+
end
|
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,170 @@
|
|
1
1
|
# ModelAttribute
|
2
2
|
|
3
|
-
|
3
|
+
Simple attributes for a non-ActiveRecord model.
|
4
|
+
|
5
|
+
- Stores attributes in instance variables.
|
6
|
+
- Type casting and checking.
|
7
|
+
- Dirty tracking.
|
8
|
+
- List attribute names and values.
|
9
|
+
- Handles integers, booleans, strings and times - a set of types that are very
|
10
|
+
easy to persist to and parse from JSON.
|
11
|
+
- Supports efficient serialization of attributes to JSON.
|
12
|
+
- Mass assignment - handy for initializers.
|
13
|
+
|
14
|
+
Why not [Virtus][virtus-gem]? Virtus doesn't provide attribute tracking, and
|
15
|
+
doesn't integrate with [ActiveModel::Dirty][am-dirty]. So if you're not using
|
16
|
+
ActiveRecord, but you need attributes with dirty tracking, ModelAttribute may be
|
17
|
+
what you're after. For example, it works very well for a model that fronts an
|
18
|
+
HTTP web service, and you want dirty tracking so you can PATCH appropriately.
|
19
|
+
|
20
|
+
Also in favor of ModelAttribute:
|
21
|
+
|
22
|
+
- It's simple - less than [200 lines of code][source].
|
23
|
+
- It supports efficient serialization and deserialization to/from JSON.
|
24
|
+
|
25
|
+
[virtus-gem]:https://github.com/solnic/virtus
|
26
|
+
[am-dirty]:https://github.com/rails/rails/blob/v3.0.20/activemodel/lib/active_model/dirty.rb
|
27
|
+
[source]:https://github.com/yammer/model_attribute/blob/master/lib/model_attribute.rb
|
28
|
+
|
29
|
+
## Usage
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
require 'model_attribute'
|
33
|
+
class User
|
34
|
+
extend ModelAttribute
|
35
|
+
attribute :id, :integer
|
36
|
+
attribute :paid, :boolean
|
37
|
+
attribute :name, :string
|
38
|
+
attribute :created_at, :time
|
39
|
+
attribute :grades, :json
|
40
|
+
|
41
|
+
def initialize(attributes = {})
|
42
|
+
set_attributes(attributes)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
User.attributes # => [:id, :paid, :name, :created_at, :grades]
|
47
|
+
user = User.new
|
48
|
+
|
49
|
+
user.attributes # => {:id=>nil, :paid=>nil, :name=>nil, :created_at=>nil, :grades=>nil}
|
50
|
+
|
51
|
+
# An integer attribute
|
52
|
+
user.id # => nil
|
53
|
+
|
54
|
+
user.id = 3
|
55
|
+
user.id # => 3
|
56
|
+
|
57
|
+
# Stores values that convert cleanly to an integer
|
58
|
+
user.id = '5'
|
59
|
+
user.id # => 5
|
60
|
+
|
61
|
+
# Protects you against nonsense assignment
|
62
|
+
user.id = '5error'
|
63
|
+
ArgumentError: invalid value for Integer(): "5error"
|
64
|
+
|
65
|
+
# A boolean attribute
|
66
|
+
user.paid # => nil
|
67
|
+
user.paid = true
|
68
|
+
|
69
|
+
# Booleans also define a predicate method (ending in '?')
|
70
|
+
user.paid? # => true
|
71
|
+
|
72
|
+
# Conversion from strings used by databases.
|
73
|
+
user.paid = 'f'
|
74
|
+
user.paid # => false
|
75
|
+
user.paid = 't'
|
76
|
+
user.paid # => true
|
77
|
+
|
78
|
+
# A :time attribute
|
79
|
+
user.created_at = Time.now
|
80
|
+
user.created_at # => 2015-01-08 15:57:05 +0000
|
81
|
+
|
82
|
+
# Also converts from other reasonable time formats
|
83
|
+
user.created_at = "2014-12-25 14:00:00 +0100"
|
84
|
+
user.created_at # => 2014-12-25 13:00:00 +0000
|
85
|
+
user.created_at = Date.parse('2014-01-08')
|
86
|
+
user.created_at # => 2014-01-08 00:00:00 +0000
|
87
|
+
user.created_at = DateTime.parse("2014-12-25 13:00:45")
|
88
|
+
user.created_at # => 2014-12-25 13:00:45 +0000
|
89
|
+
# Convert from seconds since the epoch
|
90
|
+
user.created_at = Time.now.to_f
|
91
|
+
user.created_at # => 2015-01-08 16:23:02 +0000
|
92
|
+
# Or milliseconds since the epoch
|
93
|
+
user.created_at = 1420734182000
|
94
|
+
user.created_at # => 2015-01-08 16:23:02 +0000
|
95
|
+
|
96
|
+
# A :json attribute is schemaless and accepts the basic JSON types - hash,
|
97
|
+
# array, nil, numeric, string and boolean.
|
98
|
+
user.grades = {'maths' => 'A', 'history' => 'C'}
|
99
|
+
user.grades # => {"maths"=>"A", "history"=>"C"}
|
100
|
+
user.grades = ['A', 'A*', 'C']
|
101
|
+
user.grades # => ["A", "A*", "C"]
|
102
|
+
user.grades = 'AAB'
|
103
|
+
user.grades # => "AAB"
|
104
|
+
user.grades = Time.now
|
105
|
+
# => RuntimeError: JSON only supports nil, numeric, string, boolean and arrays and hashes of those.
|
106
|
+
|
107
|
+
# read_attribute and write_attribute methods
|
108
|
+
user.read_attribute(:created_at)
|
109
|
+
user.write_attribute(:name, 'Fred')
|
110
|
+
|
111
|
+
# View attributes
|
112
|
+
user.attributes # => {:id=>5, :paid=>true, :name=>"Fred", :created_at=>2015-01-08 15:57:05 +0000, :grades=>{"maths"=>"A", "history"=>"C"}}
|
113
|
+
user.inspect # => "#<User id: 5, paid: true, name: \"Fred\", created_at: 2015-01-08 15:57:05 +0000, grades: {\"maths\"=>\"A\", \"history\"=>\"C\"}>"
|
114
|
+
|
115
|
+
# Mass assignment
|
116
|
+
user.set_attributes(name: "Sally", paid: false)
|
117
|
+
user.attributes # => {:id=>5, :paid=>false, :name=>"Sally", :created_at=>2015-01-08 15:57:05 +0000}
|
118
|
+
|
119
|
+
# Efficient JSON serialization and deserialization.
|
120
|
+
# Attributes with nil values are omitted.
|
121
|
+
user.attributes_for_json
|
122
|
+
# => {"id"=>5, "paid"=>true, "name"=>"Fred", "created_at"=>1421171317762}
|
123
|
+
require 'oj'
|
124
|
+
Oj.dump(user.attributes_for_json, mode: :strict)
|
125
|
+
# => "{\"id\":5,\"paid\":true,\"name\":\"Fred\",\"created_at\":1421171317762}"
|
126
|
+
user2 = User.new(Oj.load(json, strict: true))
|
127
|
+
|
128
|
+
# Change tracking. A much smaller set of function than that provided by
|
129
|
+
# ActiveModel::Dity.
|
130
|
+
user.changes # => {:id=>[nil, 5], :paid=>[nil, true], :created_at=>[nil, 2015-01-08 15:57:05 +0000], :name=>[nil, "Fred"]}
|
131
|
+
user.name_changed? # => true
|
132
|
+
# If you need the new values to send as a PUT to a web service
|
133
|
+
user.changes_for_json # => {"id"=>5, "paid"=>true, "name"=>"Fred", "created_at"=>1421171317762}
|
134
|
+
# If you're imitating ActiveRecord behaviour, changes are cleared after
|
135
|
+
# after_save callbacks, but before after_commit callbacks.
|
136
|
+
user.changes.clear
|
137
|
+
user.changes # => {}
|
138
|
+
|
139
|
+
# Equality of all the attribute values match
|
140
|
+
another = User.new
|
141
|
+
another.id = 5
|
142
|
+
another.paid = true
|
143
|
+
another.created_at = user.created_at
|
144
|
+
another.name = 'Fred'
|
145
|
+
|
146
|
+
user == another # => true
|
147
|
+
user === another # => true
|
148
|
+
user.eql? another # => true
|
149
|
+
|
150
|
+
# Making some attributes private
|
151
|
+
|
152
|
+
class User
|
153
|
+
extend ModelAttribute
|
154
|
+
attribute :events, :string
|
155
|
+
private :events=
|
156
|
+
|
157
|
+
def initialize(attributes)
|
158
|
+
# Pass flag to set_attributes to allow setting attributes with private writers
|
159
|
+
set_attributes(attributes, true)
|
160
|
+
end
|
161
|
+
|
162
|
+
def add_event(new_event)
|
163
|
+
events ||= ""
|
164
|
+
events += new_event
|
165
|
+
end
|
166
|
+
end
|
167
|
+
```
|
4
168
|
|
5
169
|
## Installation
|
6
170
|
|
@@ -18,10 +182,6 @@ Or install it yourself as:
|
|
18
182
|
|
19
183
|
$ gem install model_attribute
|
20
184
|
|
21
|
-
## Usage
|
22
|
-
|
23
|
-
TODO: Write usage instructions here
|
24
|
-
|
25
185
|
## Contributing
|
26
186
|
|
27
187
|
1. Fork it ( https://github.com/[my-github-username]/model_attribute/fork )
|
data/Rakefile
CHANGED
data/lib/model_attribute.rb
CHANGED
@@ -1,5 +1,202 @@
|
|
1
1
|
require "model_attribute/version"
|
2
|
+
require "model_attribute/json"
|
3
|
+
require "model_attribute/errors"
|
4
|
+
require "time"
|
2
5
|
|
3
6
|
module ModelAttribute
|
4
|
-
|
7
|
+
SUPPORTED_TYPES = [:integer, :boolean, :string, :time, :json]
|
8
|
+
|
9
|
+
def self.extended(base)
|
10
|
+
base.send(:include, InstanceMethods)
|
11
|
+
base.instance_variable_set('@attribute_names', [])
|
12
|
+
base.instance_variable_set('@attribute_types', {})
|
13
|
+
end
|
14
|
+
|
15
|
+
def attribute(name, type)
|
16
|
+
name = name.to_sym
|
17
|
+
type = type.to_sym
|
18
|
+
raise UnsupportedTypeError.new(type) unless SUPPORTED_TYPES.include?(type)
|
19
|
+
|
20
|
+
@attribute_names << name
|
21
|
+
@attribute_types[name] = type
|
22
|
+
|
23
|
+
self.class_eval(<<-CODE, __FILE__, __LINE__ + 1)
|
24
|
+
def #{name}=(value)
|
25
|
+
write_attribute(#{name.inspect}, value, #{type.inspect})
|
26
|
+
end
|
27
|
+
|
28
|
+
def #{name}
|
29
|
+
read_attribute(#{name.inspect})
|
30
|
+
end
|
31
|
+
|
32
|
+
def #{name}_changed?
|
33
|
+
!!changes[#{name.inspect}]
|
34
|
+
end
|
35
|
+
CODE
|
36
|
+
|
37
|
+
if type == :boolean
|
38
|
+
self.class_eval(<<-CODE, __FILE__, __LINE__ + 1)
|
39
|
+
def #{name}?
|
40
|
+
!!read_attribute(#{name.inspect})
|
41
|
+
end
|
42
|
+
CODE
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def attributes
|
47
|
+
@attribute_names
|
48
|
+
end
|
49
|
+
|
50
|
+
module InstanceMethods
|
51
|
+
def write_attribute(name, value, type = nil)
|
52
|
+
name = name.to_sym
|
53
|
+
|
54
|
+
# Don't want to expose attribute types as a method on the class, so access
|
55
|
+
# via a back door.
|
56
|
+
type ||= self.class.instance_variable_get('@attribute_types')[name]
|
57
|
+
raise InvalidAttributeNameError.new(name) unless type
|
58
|
+
|
59
|
+
value = cast(value, type)
|
60
|
+
return if value == read_attribute(name)
|
61
|
+
|
62
|
+
if changes.has_key? name
|
63
|
+
original = changes[name].first
|
64
|
+
else
|
65
|
+
original = read_attribute(name)
|
66
|
+
end
|
67
|
+
|
68
|
+
if original == value
|
69
|
+
changes.delete(name)
|
70
|
+
else
|
71
|
+
changes[name] = [original, value]
|
72
|
+
end
|
73
|
+
|
74
|
+
instance_variable_set("@#{name}", value)
|
75
|
+
end
|
76
|
+
|
77
|
+
def read_attribute(name)
|
78
|
+
ivar_name = "@#{name}"
|
79
|
+
if instance_variable_defined?(ivar_name)
|
80
|
+
instance_variable_get(ivar_name)
|
81
|
+
elsif !self.class.attributes.include?(name.to_sym)
|
82
|
+
raise InvalidAttributeNameError.new(name)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def attributes
|
87
|
+
self.class.attributes.each_with_object({}) do |name, attributes|
|
88
|
+
attributes[name] = read_attribute(name)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def set_attributes(attributes, can_set_private_attrs = false)
|
93
|
+
attributes.each do |key, value|
|
94
|
+
send("#{key}=", value) if respond_to?("#{key}=", can_set_private_attrs)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def ==(other)
|
99
|
+
return true if equal?(other)
|
100
|
+
if respond_to?(:id)
|
101
|
+
other.kind_of?(self.class) && id == other.id
|
102
|
+
else
|
103
|
+
other.kind_of?(self.class) && attributes == other.attributes
|
104
|
+
end
|
105
|
+
end
|
106
|
+
alias_method :eql?, :==
|
107
|
+
|
108
|
+
def changes
|
109
|
+
@changes ||= {} #HashWithIndifferentAccess.new
|
110
|
+
end
|
111
|
+
|
112
|
+
# Attributes suitable for serializing to a JSON string.
|
113
|
+
#
|
114
|
+
# - Attribute keys are strings (for 'strict' JSON dumping).
|
115
|
+
# - Attributes with a nil value are omitted to speed serialization.
|
116
|
+
# - :time attributes are serialized as an Integer giving the number of
|
117
|
+
# milliseconds since the epoch.
|
118
|
+
def attributes_for_json
|
119
|
+
self.class.attributes.each_with_object({}) do |name, attributes|
|
120
|
+
value = read_attribute(name)
|
121
|
+
unless value.nil?
|
122
|
+
value = (value.to_f * 1000).to_i if value.is_a? Time
|
123
|
+
attributes[name.to_s] = value
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
# Changed attributes suitable for serializing to a JSON string. Returns a
|
129
|
+
# hash from attribute name (as a string) to the new value of that attribute,
|
130
|
+
# for attributes that have changed.
|
131
|
+
#
|
132
|
+
# - :time attributes are serialized as an Integer giving the number of
|
133
|
+
# milliseconds since the epoch.
|
134
|
+
# - Unlike attributes_for_json, attributes that have changed to a nil value
|
135
|
+
# *are* included.
|
136
|
+
def changes_for_json
|
137
|
+
hash = {}
|
138
|
+
changes.each do |attr_name, (_old_value, new_value)|
|
139
|
+
new_value = (new_value.to_f * 1000).to_i if new_value.is_a? Time
|
140
|
+
hash[attr_name.to_s] = new_value
|
141
|
+
end
|
142
|
+
|
143
|
+
hash
|
144
|
+
end
|
145
|
+
|
146
|
+
# Includes the class name and all the attributes and their values. e.g.
|
147
|
+
# "#<User id: 1, paid: true, name: \"Fred\", created_at: 2014-12-25 08:00:00 +0000>"
|
148
|
+
def inspect
|
149
|
+
attribute_string = self.class.attributes.map do |key|
|
150
|
+
"#{key}: #{read_attribute(key).inspect}"
|
151
|
+
end.join(', ')
|
152
|
+
"#<#{self.class} #{attribute_string}>"
|
153
|
+
end
|
154
|
+
|
155
|
+
def cast(value, type)
|
156
|
+
return nil if value.nil?
|
157
|
+
|
158
|
+
case type
|
159
|
+
when :integer
|
160
|
+
int = Integer(value)
|
161
|
+
float = Float(value)
|
162
|
+
raise "Can't cast #{value.inspect} to an integer without loss of precision" unless int == float
|
163
|
+
int
|
164
|
+
when :boolean
|
165
|
+
if !!value == value
|
166
|
+
value
|
167
|
+
elsif value == 't'
|
168
|
+
true
|
169
|
+
elsif value == 'f'
|
170
|
+
false
|
171
|
+
else
|
172
|
+
raise "Can't cast #{value.inspect} to boolean"
|
173
|
+
end
|
174
|
+
when :time
|
175
|
+
case value
|
176
|
+
when Time
|
177
|
+
value
|
178
|
+
when Date, DateTime
|
179
|
+
value.to_time
|
180
|
+
when Integer
|
181
|
+
# Assume milliseconds since epoch.
|
182
|
+
Time.at(value / 1000.0)
|
183
|
+
when Numeric
|
184
|
+
# Numeric, but not an integer. Assume seconds since epoch.
|
185
|
+
Time.at(value)
|
186
|
+
else
|
187
|
+
Time.parse(value)
|
188
|
+
end
|
189
|
+
when :string
|
190
|
+
String(value)
|
191
|
+
when :json
|
192
|
+
if Json.valid?(value)
|
193
|
+
value
|
194
|
+
else
|
195
|
+
raise "JSON only supports nil, numeric, string, boolean and arrays and hashes of those."
|
196
|
+
end
|
197
|
+
else
|
198
|
+
raise UnsupportedTypeError.new(type)
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
5
202
|
end
|