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 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
@@ -0,0 +1,2 @@
1
+ *.gem
2
+ *.swp
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
@@ -0,0 +1,13 @@
1
+ require 'rake/testtask'
2
+
3
+ task :default => "build"
4
+
5
+ Rake::TestTask.new do |t|
6
+ t.description = "Run tests"
7
+ t.name = "test"
8
+ t.pattern = "./test/**/*.rb"
9
+ end
10
+
11
+ task :build do
12
+ `gem build couchpillow.gemspec`
13
+ end
@@ -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
@@ -0,0 +1,17 @@
1
+ require 'securerandom'
2
+ require 'json'
3
+
4
+ module CouchPillow
5
+
6
+ def self.db= driver
7
+ @db = driver
8
+ end
9
+
10
+ def self.db
11
+ @db || nil
12
+ end
13
+
14
+ end
15
+
16
+ require './lib/couchpillow/document'
17
+ require './lib/couchpillow/version'
@@ -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
@@ -0,0 +1,5 @@
1
+ module CouchPillow
2
+ GEM_NAME = "couchpillow"
3
+ NAME = "CouchPillow"
4
+ VERSION = "0.1"
5
+ 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: []