couchpillow 0.1
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 +7 -0
- data/.gitignore +2 -0
- data/README.markdown +106 -0
- data/Rakefile +13 -0
- data/couchpillow.gemspec +21 -0
- data/lib/couchpillow.rb +17 -0
- data/lib/couchpillow/document.rb +153 -0
- data/lib/couchpillow/version.rb +5 -0
- data/test/document.rb +93 -0
- metadata +80 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: aebdc9cea445fcc41867e0b89beda54aa8f4e317
|
4
|
+
data.tar.gz: ea3f214cabe96aac8e3001c1c48633e7cc9f4fc9
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 102f839f848e55a30e02e9e1e7e5cad6c3d8cef6d641ba5bfa0aaef39d7c18129793dd4e380af6e98c27e03a1dd14b7f1a40e2f1ed47240f4f4f0f9137830b97
|
7
|
+
data.tar.gz: 10c976296156db15b542456e44bfed6d8c8e38d6247ceb62a8674606f109e51427a4810040933267cc13c16db966c6ba3ea134b75d902ea518d05298db973354
|
data/.gitignore
ADDED
data/README.markdown
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
# CouchPillow
|
2
|
+
|
3
|
+
Light and comfortable Document wrapper for Couchbase Server.
|
4
|
+
|
5
|
+
|
6
|
+
## Why CouchPillow?
|
7
|
+
|
8
|
+
Yet another ORM? Not quite. CouchPillow separates itself from the database
|
9
|
+
drivers, making it light and independent from the implementation.
|
10
|
+
|
11
|
+
Although it is initially designed to work with Couchbase Server, one can
|
12
|
+
easily extend this to other NoSQL drivers as long as they `respond_to?`
|
13
|
+
the `set`, `delete`, `replace`, and `get` methods.
|
14
|
+
|
15
|
+
|
16
|
+
## Features
|
17
|
+
|
18
|
+
- Automatic timestamp
|
19
|
+
- Validation
|
20
|
+
|
21
|
+
|
22
|
+
## How To Use
|
23
|
+
|
24
|
+
CouchPillow.db = Couchbase.connect(
|
25
|
+
bucket: 'default',
|
26
|
+
host: 'localhost' )
|
27
|
+
doc = CouchPillow::Document.new( { :stuff => 'hello' }, '123' )
|
28
|
+
doc.save!
|
29
|
+
|
30
|
+
# {
|
31
|
+
# '_type': 'default',
|
32
|
+
# 'stuff': 'hello',
|
33
|
+
# 'created_at': '2014-07-04 00:00:00 UTC'
|
34
|
+
# 'updated_at': '2014-07-04 00:00:00 UTC'
|
35
|
+
# }
|
36
|
+
|
37
|
+
|
38
|
+
Retrieving Documents
|
39
|
+
|
40
|
+
doc = CouchPillow::Document.get('123')
|
41
|
+
doc.stuff # '123'
|
42
|
+
|
43
|
+
|
44
|
+
Overriding `CouchPillow::Document`
|
45
|
+
|
46
|
+
class User < CouchPillow::Document
|
47
|
+
type :user
|
48
|
+
end
|
49
|
+
|
50
|
+
CouchPillow.db = Couchbase.connect(
|
51
|
+
bucket: 'default',
|
52
|
+
host: 'localhost' )
|
53
|
+
doc = User.new( { :email => 'john@email.com' } )
|
54
|
+
doc.email # 'john@email.com'
|
55
|
+
doc.save!
|
56
|
+
|
57
|
+
# {
|
58
|
+
# '_id': 'fb579b265cc005c47ff420a5c2a15d2b',
|
59
|
+
# '_type': 'user',
|
60
|
+
# 'email': 'john@email.com',
|
61
|
+
# 'created_at': '2014-07-04 00:00:00 UTC'
|
62
|
+
# 'updated_at': '2014-07-04 00:00:00 UTC'
|
63
|
+
# }
|
64
|
+
|
65
|
+
Using validation
|
66
|
+
|
67
|
+
class User < CouchPillow::Document
|
68
|
+
type :user
|
69
|
+
validate_presence :email
|
70
|
+
end
|
71
|
+
|
72
|
+
CouchPillow.db = Couchbase.connect(
|
73
|
+
bucket: 'default',
|
74
|
+
host: 'localhost' )
|
75
|
+
doc = User.new( { :first_name => 'John' } )
|
76
|
+
doc.save! # raises ValidationError('email is missing')
|
77
|
+
doc.email = 'john@email.com'
|
78
|
+
doc.save! # Success!
|
79
|
+
|
80
|
+
Using custom validation blocks
|
81
|
+
|
82
|
+
class User < CouchPillow::Document
|
83
|
+
type :user
|
84
|
+
validate_presence :email
|
85
|
+
validate :phone, 'is not a number', lambda { |v| v.is_a? Numeric }
|
86
|
+
end
|
87
|
+
|
88
|
+
CouchPillow.db = Couchbase.connect(
|
89
|
+
bucket: 'default',
|
90
|
+
host: 'localhost' )
|
91
|
+
doc = User.new
|
92
|
+
doc.email = 'john@email.com'
|
93
|
+
doc.first_name = 'john'
|
94
|
+
doc.phone = '123'
|
95
|
+
doc.save! # raises ValidationError('phone is not a number')
|
96
|
+
doc.phone = 123
|
97
|
+
doc.save! # Success!
|
98
|
+
|
99
|
+
What about Design Docs and Views? They are outside the scope of CouchPillow.
|
100
|
+
However, given a design doc named `my_design_doc` and a View named `by_email`,
|
101
|
+
that returns documents as values, you can easily use it like this:
|
102
|
+
|
103
|
+
CouchPillow.db.design_docs['my_design_doc'].
|
104
|
+
by_email(:body => { :key => 'john@email.com' }).map do |v|
|
105
|
+
new User(v.value, v.id)
|
106
|
+
end
|
data/Rakefile
ADDED
data/couchpillow.gemspec
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
$:.push File.expand_path("../lib", __FILE__)
|
2
|
+
require 'couchpillow/version'
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = "#{CouchPillow::GEM_NAME}"
|
6
|
+
s.version = CouchPillow::VERSION
|
7
|
+
s.authors = ["Albert Tedja"]
|
8
|
+
s.email = "nicho_tedja@yahoo.com"
|
9
|
+
s.homepage = "https://github.com/atedja/pillow"
|
10
|
+
s.summary = "Document wrapper for Couchbase"
|
11
|
+
s.description = "#{CouchPillow::NAME} is a light and comfortable Document wrapper for Couchbase Server."
|
12
|
+
|
13
|
+
s.files = `git ls-files`.split("\n")
|
14
|
+
s.test_files = `git ls-files -- {test}/*`.split("\n")
|
15
|
+
s.require_paths = ["lib"]
|
16
|
+
|
17
|
+
s.add_development_dependency 'minitest', '~> 5.3'
|
18
|
+
s.add_development_dependency 'mocha', '~> 1.1'
|
19
|
+
|
20
|
+
s.license = "BSD"
|
21
|
+
end
|
data/lib/couchpillow.rb
ADDED
@@ -0,0 +1,153 @@
|
|
1
|
+
module CouchPillow
|
2
|
+
|
3
|
+
class Document
|
4
|
+
|
5
|
+
class ValidationError < StandardError; end
|
6
|
+
|
7
|
+
|
8
|
+
attr_reader :id
|
9
|
+
|
10
|
+
|
11
|
+
@type = "default"
|
12
|
+
|
13
|
+
|
14
|
+
PRESENCE_LAMBDA = lambda do |value| !value.nil? end
|
15
|
+
|
16
|
+
|
17
|
+
def initialize hash = {}, id = SecureRandom.hex
|
18
|
+
@data = self.class.symbolize(hash)
|
19
|
+
@id = id
|
20
|
+
|
21
|
+
@data[:created_at] and
|
22
|
+
@data[:created_at] = Time.parse(@data[:created_at]) or
|
23
|
+
@data[:created_at] = Time.now.utc
|
24
|
+
@data[:updated_at] = Time.parse(@data[:updated_at]) if @data[:updated_at]
|
25
|
+
|
26
|
+
raise TypeError if @data[:_type] && @data[:_type] != self.class._type
|
27
|
+
@data[:_type] = self.class._type
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
# Map hash keys to methods
|
32
|
+
#
|
33
|
+
def method_missing m, *args, &block
|
34
|
+
ms = m.to_s
|
35
|
+
if ms.end_with?("=")
|
36
|
+
ms.gsub!('=', '')
|
37
|
+
@data[ms.to_sym] = args[0]
|
38
|
+
else
|
39
|
+
@data.has_key?(m) and
|
40
|
+
@data[m] or
|
41
|
+
super
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
def respond_to? m
|
47
|
+
ms = m.to_s
|
48
|
+
return true if ms.end_with?("=")
|
49
|
+
@data.has_key?(m) or
|
50
|
+
super
|
51
|
+
end
|
52
|
+
|
53
|
+
def timestamp!
|
54
|
+
@data[:updated_at] = Time.now.utc
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
def save!
|
59
|
+
validate
|
60
|
+
sort!
|
61
|
+
timestamp!
|
62
|
+
CouchPillow.db.set @id, @data
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
def delete!
|
67
|
+
CouchPillow.db.delete @id
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
def sort!
|
72
|
+
@data = @data.sort.to_h
|
73
|
+
end
|
74
|
+
|
75
|
+
|
76
|
+
# Attempt to update this Document. Fails if this Document does not yet
|
77
|
+
# exist in the database.
|
78
|
+
#
|
79
|
+
def update!
|
80
|
+
validate
|
81
|
+
sort!
|
82
|
+
timestamp!
|
83
|
+
CouchPillow.db.replace @id, @data
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
def to_json *a
|
88
|
+
h = { :_id => @id }.merge!(@data)
|
89
|
+
h.to_json(*a)
|
90
|
+
end
|
91
|
+
|
92
|
+
|
93
|
+
def validate
|
94
|
+
self.class.validate_keys.each do |k, msg, method|
|
95
|
+
raise ValidationError, "#{k} #{msg}" unless
|
96
|
+
@data.has_key?(k) && method.call(@data[k])
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
|
101
|
+
def self.get id
|
102
|
+
new(CouchPillow.db.get(id), id)
|
103
|
+
end
|
104
|
+
|
105
|
+
|
106
|
+
def self.type value
|
107
|
+
@type = value.to_s
|
108
|
+
end
|
109
|
+
|
110
|
+
|
111
|
+
def self._type
|
112
|
+
@type
|
113
|
+
end
|
114
|
+
|
115
|
+
|
116
|
+
def self.design_doc value
|
117
|
+
@ddoc = value
|
118
|
+
end
|
119
|
+
|
120
|
+
|
121
|
+
# Validate the presence of a particular key, and the value of that key
|
122
|
+
# cannot be nil.
|
123
|
+
#
|
124
|
+
def self.validate_presence key
|
125
|
+
validate key, "is missing", PRESENCE_LAMBDA
|
126
|
+
end
|
127
|
+
|
128
|
+
|
129
|
+
# Validate the presence of a particular key using a custom validation method.
|
130
|
+
# Implies the Document must contain the key.
|
131
|
+
#
|
132
|
+
def self.validate key, message, block
|
133
|
+
raise ValidationError, "Provide validation method for key #{key}" unless block
|
134
|
+
validate_keys << [key, message, block]
|
135
|
+
end
|
136
|
+
|
137
|
+
|
138
|
+
private
|
139
|
+
|
140
|
+
|
141
|
+
def self.validate_keys
|
142
|
+
@validate_key ||= []
|
143
|
+
end
|
144
|
+
|
145
|
+
|
146
|
+
def self.symbolize hash
|
147
|
+
hash.inject({}) do |memo,(k,v)|
|
148
|
+
memo[k.to_sym] = v
|
149
|
+
memo
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
data/test/document.rb
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'minitest/unit'
|
3
|
+
require 'mocha/mini_test'
|
4
|
+
require './lib/couchpillow.rb'
|
5
|
+
|
6
|
+
class TestDocument < Minitest::Test
|
7
|
+
|
8
|
+
class TestDoc < CouchPillow::Document
|
9
|
+
type :test
|
10
|
+
validate_presence :xyz
|
11
|
+
validate :xyz, "must be Numeric", lambda { |v| v.is_a? Numeric }
|
12
|
+
end
|
13
|
+
|
14
|
+
|
15
|
+
def mock_time
|
16
|
+
return @time if @time
|
17
|
+
@time = Time.now
|
18
|
+
Time.stubs(:now).returns(@time)
|
19
|
+
@time
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
def setup
|
24
|
+
@db = mock('couchbaseserver')
|
25
|
+
@db.stubs(:set)
|
26
|
+
@db.stubs(:delete)
|
27
|
+
@db.stubs(:replace)
|
28
|
+
@db.stubs(:get)
|
29
|
+
CouchPillow.db = @db
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
def test_create
|
34
|
+
d = CouchPillow::Document.new
|
35
|
+
d.save!
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
def test_timestamp
|
40
|
+
d = CouchPillow::Document.new({}, "1")
|
41
|
+
d.save!
|
42
|
+
assert d.created_at
|
43
|
+
assert d.updated_at
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
def test_type
|
48
|
+
d = CouchPillow::Document.new({}, "1")
|
49
|
+
assert_equal "default", d._type
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
def test_type_subclasses
|
54
|
+
d = TestDoc.new({}, "1")
|
55
|
+
assert_equal "test", d._type
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
def test_validate_presence
|
60
|
+
d = TestDoc.new
|
61
|
+
assert_raises CouchPillow::Document::ValidationError do
|
62
|
+
d.save!
|
63
|
+
end
|
64
|
+
|
65
|
+
d.xyz = 10
|
66
|
+
d.save!
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
def test_validate_custom
|
71
|
+
d = TestDoc.new
|
72
|
+
d.xyz = "string"
|
73
|
+
assert_raises CouchPillow::Document::ValidationError do
|
74
|
+
d.save!
|
75
|
+
end
|
76
|
+
|
77
|
+
d.xyz = {}
|
78
|
+
assert_raises CouchPillow::Document::ValidationError do
|
79
|
+
d.save!
|
80
|
+
end
|
81
|
+
|
82
|
+
d.xyz = 123
|
83
|
+
d.save!
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
def test_to_json
|
88
|
+
mock_time
|
89
|
+
d = CouchPillow::Document.new({}, "1")
|
90
|
+
assert_equal "{\"_id\":\"1\",\"created_at\":\"#{mock_time.to_s}\",\"_type\":\"default\"}", d.to_json
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
metadata
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: couchpillow
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.1'
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Albert Tedja
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-07-04 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: minitest
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '5.3'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '5.3'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: mocha
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.1'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.1'
|
41
|
+
description: CouchPillow is a light and comfortable Document wrapper for Couchbase
|
42
|
+
Server.
|
43
|
+
email: nicho_tedja@yahoo.com
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- ".gitignore"
|
49
|
+
- README.markdown
|
50
|
+
- Rakefile
|
51
|
+
- couchpillow.gemspec
|
52
|
+
- lib/couchpillow.rb
|
53
|
+
- lib/couchpillow/document.rb
|
54
|
+
- lib/couchpillow/version.rb
|
55
|
+
- test/document.rb
|
56
|
+
homepage: https://github.com/atedja/pillow
|
57
|
+
licenses:
|
58
|
+
- BSD
|
59
|
+
metadata: {}
|
60
|
+
post_install_message:
|
61
|
+
rdoc_options: []
|
62
|
+
require_paths:
|
63
|
+
- lib
|
64
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: '0'
|
74
|
+
requirements: []
|
75
|
+
rubyforge_project:
|
76
|
+
rubygems_version: 2.2.2
|
77
|
+
signing_key:
|
78
|
+
specification_version: 4
|
79
|
+
summary: Document wrapper for Couchbase
|
80
|
+
test_files: []
|