couchpillow 0.1

Sign up to get free protection for your applications and to get access to all the features.
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: []