couchpillow 0.3.10 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.markdown +55 -36
- data/lib/couchpillow/attribute.rb +153 -0
- data/lib/couchpillow/attributive.rb +37 -0
- data/lib/couchpillow/boolean.rb +8 -0
- data/lib/couchpillow/document.rb +70 -192
- data/lib/couchpillow/validation_error.rb +8 -0
- data/lib/couchpillow/version.rb +1 -1
- data/lib/couchpillow.rb +4 -0
- data/test/helper.rb +4 -0
- data/test/test_attribute.rb +122 -0
- data/test/test_document.rb +128 -208
- metadata +8 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cb55c110b2b546a414fc253680530a0be2d4525b
|
4
|
+
data.tar.gz: 8aee488a67a464972655088b9976777f4a7649ae
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d30b27dc47e70736de216834c2c93721dc536809f90adb06942daedf5ab6d98da0c04c438153a6defe3ecca3737c7599d96279f88335162d7ac831aab71b5c85
|
7
|
+
data.tar.gz: 1c3e5a11b8098a3038e00026a3fc9c97060aaa4f6e50c189c9d4988f46fe689d9a3d29a37d5bcf320342122300b8278a7dcf3c22278796983a5bd5c1b0073967
|
data/README.markdown
CHANGED
@@ -30,89 +30,108 @@ long as they `respond_to?` the `set`, `delete`, `replace`, and `get` methods.
|
|
30
30
|
|
31
31
|
require 'couchpillow'
|
32
32
|
|
33
|
+
class MyDocument < CouchPillow::Document
|
34
|
+
type :my_document
|
35
|
+
attribute(:stuff)
|
36
|
+
end
|
37
|
+
|
33
38
|
CouchPillow.db = Couchbase.connect( bucket: 'default', host: 'localhost' )
|
34
|
-
doc = CouchPillow::Document.new( { :stuff => 'hello' }
|
39
|
+
doc = CouchPillow::Document.new( { :stuff => 'hello' } )
|
35
40
|
doc.save!
|
36
41
|
|
37
42
|
# {
|
38
|
-
# '
|
43
|
+
# '_id': 'my_document_fb579b265cc005c47ff420a5c2a15d2b',
|
44
|
+
# '_type': 'my_document',
|
39
45
|
# 'stuff': 'hello',
|
40
|
-
# '
|
41
|
-
# '
|
46
|
+
# '_created_at': '2014-07-04 00:00:00 UTC'
|
47
|
+
# '_updated_at': '2014-07-04 00:00:00 UTC'
|
42
48
|
# }
|
43
49
|
|
44
50
|
|
45
51
|
Retrieving Documents:
|
46
52
|
|
47
|
-
doc =
|
53
|
+
doc = MyDocument.get('123')
|
48
54
|
doc.stuff # 'hello'
|
49
|
-
|
50
55
|
|
51
|
-
|
56
|
+
Specifying custom id:
|
52
57
|
|
53
58
|
class User < CouchPillow::Document
|
54
59
|
type :user
|
60
|
+
attribute(:email)
|
55
61
|
end
|
56
62
|
|
57
63
|
CouchPillow.db = Couchbase.connect( bucket: 'default', host: 'localhost' )
|
58
|
-
doc = User.new( { :email => 'john@email.com' } )
|
64
|
+
doc = User.new( { :email => 'john@email.com' }, '123' )
|
59
65
|
doc.email # 'john@email.com'
|
60
66
|
doc.save!
|
61
67
|
|
62
68
|
# {
|
63
|
-
# '_id': '
|
69
|
+
# '_id': '123',
|
64
70
|
# '_type': 'user',
|
65
71
|
# 'email': 'john@email.com',
|
66
72
|
# 'created_at': '2014-07-04 00:00:00 UTC'
|
67
73
|
# 'updated_at': '2014-07-04 00:00:00 UTC'
|
68
74
|
# }
|
69
75
|
|
70
|
-
|
76
|
+
### Attributes
|
77
|
+
|
78
|
+
Using Attribute Directives:
|
71
79
|
|
72
80
|
class User < CouchPillow::Document
|
73
81
|
type :user
|
74
|
-
|
75
|
-
|
82
|
+
attribute(:email)
|
83
|
+
.required
|
84
|
+
|
85
|
+
attribute(:first_name)
|
86
|
+
.type(String)
|
76
87
|
end
|
77
88
|
|
78
89
|
CouchPillow.db = Couchbase.connect( bucket: 'default', host: 'localhost' )
|
79
90
|
doc = User.new( { :first_name => 'John' } )
|
80
|
-
doc.save! # raises ValidationError
|
91
|
+
doc.save! # raises ValidationError "Attribute 'email' is missing"
|
81
92
|
doc.email = 'john@email.com'
|
82
93
|
doc.save! # Success!
|
83
94
|
|
84
|
-
|
95
|
+
List of Attribute Directives:
|
85
96
|
|
86
|
-
|
87
|
-
type :user
|
88
|
-
validate :phone, 'is not a number', lambda { |v| v.is_a? Numeric }
|
89
|
-
end
|
97
|
+
* `required`
|
90
98
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
doc.phone = 123
|
96
|
-
doc.save! # Success!
|
99
|
+
Sets this attribute as required. Will raise an error if attribute is missing
|
100
|
+
upon `save!` or `update!`
|
101
|
+
|
102
|
+
* `type(T)`
|
97
103
|
|
98
|
-
|
104
|
+
Sets the type of this attribute. Will perform type check if specified.
|
105
|
+
|
106
|
+
* `auto_convert`
|
107
|
+
|
108
|
+
Enables auto-conversion to the specified type. This gets ignored if `type`
|
109
|
+
directive is not specified.
|
110
|
+
|
111
|
+
* `default(&block)`
|
112
|
+
|
113
|
+
Runs the block to set the default value for this attribute, if it's missing
|
114
|
+
or nil.
|
115
|
+
|
116
|
+
* `content(&block)`
|
117
|
+
|
118
|
+
Custom validation method to check the value of the attribute. This is useful
|
119
|
+
in cases where you only want certain values to be stored (e.g a number between
|
120
|
+
1-10 only)
|
121
|
+
|
122
|
+
|
123
|
+
### Migration
|
124
|
+
|
125
|
+
Using `rename` to rename keys. Useful to maintain document integrity
|
126
|
+
from a migration.
|
99
127
|
|
100
128
|
class User < CouchPillow::Document
|
101
129
|
rename :username, :nickname
|
130
|
+
attribute(:nickname)
|
102
131
|
end
|
103
132
|
u = User.new( { :username => 'jdoe' } )
|
104
133
|
u.nickname # 'jdoe'
|
105
134
|
|
106
|
-
Using `whitelist` to whitelist keys. Also useful to maintain document integrity
|
107
|
-
and keeping other fields from being saved. Keep in mind that when loading
|
108
|
-
existing documents and if they have keys that are not listed on the whitelist,
|
109
|
-
those keys will be dropped upon `save!`.
|
110
|
-
|
111
|
-
class User < CouchPillow::Document
|
112
|
-
whitelist :name
|
113
|
-
end
|
114
|
-
u = User.new( { :age => 10, :name => 'John' } )
|
115
|
-
u.has?(:age) # false
|
116
135
|
|
117
136
|
|
118
137
|
## Design Docs and Views
|
@@ -123,5 +142,5 @@ that returns documents as values, you can easily use it like this:
|
|
123
142
|
|
124
143
|
CouchPillow.db.design_docs['my_design_doc'].
|
125
144
|
by_email(:body => { :key => 'john@email.com' }).map do |v|
|
126
|
-
new User(v.
|
145
|
+
new User(v.doc, v.id)
|
127
146
|
end
|
@@ -0,0 +1,153 @@
|
|
1
|
+
module CouchPillow
|
2
|
+
|
3
|
+
class Attribute
|
4
|
+
|
5
|
+
attr_reader :name
|
6
|
+
|
7
|
+
@required = false
|
8
|
+
@type = nil
|
9
|
+
@auto_convert = false
|
10
|
+
@default_block = nil
|
11
|
+
@check_value_message = nil
|
12
|
+
@check_value_block = nil
|
13
|
+
|
14
|
+
|
15
|
+
def initialize name
|
16
|
+
@name = name.to_s.to_sym
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
# Directive to mark this Attribute as required.
|
21
|
+
#
|
22
|
+
def required
|
23
|
+
@required = true
|
24
|
+
self
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
def required?
|
29
|
+
@required
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
# Directive to enforce the data type of this Attribute.
|
34
|
+
#
|
35
|
+
def type t
|
36
|
+
@type = t
|
37
|
+
self
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
# Attempts to auto convert values to the type specified by the {type}
|
42
|
+
# directive if the value is not of the same type.
|
43
|
+
# Has no effect if {type} is not specified.
|
44
|
+
#
|
45
|
+
def auto_convert
|
46
|
+
@auto_convert = true
|
47
|
+
self
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
# Directive to set the default value of this Attribute. Once specified, if
|
52
|
+
# this Attribute does not exist during the Document initialization, whether
|
53
|
+
# that's from {Document.get} or {Document#initialize}, the value of the
|
54
|
+
# Attribute will be set to the value returned by the block.
|
55
|
+
#
|
56
|
+
# @yield Sets the value of this Attribute to the value returned by the
|
57
|
+
# block.
|
58
|
+
#
|
59
|
+
def default &block
|
60
|
+
@default_block = block
|
61
|
+
self
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
def has_default?
|
66
|
+
@default_block != nil
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
# Directive to perform a validation over the value of this Attribute.
|
71
|
+
#
|
72
|
+
# @param message Message when block passed fails. Optional.
|
73
|
+
# @yield [v] Value of the Attribute.
|
74
|
+
#
|
75
|
+
# @example
|
76
|
+
# content("Name must be John") { |v| v == "John" }
|
77
|
+
# content { |v| v == "John" }
|
78
|
+
#
|
79
|
+
def content message = nil, &block
|
80
|
+
@check_value_message = message
|
81
|
+
@check_value_block = block
|
82
|
+
self
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
# Check the default value.
|
87
|
+
#
|
88
|
+
# @param value The value of this attribute to validate.
|
89
|
+
#
|
90
|
+
def trigger_default_directive
|
91
|
+
@default_block.call if has_default?
|
92
|
+
end
|
93
|
+
|
94
|
+
|
95
|
+
# Check value.
|
96
|
+
#
|
97
|
+
def trigger_content_directive value
|
98
|
+
if @check_value_block
|
99
|
+
raise ValidationError, @check_value_message unless
|
100
|
+
@check_value_block.call(value)
|
101
|
+
end
|
102
|
+
value
|
103
|
+
end
|
104
|
+
|
105
|
+
|
106
|
+
# Check type.
|
107
|
+
#
|
108
|
+
def trigger_type_directive value
|
109
|
+
if @type
|
110
|
+
# Run auto-conversion first.
|
111
|
+
if @auto_convert
|
112
|
+
if @type == Integer
|
113
|
+
value = Integer(value)
|
114
|
+
elsif @type == Float
|
115
|
+
value = Float(value)
|
116
|
+
elsif @type == String
|
117
|
+
value = String(value)
|
118
|
+
elsif @type == Array
|
119
|
+
value = Array(value)
|
120
|
+
elsif @type == Time && !value.is_a?(Time)
|
121
|
+
value = Time.parse(value)
|
122
|
+
elsif @type == CouchPillow::Boolean
|
123
|
+
value = value == 0 || value.to_s.downcase == "false" || !value ? false : true
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
if @type == CouchPillow::Boolean
|
128
|
+
raise ValidationError unless !!value == value
|
129
|
+
else
|
130
|
+
raise ValidationError unless value.is_a?(@type)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
value
|
135
|
+
end
|
136
|
+
|
137
|
+
|
138
|
+
# Run the validation directives, except required directive.
|
139
|
+
# First it executes the {default} directive, then {auto_convert} to type,
|
140
|
+
# then {type} validation, then finally the {content} directive.
|
141
|
+
#
|
142
|
+
# @return The final value after the validation.
|
143
|
+
#
|
144
|
+
def validate value
|
145
|
+
value = trigger_default_directive if value.nil?
|
146
|
+
trigger_content_directive(trigger_type_directive(value))
|
147
|
+
end
|
148
|
+
|
149
|
+
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module CouchPillow
|
2
|
+
|
3
|
+
module Attributive
|
4
|
+
|
5
|
+
# Declares a new Attribute
|
6
|
+
#
|
7
|
+
def attribute attr
|
8
|
+
attr = attr.to_s.to_sym
|
9
|
+
new_attr = Attribute.new(attr)
|
10
|
+
attributes[attr] = new_attr
|
11
|
+
|
12
|
+
# Define accessor methods
|
13
|
+
define_method(attr) do
|
14
|
+
@data[attr]
|
15
|
+
end
|
16
|
+
define_method("#{attr}=") do |val|
|
17
|
+
@data[attr] = val
|
18
|
+
end
|
19
|
+
|
20
|
+
new_attr
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
def attributes
|
25
|
+
@attributes ||= {}
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
def inherited(subclass)
|
30
|
+
# Copy existing attributes to subclasses
|
31
|
+
attributes.each do |k, v|
|
32
|
+
subclass.attributes[k] = v
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
data/lib/couchpillow/document.rb
CHANGED
@@ -2,45 +2,36 @@ module CouchPillow
|
|
2
2
|
|
3
3
|
class Document
|
4
4
|
|
5
|
-
|
6
|
-
#
|
7
|
-
class ValidationError < StandardError; end
|
8
|
-
|
9
|
-
|
10
|
-
# Faux Boolean class used for type checking and conversion.
|
11
|
-
#
|
12
|
-
class Boolean; end
|
13
|
-
|
5
|
+
extend Attributive
|
14
6
|
|
15
7
|
attr_reader :id
|
16
8
|
|
9
|
+
RESERVED_KEYS = %i[_id _type _created_at _updated_at]
|
17
10
|
|
18
|
-
|
11
|
+
DEFAULT_TYPE = "default".freeze
|
19
12
|
|
20
13
|
|
21
|
-
|
14
|
+
attribute(:_created_at)
|
15
|
+
.required
|
16
|
+
.type(Time).auto_convert
|
17
|
+
.default { Time.now.utc }
|
22
18
|
|
23
|
-
|
19
|
+
attribute(:_updated_at)
|
20
|
+
.required
|
21
|
+
.type(Time).auto_convert
|
22
|
+
.default { Time.now.utc }
|
24
23
|
|
25
24
|
|
26
|
-
def initialize hash = {}, id = SecureRandom.hex
|
25
|
+
def initialize hash = {}, id = "#{self.class._type}_#{SecureRandom.hex}"
|
27
26
|
@data = self.class.symbolize(hash)
|
28
|
-
@id = id
|
29
27
|
|
30
|
-
@
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
@data[:updated_at] and
|
36
|
-
@data[:updated_at].is_a? String and
|
37
|
-
@data[:updated_at] = Time.parse(@data[:updated_at])
|
28
|
+
@id = id
|
29
|
+
time = Time.now.utc
|
30
|
+
@data[:_created_at] ||= time
|
31
|
+
@data[:_updated_at] = time
|
38
32
|
|
39
|
-
raise TypeError if @data[:_type] && @data[:_type] != self.class._type
|
40
|
-
@data[:_type] = self.class._type
|
41
33
|
rename!
|
42
34
|
whitelist!
|
43
|
-
ensure_types!
|
44
35
|
end
|
45
36
|
|
46
37
|
|
@@ -54,38 +45,10 @@ module CouchPillow
|
|
54
45
|
end
|
55
46
|
|
56
47
|
|
57
|
-
# @private
|
58
|
-
# Map hash keys to methods
|
59
|
-
#
|
60
|
-
def method_missing m, *args, &block
|
61
|
-
ms = m.to_s
|
62
|
-
if ms.end_with?("=")
|
63
|
-
ms.gsub!('=', '')
|
64
|
-
@data[ms.to_sym] = args[0]
|
65
|
-
else
|
66
|
-
if @data.has_key?(m)
|
67
|
-
@data[m]
|
68
|
-
else
|
69
|
-
super
|
70
|
-
end
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
|
75
|
-
# @private
|
76
|
-
#
|
77
|
-
def respond_to? m
|
78
|
-
ms = m.to_s
|
79
|
-
return true if ms.end_with?("=")
|
80
|
-
@data.has_key?(m) or
|
81
|
-
super
|
82
|
-
end
|
83
|
-
|
84
|
-
|
85
48
|
# @private
|
86
49
|
#
|
87
50
|
def timestamp!
|
88
|
-
@data[:
|
51
|
+
@data[:_updated_at] = Time.now.utc
|
89
52
|
end
|
90
53
|
|
91
54
|
|
@@ -93,10 +56,13 @@ module CouchPillow
|
|
93
56
|
#
|
94
57
|
def save!
|
95
58
|
whitelist!
|
96
|
-
validate
|
59
|
+
validate!
|
97
60
|
sort!
|
98
61
|
timestamp!
|
99
|
-
|
62
|
+
to_save = @data.merge({
|
63
|
+
:_type => self.class._type
|
64
|
+
})
|
65
|
+
CouchPillow.db.set @id, to_save
|
100
66
|
end
|
101
67
|
|
102
68
|
|
@@ -107,34 +73,32 @@ module CouchPillow
|
|
107
73
|
end
|
108
74
|
|
109
75
|
|
110
|
-
# Sort keys on this document.
|
111
|
-
#
|
112
|
-
def sort!
|
113
|
-
@data = @data.sort.to_h
|
114
|
-
end
|
115
|
-
|
116
|
-
|
117
76
|
# Attempt to update this Document. Fails if this Document does not yet
|
118
77
|
# exist in the database.
|
119
78
|
#
|
120
79
|
def update!
|
121
80
|
whitelist!
|
122
|
-
validate
|
81
|
+
validate!
|
123
82
|
sort!
|
124
83
|
timestamp!
|
125
|
-
|
84
|
+
to_save = @data.merge({
|
85
|
+
:_type => self.class._type
|
86
|
+
})
|
87
|
+
CouchPillow.db.replace @id, to_save
|
126
88
|
end
|
127
89
|
|
128
90
|
|
129
91
|
# Updates the attributes in the document.
|
130
92
|
# Existing attributes will be overwritten and new ones will be added.
|
131
|
-
#
|
93
|
+
# Any other existing attributes that are not present in the hash will be ignored.
|
132
94
|
#
|
133
95
|
def update hash
|
134
96
|
hash.each do |k,v|
|
135
97
|
@data[k.to_sym] = v
|
136
98
|
end
|
99
|
+
rename!
|
137
100
|
whitelist!
|
101
|
+
validate!
|
138
102
|
end
|
139
103
|
|
140
104
|
|
@@ -149,15 +113,7 @@ module CouchPillow
|
|
149
113
|
|
150
114
|
|
151
115
|
def to_hash
|
152
|
-
{ :_id => @id }.merge!(@data)
|
153
|
-
end
|
154
|
-
|
155
|
-
|
156
|
-
def validate
|
157
|
-
self.class.validate_keys.each do |k, msg, method|
|
158
|
-
raise ValidationError, "[#{k}, #{@data[k]}] #{msg}" unless
|
159
|
-
@data.has_key?(k) && method.call(@data[k])
|
160
|
-
end
|
116
|
+
{ :_id => @id, :_type => self.class._type }.merge!(@data)
|
161
117
|
end
|
162
118
|
|
163
119
|
|
@@ -172,63 +128,61 @@ module CouchPillow
|
|
172
128
|
end
|
173
129
|
|
174
130
|
|
175
|
-
#
|
176
|
-
# specified in the {validate_type} method.
|
131
|
+
# Cleanup the @data hash so it only contains relevant fields.
|
177
132
|
#
|
178
|
-
def
|
179
|
-
|
180
|
-
|
181
|
-
if t == Integer
|
182
|
-
@data[k] = Integer(value)
|
183
|
-
elsif t == Float
|
184
|
-
@data[k] = Float(value)
|
185
|
-
elsif t == String
|
186
|
-
@data[k] = String(value)
|
187
|
-
elsif t == Array
|
188
|
-
@data[k] = Array(value)
|
189
|
-
elsif t == Time
|
190
|
-
@data[k] = Time.parse(value)
|
191
|
-
elsif t == Boolean
|
192
|
-
@data[k] = value == 0 || !value ? false : true
|
193
|
-
end
|
194
|
-
end
|
133
|
+
def whitelist!
|
134
|
+
@data.delete_if do |k, v|
|
135
|
+
!self.class.attributes.has_key?(k)
|
195
136
|
end
|
196
137
|
end
|
197
138
|
|
198
139
|
|
199
|
-
#
|
200
|
-
#
|
140
|
+
# Go through each attribute, and validate the values.
|
141
|
+
# Validation also perform auto-conversion if auto-conversion is enabled
|
142
|
+
# for that attribute.
|
201
143
|
#
|
202
|
-
def
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
144
|
+
def validate!
|
145
|
+
self.class.attributes.each do |k, attr|
|
146
|
+
if has?(k)
|
147
|
+
@data[k] = attr.validate(@data[k]) if has?(k)
|
148
|
+
else
|
149
|
+
@data[k] = attr.trigger_default_directive if attr.has_default?
|
150
|
+
raise ValidationError, "Attribute '#{k}' is required" if attr.required? && !has?(k)
|
207
151
|
end
|
208
152
|
end
|
209
153
|
end
|
210
154
|
|
211
155
|
|
156
|
+
# Sort keys on this document.
|
157
|
+
#
|
158
|
+
def sort!
|
159
|
+
@data = @data.sort.to_h
|
160
|
+
end
|
161
|
+
|
162
|
+
|
163
|
+
def _type
|
164
|
+
self.class._type
|
165
|
+
end
|
166
|
+
|
167
|
+
|
168
|
+
def _id
|
169
|
+
@id
|
170
|
+
end
|
171
|
+
|
172
|
+
|
212
173
|
# Get a Document given an id.
|
213
174
|
#
|
214
|
-
# @return nil if Document is of a different type.
|
175
|
+
# @return nil if not found or Document is of a different type.
|
215
176
|
#
|
216
177
|
def self.get id
|
217
178
|
result = CouchPillow.db.get(id) and
|
218
|
-
type = result[
|
219
|
-
type ==
|
179
|
+
type = result[:_type] || result["_type"] and
|
180
|
+
type == _type and
|
220
181
|
new(result, id) or
|
221
182
|
nil
|
222
183
|
end
|
223
184
|
|
224
185
|
|
225
|
-
# Sets the type of this Document.
|
226
|
-
#
|
227
|
-
def self.type value
|
228
|
-
@type = value.to_s
|
229
|
-
end
|
230
|
-
|
231
|
-
|
232
186
|
# Rename an existing key to a new key. This is invoked right after
|
233
187
|
# initialize.
|
234
188
|
#
|
@@ -240,80 +194,18 @@ module CouchPillow
|
|
240
194
|
end
|
241
195
|
|
242
196
|
|
243
|
-
#
|
244
|
-
# cannot be nil.
|
245
|
-
#
|
246
|
-
def self.validate_presence key
|
247
|
-
validate key, "is missing", PRESENCE_LAMBDA
|
248
|
-
end
|
249
|
-
|
250
|
-
|
251
|
-
# Validate the type of a particular key.
|
252
|
-
#
|
253
|
-
# @example
|
254
|
-
# validate_type :name, String
|
255
|
-
#
|
256
|
-
def self.validate_type key, type
|
257
|
-
validate key, "#{key} is not the correct type. Expected a #{type}", lambda { |v| v.is_a? type }
|
258
|
-
type_keys << [key, type]
|
259
|
-
end
|
260
|
-
|
261
|
-
|
262
|
-
# Validate the type is a Boolean, since Ruby lacks a Boolean class.
|
263
|
-
#
|
264
|
-
# @example
|
265
|
-
# validate_type_boolean :available, Boolean
|
266
|
-
#
|
267
|
-
def self.validate_type_boolean key
|
268
|
-
validate key, "#{key} is not the correct type. Expected a Boolean", lambda { |v| !!v == v }
|
269
|
-
type_keys << [key, Boolean]
|
270
|
-
end
|
271
|
-
|
272
|
-
|
273
|
-
# Validate the presence of a particular key using a custom validation method.
|
274
|
-
#
|
275
|
-
# @param key Key to be validated.
|
276
|
-
# @param message Message that will be displayed when validation fails.
|
277
|
-
# @yield [v] Value of the key.
|
278
|
-
# The block must return truthy for it to pass the validation.
|
279
|
-
#
|
280
|
-
# @example
|
281
|
-
# validate :first_name, 'first name is not Joe', lambda { |v| v != "Joe" }
|
282
|
-
#
|
283
|
-
def self.validate key, message, block
|
284
|
-
raise ValidationError, "Provide validation method for key #{key}" unless block
|
285
|
-
validate_keys << [key, message, block]
|
286
|
-
end
|
287
|
-
|
288
|
-
|
289
|
-
# This Document should only accept keys that are specified here.
|
290
|
-
# The existence of these keys are optional, and won't trigger any validation
|
291
|
-
# unless specified by the validate methods. Hashes passed to {#update} and
|
292
|
-
# {#initialize} will be filtered through this list.
|
293
|
-
#
|
294
|
-
# If you don't specify a whitelist, Document will accept any keys, but
|
295
|
-
# once you specify it, only those keys will be accepted and the rest will
|
296
|
-
# be dropped.
|
297
|
-
#
|
298
|
-
# @param list Whitelist of keys.
|
299
|
-
#
|
300
|
-
# @example
|
301
|
-
# whitelist :first_name, :last_name, :address
|
197
|
+
# Sets the type of this Document.
|
302
198
|
#
|
303
|
-
def self.
|
304
|
-
|
305
|
-
whitelist_keys << k.to_s.to_sym
|
306
|
-
end
|
199
|
+
def self.type value
|
200
|
+
@type = value.to_s
|
307
201
|
end
|
308
202
|
|
309
203
|
|
310
204
|
private
|
311
205
|
|
312
206
|
|
313
|
-
# Read the type of this Document. Internal use only.
|
314
|
-
#
|
315
207
|
def self._type
|
316
|
-
@type
|
208
|
+
@type ||= DEFAULT_TYPE
|
317
209
|
end
|
318
210
|
|
319
211
|
|
@@ -322,26 +214,12 @@ module CouchPillow
|
|
322
214
|
end
|
323
215
|
|
324
216
|
|
325
|
-
def self.type_keys
|
326
|
-
@type_keys ||= []
|
327
|
-
end
|
328
|
-
|
329
|
-
|
330
|
-
def self.validate_keys
|
331
|
-
@validate_keys ||= []
|
332
|
-
end
|
333
|
-
|
334
|
-
|
335
|
-
def self.whitelist_keys
|
336
|
-
@whitelist_keys ||= []
|
337
|
-
end
|
338
|
-
|
339
|
-
|
340
217
|
def self.symbolize hash
|
341
218
|
hash.inject({}) do |memo,(k,v)|
|
342
219
|
memo[k.to_sym] = v
|
343
220
|
memo
|
344
221
|
end
|
345
222
|
end
|
223
|
+
|
346
224
|
end
|
347
225
|
end
|
data/lib/couchpillow/version.rb
CHANGED
data/lib/couchpillow.rb
CHANGED
data/test/helper.rb
CHANGED
@@ -0,0 +1,122 @@
|
|
1
|
+
require './test/helper.rb'
|
2
|
+
|
3
|
+
class TestAttribute < Minitest::Test
|
4
|
+
|
5
|
+
Attribute = CouchPillow::Attribute
|
6
|
+
|
7
|
+
|
8
|
+
def test_required
|
9
|
+
attr = Attribute.new(:batman).required
|
10
|
+
assert_equal true, attr.required?
|
11
|
+
end
|
12
|
+
|
13
|
+
|
14
|
+
def test_type
|
15
|
+
attr = Attribute.new(:batman).type(String)
|
16
|
+
assert_equal "hello", attr.validate("hello")
|
17
|
+
assert_raises CouchPillow::ValidationError do
|
18
|
+
attr.validate(1)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
def test_auto_convert_integer
|
24
|
+
attr = Attribute.new(:batman).type(Integer).auto_convert
|
25
|
+
assert_equal 1000, attr.validate(1000)
|
26
|
+
assert_equal 1000, attr.validate("1000")
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
def test_auto_convert_float
|
31
|
+
attr = Attribute.new(:batman).type(Float).auto_convert
|
32
|
+
assert_equal 1000.01, attr.validate(1000.01)
|
33
|
+
assert_equal 1000.01, attr.validate("1000.01")
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
def test_auto_convert_string
|
38
|
+
attr = Attribute.new(:batman).type(String).auto_convert
|
39
|
+
assert_equal "hello", attr.validate("hello")
|
40
|
+
assert_equal "1", attr.validate(1)
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
def test_auto_convert_array
|
45
|
+
attr = Attribute.new(:batman).type(Array).auto_convert
|
46
|
+
assert_equal ["hello"], attr.validate(["hello"])
|
47
|
+
assert_equal [1], attr.validate(1)
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
def test_auto_convert_time
|
52
|
+
attr = Attribute.new(:batman).type(Time).auto_convert
|
53
|
+
time = attr.validate("2014/7/14")
|
54
|
+
assert time.is_a?(Time)
|
55
|
+
assert_equal 2014, time.year
|
56
|
+
assert_equal 7, time.month
|
57
|
+
assert_equal 14, time.day
|
58
|
+
|
59
|
+
time = attr.validate(Time.now.utc)
|
60
|
+
assert time
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
def test_auto_convert_boolean
|
65
|
+
attr = Attribute.new(:batman).type(CouchPillow::Boolean).auto_convert
|
66
|
+
assert_equal false, attr.validate("false")
|
67
|
+
assert_equal true, attr.validate("123")
|
68
|
+
assert_equal true, attr.validate(1)
|
69
|
+
assert_equal false, attr.validate(0)
|
70
|
+
assert_equal false, attr.validate(nil)
|
71
|
+
end
|
72
|
+
|
73
|
+
|
74
|
+
def test_default
|
75
|
+
attr = Attribute.new(:batman).default { "John" }
|
76
|
+
assert_equal "John", attr.validate(nil)
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
def test_has_default
|
81
|
+
attr = Attribute.new(:batman).default { "John" }
|
82
|
+
assert_equal true, attr.has_default?
|
83
|
+
|
84
|
+
no_default_attr = Attribute.new(:batman)
|
85
|
+
assert_equal false, no_default_attr.has_default?
|
86
|
+
end
|
87
|
+
|
88
|
+
|
89
|
+
def test_content
|
90
|
+
attr = Attribute.new(:batman).content { |v| v == "John" }
|
91
|
+
assert_raises CouchPillow::ValidationError do
|
92
|
+
attr.validate("Jim")
|
93
|
+
end
|
94
|
+
attr.validate("John")
|
95
|
+
end
|
96
|
+
|
97
|
+
|
98
|
+
def test_all
|
99
|
+
attr = Attribute.new(:batman)
|
100
|
+
.required
|
101
|
+
.type(Integer)
|
102
|
+
.default { 0 }
|
103
|
+
.content { |v| v >= 0 }
|
104
|
+
|
105
|
+
assert_equal true, attr.required?
|
106
|
+
assert_equal 0, attr.validate(nil)
|
107
|
+
assert_equal 100, attr.validate(100)
|
108
|
+
assert_raises CouchPillow::ValidationError do
|
109
|
+
attr.validate(-100)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
|
114
|
+
def test_validate_maintain_value_if_type_not_specified
|
115
|
+
attr = Attribute.new(:batman)
|
116
|
+
[true, false, :symbol, 1, 0, -1, 10.0, "string", 1293.1, 0x90, Time.now, nil].each do |v|
|
117
|
+
assert_equal v, attr.validate(v)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
|
122
|
+
end
|
data/test/test_document.rb
CHANGED
@@ -1,6 +1,3 @@
|
|
1
|
-
require 'minitest/autorun'
|
2
|
-
require 'minitest/unit'
|
3
|
-
require 'mocha/mini_test'
|
4
1
|
require './test/helper.rb'
|
5
2
|
|
6
3
|
class TestDocument < Minitest::Test
|
@@ -29,12 +26,12 @@ class TestDocument < Minitest::Test
|
|
29
26
|
def test_timestamp
|
30
27
|
d = Document.new({}, "1")
|
31
28
|
d.save!
|
32
|
-
assert d.
|
33
|
-
assert d.
|
29
|
+
assert d._created_at
|
30
|
+
assert d._updated_at
|
34
31
|
end
|
35
32
|
|
36
33
|
|
37
|
-
def
|
34
|
+
def test_default_type
|
38
35
|
d = Document.new({}, "1")
|
39
36
|
assert_equal "default", d._type
|
40
37
|
end
|
@@ -51,16 +48,24 @@ class TestDocument < Minitest::Test
|
|
51
48
|
def test_update
|
52
49
|
d = Class.new(Document) do
|
53
50
|
type 'test'
|
51
|
+
|
52
|
+
attribute(:me)
|
53
|
+
attribute(:batman)
|
54
|
+
attribute(:apple)
|
54
55
|
end.new
|
56
|
+
refute d.me
|
57
|
+
refute d.batman
|
58
|
+
refute d.apple
|
59
|
+
|
55
60
|
hash = {
|
56
61
|
'me' => 'too',
|
57
62
|
:batman => 'robin',
|
58
63
|
'apple' => 123
|
59
64
|
}
|
60
65
|
d.update(hash)
|
61
|
-
assert_equal 'too', d
|
62
|
-
assert_equal 'robin', d
|
63
|
-
assert_equal 123, d
|
66
|
+
assert_equal 'too', d.me
|
67
|
+
assert_equal 'robin', d.batman
|
68
|
+
assert_equal 123, d.apple
|
64
69
|
assert d.save!
|
65
70
|
end
|
66
71
|
|
@@ -68,8 +73,11 @@ class TestDocument < Minitest::Test
|
|
68
73
|
def test_update_existing_fields
|
69
74
|
d = Class.new(Document) do
|
70
75
|
type 'test'
|
76
|
+
attribute(:a)
|
77
|
+
attribute(:b)
|
71
78
|
end.new({ a: 1, b: 2})
|
72
79
|
assert_equal 1, d[:a]
|
80
|
+
assert_equal 1, d.a
|
73
81
|
hash = {
|
74
82
|
'a' => 'too',
|
75
83
|
}
|
@@ -80,200 +88,11 @@ class TestDocument < Minitest::Test
|
|
80
88
|
end
|
81
89
|
|
82
90
|
|
83
|
-
def
|
84
|
-
d = Class.new(Document) do
|
85
|
-
validate_presence :xyz
|
86
|
-
end.new
|
87
|
-
assert_raises Document::ValidationError do
|
88
|
-
d.save!
|
89
|
-
end
|
90
|
-
|
91
|
-
d.xyz = 10
|
92
|
-
d.save!
|
93
|
-
end
|
94
|
-
|
95
|
-
|
96
|
-
def test_validate_type
|
97
|
-
d = Class.new(Document) do
|
98
|
-
validate_type :abc, Hash
|
99
|
-
end.new
|
100
|
-
|
101
|
-
d.abc = "other type"
|
102
|
-
assert_raises Document::ValidationError do
|
103
|
-
d.save!
|
104
|
-
end
|
105
|
-
|
106
|
-
d.abc = { :hello => "world" }
|
107
|
-
d.save!
|
108
|
-
end
|
109
|
-
|
110
|
-
|
111
|
-
def test_validate_type_boolean
|
112
|
-
d = Class.new(Document) do
|
113
|
-
validate_type_boolean :abc
|
114
|
-
end.new
|
115
|
-
|
116
|
-
d.abc = "other type"
|
117
|
-
assert_raises Document::ValidationError do
|
118
|
-
d.save!
|
119
|
-
end
|
120
|
-
|
121
|
-
d.abc = true
|
122
|
-
d.save!
|
123
|
-
end
|
124
|
-
|
125
|
-
|
126
|
-
def test_validate_type_also_ensures_types_on_create_integer
|
127
|
-
klass = Class.new(Document) do
|
128
|
-
type "test"
|
129
|
-
validate_type :abc, Integer
|
130
|
-
end
|
131
|
-
|
132
|
-
CouchPillow.db.
|
133
|
-
expects(:get).
|
134
|
-
with('123').
|
135
|
-
returns( { '_type' => 'test', 'abc' => '100' } )
|
136
|
-
|
137
|
-
d = klass.get('123')
|
138
|
-
assert d
|
139
|
-
assert_equal 100, d.abc
|
140
|
-
end
|
141
|
-
|
142
|
-
|
143
|
-
def test_validate_type_also_ensures_types_on_create_string
|
144
|
-
klass = Class.new(Document) do
|
145
|
-
type "test"
|
146
|
-
validate_type :abc, String
|
147
|
-
end
|
148
|
-
|
149
|
-
CouchPillow.db.
|
150
|
-
expects(:get).
|
151
|
-
with('123').
|
152
|
-
returns( { '_type' => 'test', 'abc' => 100 } )
|
153
|
-
|
154
|
-
d = klass.get('123')
|
155
|
-
assert d
|
156
|
-
assert_equal '100', d.abc
|
157
|
-
end
|
158
|
-
|
159
|
-
|
160
|
-
def test_validate_type_also_ensures_types_on_create_float
|
161
|
-
klass = Class.new(Document) do
|
162
|
-
type "test"
|
163
|
-
validate_type :abc, Float
|
164
|
-
end
|
165
|
-
|
166
|
-
CouchPillow.db.
|
167
|
-
expects(:get).
|
168
|
-
with('123').
|
169
|
-
returns( { '_type' => 'test', 'abc' => '121.21' } )
|
170
|
-
|
171
|
-
d = klass.get('123')
|
172
|
-
assert d
|
173
|
-
assert_equal 121.21, d.abc
|
174
|
-
end
|
175
|
-
|
176
|
-
|
177
|
-
def test_validate_type_also_ensures_types_on_create_array
|
178
|
-
klass = Class.new(Document) do
|
179
|
-
type "test"
|
180
|
-
validate_type :abc, Array
|
181
|
-
end
|
182
|
-
|
183
|
-
CouchPillow.db.
|
184
|
-
expects(:get).
|
185
|
-
with('123').
|
186
|
-
returns( { '_type' => 'test', 'abc' => 1 } )
|
187
|
-
|
188
|
-
d = klass.get('123')
|
189
|
-
assert d
|
190
|
-
assert_equal [1], d.abc
|
191
|
-
end
|
192
|
-
|
193
|
-
|
194
|
-
def test_validate_type_also_ensures_types_on_create_time
|
195
|
-
klass = Class.new(Document) do
|
196
|
-
type "test"
|
197
|
-
validate_type :abc, Time
|
198
|
-
end
|
199
|
-
|
200
|
-
CouchPillow.db.
|
201
|
-
expects(:get).
|
202
|
-
with('123').
|
203
|
-
returns( { '_type' => 'test', 'abc' => "2014/07/04" } )
|
204
|
-
|
205
|
-
d = klass.get('123')
|
206
|
-
assert d
|
207
|
-
assert d.abc.is_a?(Time)
|
208
|
-
assert_equal 2014, d.abc.year
|
209
|
-
assert_equal 7, d.abc.month
|
210
|
-
assert_equal 4, d.abc.day
|
211
|
-
end
|
212
|
-
|
213
|
-
|
214
|
-
def test_validate_type_also_ensures_types_on_create_boolean
|
215
|
-
klass = Class.new(Document) do
|
216
|
-
type "test"
|
217
|
-
validate_type_boolean :abc
|
218
|
-
end
|
219
|
-
|
220
|
-
# true
|
221
|
-
CouchPillow.db.
|
222
|
-
expects(:get).
|
223
|
-
with('123').
|
224
|
-
returns( { '_type' => 'test', 'abc' => 1 } )
|
225
|
-
|
226
|
-
d = klass.get('123')
|
227
|
-
assert d
|
228
|
-
assert_equal true, d.abc
|
229
|
-
|
230
|
-
# false
|
231
|
-
CouchPillow.db.
|
232
|
-
expects(:get).
|
233
|
-
with('123').
|
234
|
-
returns( { '_type' => 'test', 'abc' => 0 } )
|
235
|
-
|
236
|
-
d = klass.get('123')
|
237
|
-
assert d
|
238
|
-
assert_equal false, d.abc
|
239
|
-
end
|
240
|
-
|
241
|
-
|
242
|
-
def test_validate_custom
|
243
|
-
d = Class.new(Document) do
|
244
|
-
validate :xyz, "must be Numeric", lambda { |v| v.is_a? Numeric }
|
245
|
-
end.new
|
246
|
-
d.xyz = "string"
|
247
|
-
assert_raises Document::ValidationError do
|
248
|
-
d.save!
|
249
|
-
end
|
250
|
-
|
251
|
-
d.xyz = {}
|
252
|
-
assert_raises Document::ValidationError do
|
253
|
-
d.save!
|
254
|
-
end
|
255
|
-
|
256
|
-
d.xyz = 123
|
257
|
-
d.save!
|
258
|
-
end
|
259
|
-
|
260
|
-
|
261
|
-
def test_whitelist
|
262
|
-
d = Class.new(Document) do
|
263
|
-
whitelist :a, :b, :c
|
264
|
-
end.new
|
265
|
-
d.update({a: 1, b: 2, c: 3, d: 4})
|
266
|
-
assert d.save!
|
267
|
-
assert_equal 1, d.a
|
268
|
-
assert_equal 2, d.b
|
269
|
-
assert_equal 3, d.c
|
270
|
-
assert_equal false, d.has?(:d)
|
271
|
-
end
|
272
|
-
|
273
|
-
|
274
|
-
def test_whitelist_using_symbol_literal
|
91
|
+
def test_exclusion_other_keys
|
275
92
|
d = Class.new(Document) do
|
276
|
-
|
93
|
+
attribute :a
|
94
|
+
attribute :b
|
95
|
+
attribute :c
|
277
96
|
end.new
|
278
97
|
d.update({a: 1, b: 2, c: 3, d: 4})
|
279
98
|
assert d.save!
|
@@ -287,14 +106,14 @@ class TestDocument < Minitest::Test
|
|
287
106
|
def test_to_json
|
288
107
|
mock_time
|
289
108
|
d = Document.new({}, "1")
|
290
|
-
assert_equal "{\"_id\":\"1\",\"
|
109
|
+
assert_equal "{\"_id\":\"1\",\"_type\":\"default\",\"_created_at\":\"#{mock_time.to_s}\",\"_updated_at\":\"#{mock_time.to_s}\"}", d.to_json
|
291
110
|
end
|
292
111
|
|
293
112
|
|
294
113
|
def test_to_hash
|
295
114
|
mock_time
|
296
115
|
d = Document.new({}, "1")
|
297
|
-
assert_equal({ :_id => "1", :
|
116
|
+
assert_equal({ :_id => "1", :_created_at => mock_time, :_type => "default", :_updated_at => mock_time }, d.to_hash)
|
298
117
|
end
|
299
118
|
|
300
119
|
|
@@ -315,6 +134,9 @@ class TestDocument < Minitest::Test
|
|
315
134
|
|
316
135
|
def test_rename_keys
|
317
136
|
d = Class.new(Document) do
|
137
|
+
attribute(:bar)
|
138
|
+
attribute(:other)
|
139
|
+
|
318
140
|
rename :foo, :bar
|
319
141
|
end.new( { :foo => 123, :other => 'abc' } )
|
320
142
|
assert_equal 123, d[:bar]
|
@@ -340,18 +162,34 @@ class TestDocument < Minitest::Test
|
|
340
162
|
|
341
163
|
assert_raises ArgumentError do
|
342
164
|
d = Class.new(Document) do
|
343
|
-
rename :
|
165
|
+
rename :_created_at, :err
|
344
166
|
end.new
|
345
167
|
end
|
346
168
|
|
347
169
|
assert_raises ArgumentError do
|
348
170
|
d = Class.new(Document) do
|
349
|
-
rename :bla, :
|
171
|
+
rename :bla, :_updated_at
|
350
172
|
end.new
|
351
173
|
end
|
352
174
|
end
|
353
175
|
|
354
176
|
|
177
|
+
def test_get
|
178
|
+
klass = Class.new(Document) do
|
179
|
+
type 'test'
|
180
|
+
attribute(:foo)
|
181
|
+
end
|
182
|
+
|
183
|
+
k = klass.new
|
184
|
+
k.foo = 100
|
185
|
+
k.save!
|
186
|
+
k_id = k._id
|
187
|
+
|
188
|
+
d = klass.get(k_id)
|
189
|
+
assert_equal 100, d.foo
|
190
|
+
end
|
191
|
+
|
192
|
+
|
355
193
|
def test_get_returns_nil
|
356
194
|
CouchPillow.db.expects(:get).with('123').returns(nil)
|
357
195
|
d = Document.get('123')
|
@@ -375,14 +213,96 @@ class TestDocument < Minitest::Test
|
|
375
213
|
|
376
214
|
|
377
215
|
def test_key_with_false_values
|
378
|
-
|
216
|
+
klass = Class.new(Document) do
|
217
|
+
attribute(:key)
|
218
|
+
end
|
219
|
+
d = klass.new({ :key => false }, "1")
|
379
220
|
assert_equal false, d.key
|
380
221
|
end
|
381
222
|
|
382
223
|
|
383
224
|
def test_key_with_nil_values
|
384
|
-
|
225
|
+
klass = Class.new(Document) do
|
226
|
+
attribute(:key)
|
227
|
+
end
|
228
|
+
d = klass.new({ :key => nil }, "1")
|
385
229
|
assert_equal nil, d.key
|
386
230
|
end
|
387
231
|
|
232
|
+
|
233
|
+
def test_attribute
|
234
|
+
d = Class.new(Document) do
|
235
|
+
type 'test'
|
236
|
+
attribute(:foo)
|
237
|
+
end.new
|
238
|
+
|
239
|
+
d.foo = 12345
|
240
|
+
d.save!
|
241
|
+
end
|
242
|
+
|
243
|
+
|
244
|
+
def test_attribute_type
|
245
|
+
d = Class.new(Document) do
|
246
|
+
type 'test'
|
247
|
+
attribute(:foo)
|
248
|
+
.type(String)
|
249
|
+
end.new
|
250
|
+
|
251
|
+
d.foo = "test"
|
252
|
+
d.save!
|
253
|
+
end
|
254
|
+
|
255
|
+
|
256
|
+
def test_attribute_auto_convert
|
257
|
+
d = Class.new(Document) do
|
258
|
+
type 'test'
|
259
|
+
attribute(:foo)
|
260
|
+
.type(String).auto_convert
|
261
|
+
end.new
|
262
|
+
|
263
|
+
d.foo = 1
|
264
|
+
d.save!
|
265
|
+
|
266
|
+
assert_equal "1", d.foo
|
267
|
+
end
|
268
|
+
|
269
|
+
|
270
|
+
def test_attribute_with_directives
|
271
|
+
d = Class.new(Document) do
|
272
|
+
type 'test'
|
273
|
+
attribute(:foo)
|
274
|
+
.default { 100 }
|
275
|
+
end.new
|
276
|
+
|
277
|
+
d.save!
|
278
|
+
assert_equal 100, d.foo
|
279
|
+
end
|
280
|
+
|
281
|
+
|
282
|
+
def test_attribute_no_default_but_required
|
283
|
+
klass = Class.new(Document) do
|
284
|
+
type 'test'
|
285
|
+
attribute(:foo)
|
286
|
+
.required
|
287
|
+
end
|
288
|
+
|
289
|
+
k = klass.new
|
290
|
+
assert_raises CouchPillow::ValidationError do
|
291
|
+
k.save!
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
|
296
|
+
def test_attribute_with_default_but_also_required
|
297
|
+
klass = Class.new(Document) do
|
298
|
+
type 'test'
|
299
|
+
attribute(:foo)
|
300
|
+
.required
|
301
|
+
.default { "batman" }
|
302
|
+
end
|
303
|
+
|
304
|
+
k = klass.new
|
305
|
+
k.save!
|
306
|
+
end
|
307
|
+
|
388
308
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: couchpillow
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Albert Tedja
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-12-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: minitest
|
@@ -64,9 +64,14 @@ files:
|
|
64
64
|
- Rakefile
|
65
65
|
- couchpillow.gemspec
|
66
66
|
- lib/couchpillow.rb
|
67
|
+
- lib/couchpillow/attribute.rb
|
68
|
+
- lib/couchpillow/attributive.rb
|
69
|
+
- lib/couchpillow/boolean.rb
|
67
70
|
- lib/couchpillow/document.rb
|
71
|
+
- lib/couchpillow/validation_error.rb
|
68
72
|
- lib/couchpillow/version.rb
|
69
73
|
- test/helper.rb
|
74
|
+
- test/test_attribute.rb
|
70
75
|
- test/test_document.rb
|
71
76
|
homepage: https://github.com/atedja/pillow
|
72
77
|
licenses:
|
@@ -88,9 +93,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
88
93
|
version: '0'
|
89
94
|
requirements: []
|
90
95
|
rubyforge_project:
|
91
|
-
rubygems_version: 2.4.
|
96
|
+
rubygems_version: 2.4.5
|
92
97
|
signing_key:
|
93
98
|
specification_version: 4
|
94
99
|
summary: Document wrapper for Couchbase
|
95
100
|
test_files: []
|
96
|
-
has_rdoc:
|