couchpillow 0.3.10 → 0.4.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 +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:
|