rufus-doric 0.1.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.
@@ -0,0 +1,300 @@
1
+ #--
2
+ # Copyright (c) 2010, John Mettraux, jmettraux@gmail.com
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in
12
+ # all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ # THE SOFTWARE.
21
+ #
22
+ # Made in Japan.
23
+ #++
24
+
25
+ #require 'cgi'
26
+
27
+
28
+ module Rufus
29
+ module Doric
30
+
31
+ DORIC_DESIGN_DOC = {
32
+ '_id' => '_design/doric',
33
+ 'views' => {
34
+ 'by_doric_type' => {
35
+ 'map' => %{
36
+ function(doc) {
37
+ if (doc.doric_type) emit(doc.doric_type, null);
38
+ }
39
+ }
40
+ }
41
+ }
42
+ }
43
+
44
+ #
45
+ # Classes extending that class have 1 Couch document per instance
46
+ #
47
+ class Model
48
+
49
+ #extend ActiveModel::Naming
50
+ include ActiveModel::Validations
51
+
52
+ #
53
+ # class 'helpers'
54
+
55
+ def self.doric_type (rt=nil)
56
+
57
+ @doric_type = rt.to_s if rt
58
+ @doric_type
59
+ end
60
+
61
+ def self._id_field (field_name=nil, &block)
62
+
63
+ @_id_field = field_name.to_s if field_name
64
+ @_id_field = block if block
65
+
66
+ @_id_field
67
+ end
68
+
69
+ def self.view_by (key)
70
+
71
+ @keys ||= []
72
+ @keys << key.to_s
73
+
74
+ instance_eval %{
75
+ def by_#{key} (val, opts={})
76
+ by('#{key}', val, opts)
77
+ end
78
+ }
79
+ end
80
+
81
+ include WithH
82
+ include WithDb
83
+
84
+ #--
85
+ # constructor and instance methods
86
+ #++
87
+
88
+ attr_reader :h
89
+
90
+ def initialize (doc={})
91
+
92
+ @h = doc.inject({}) { |h, (k, v)| h[k.to_s] = v; h }
93
+ @h['doric_type'] = self.class.doric_type
94
+ end
95
+
96
+ def _id
97
+ @h['_id']
98
+ end
99
+
100
+ def _rev
101
+ @h['_rev']
102
+ end
103
+
104
+ def id
105
+ @h['_id']
106
+ end
107
+
108
+ def attachments
109
+ (@h['_attachments'] || {}).keys.sort
110
+ end
111
+
112
+ def copy
113
+
114
+ h = Rufus::Json.dup(@h)
115
+ h.delete('_id')
116
+ h.delete('_rev')
117
+
118
+ self.class.new(h)
119
+ end
120
+
121
+ def save!
122
+
123
+ raise ActiveRecord::RecordInvalid.new(self) unless valid?
124
+
125
+ if @h['_id'].nil? && self.class._id_field
126
+
127
+ i = if self.class._id_field.is_a?(String)
128
+ self.send(self.class._id_field)
129
+ else
130
+ self.instance_eval &self.class._id_field
131
+ end
132
+
133
+ @h['_id'] = Rufus::Doric.neutralize_id(i)
134
+ end
135
+
136
+ raise ActiveRecord::RecordInvalid.new(self) if @h['_id'].nil?
137
+
138
+ r = db.put(@h)
139
+
140
+ raise(SaveFailed.new(self.class.doric_type, _id)) unless r.nil?
141
+ end
142
+
143
+ #--
144
+ # methods required by ActiveModel (see test/unit/lint_mdmodel_test.rb
145
+ #++
146
+
147
+ def to_model
148
+
149
+ self
150
+ end
151
+
152
+ def destroyed?
153
+
154
+ @h['_destroyed'] == true
155
+ end
156
+
157
+ def new_record?
158
+
159
+ @h['_id'].nil?
160
+ end
161
+
162
+ # Is used by <resource>_path and <resource>_url
163
+ #
164
+ def to_param
165
+
166
+ @h['_id']
167
+ end
168
+
169
+ def delete
170
+
171
+ @h['_destroyed'] = true
172
+ db.delete(@h)
173
+
174
+ # TODO : raise when the delete fails
175
+ end
176
+
177
+ def destroy
178
+
179
+ delete
180
+ end
181
+
182
+ #--
183
+ # class methods
184
+ #++
185
+
186
+ def self.destroy_all
187
+
188
+ get_all({}).each { |d| db.delete(d) }
189
+ end
190
+
191
+ def self.all (opts={})
192
+
193
+ get_all(opts).collect { |d| self.new(d) }
194
+ end
195
+
196
+ def self.find (_id)
197
+
198
+ doc = db.get(_id)
199
+
200
+ raise Rufus::Doric::NotFound.new(@doric_type, _id) unless doc
201
+
202
+ self.new(doc)
203
+ end
204
+
205
+ def self.design_path
206
+
207
+ name = self.to_s.downcase
208
+ name = name.gsub(/::/, '__')
209
+
210
+ "_design/doric_#{name}"
211
+ end
212
+
213
+ protected
214
+
215
+ def self.put_design_doc (key=nil)
216
+
217
+ # the 'all' view
218
+
219
+ unless key
220
+ db.put(DORIC_DESIGN_DOC)
221
+ return
222
+ end
223
+
224
+ # by_{key} views
225
+
226
+ x = {
227
+ '_id' => '_design/doric',
228
+ 'views' => {
229
+ 'by_doric_type' => {
230
+ 'map' => %{
231
+ function(doc) {
232
+ if (doc.doric_type) emit(doc.doric_type, null);
233
+ }
234
+ }
235
+ }
236
+ }
237
+ }
238
+
239
+ ddoc = db.get(design_path) || {
240
+ '_id' => design_path,
241
+ 'views' => {}
242
+ }
243
+
244
+ ddoc['views']["by_#{key}"] = {
245
+ 'map' => %{
246
+ function(doc) {
247
+ if (doc.doric_type == '#{@doric_type}') {
248
+ emit(doc['#{key}'], null);
249
+ }
250
+ }
251
+ }
252
+ }
253
+
254
+ db.put(ddoc)
255
+ end
256
+
257
+ def self.get_all (opts)
258
+
259
+ # TODO : limit, skip (opts)
260
+
261
+ path =
262
+ "_design/doric/_view/by_doric_type?key=%22#{@doric_type}%22" +
263
+ "&include_docs=true"
264
+
265
+ result = db.get(path)
266
+
267
+ unless result
268
+
269
+ # insert design doc
270
+
271
+ put_design_doc
272
+ return get_all(opts)
273
+ end
274
+
275
+ result['rows'].collect { |r| r['doc'] }
276
+ end
277
+
278
+ def self.by (key, val, opts)
279
+
280
+ # TODO : limit, skip (opts)
281
+
282
+ #v = Rufus::Json.encode(val)
283
+ #v = CGI.escape(v)
284
+ v = "%22#{val}%22"
285
+
286
+ path = "#{design_path}/_view/by_#{key}?key=#{v}&include_docs=true"
287
+
288
+ result = db.get(path)
289
+
290
+ unless result
291
+ put_design_doc(key)
292
+ return by(key, val, opts)
293
+ end
294
+
295
+ result['rows'].collect { |r| r['doc'] }
296
+ end
297
+ end
298
+ end
299
+ end
300
+
@@ -0,0 +1,122 @@
1
+ #--
2
+ # Copyright (c) 2010, John Mettraux, jmettraux@gmail.com
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in
12
+ # all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ # THE SOFTWARE.
21
+ #
22
+ # Made in Japan.
23
+ #++
24
+
25
+ require 'active_model'
26
+ require 'active_record'
27
+
28
+ # there are more 'require' at the end of this file
29
+
30
+
31
+ module Rufus
32
+ module Doric
33
+
34
+ def self.neutralize_id (s)
35
+
36
+ s.to_s.strip.gsub(/[\s\/:;\*\\\+\?]/, '_')
37
+ end
38
+
39
+ #
40
+ # The including classes get two new class methods : h_reader and h_accessor
41
+ #
42
+ module WithH
43
+
44
+ def self.included (target)
45
+
46
+ def target.known_fields
47
+ @known_fields
48
+ end
49
+
50
+ def target.h_reader (*names)
51
+ names.each do |name|
52
+ name = name.to_s
53
+ (@known_fields ||= []) << name
54
+ define_method(name) do
55
+ @h[name]
56
+ end
57
+ end
58
+ end
59
+ def target.h_accessor (*names)
60
+ h_reader(*names)
61
+ names.each do |name|
62
+ define_method("#{name}=") do |v|
63
+ @h[name.to_s] = v
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
69
+
70
+ #
71
+ # The .db 'xyz' and #db methods
72
+ #
73
+ module WithDb
74
+
75
+ def self.included (target)
76
+
77
+ def target.db (dbname=nil, opts=nil)
78
+
79
+ if dbname
80
+ @db = dbname.to_s
81
+ @db_opts = opts || {}
82
+ return @db
83
+ end
84
+
85
+ Rufus::Doric::Couch.db(@db, @db_opts)
86
+ end
87
+ end
88
+
89
+ def db
90
+
91
+ self.class.db
92
+ end
93
+ end
94
+
95
+ #--
96
+ # ERRORS
97
+ #++
98
+
99
+ # A common error class
100
+ #
101
+ class ModelError < StandardError
102
+
103
+ attr_accessor :model_class, :_id
104
+
105
+ def initialize (model_class, _id)
106
+ @model_class = model_class
107
+ @_id = _id
108
+ end
109
+
110
+ def to_s
111
+ "#{@model_class}/#{@_id}"
112
+ end
113
+ end
114
+
115
+ class NotFound < ModelError; end
116
+ class SaveFailed < ModelError; end
117
+ end
118
+ end
119
+
120
+ require 'rufus/doric/model'
121
+ require 'rufus/doric/one_doc_model'
122
+
@@ -0,0 +1,136 @@
1
+ #--
2
+ # Copyright (c) 2010, John Mettraux, jmettraux@gmail.com
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in
12
+ # all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ # THE SOFTWARE.
21
+ #
22
+ # Made in Japan.
23
+ #++
24
+
25
+
26
+ module Rufus
27
+ module Doric
28
+
29
+ #
30
+ # The models built on top of this class store all their instances
31
+ # in one Couch document.
32
+ #
33
+ class OneDocModel
34
+
35
+ include ActiveModel::Validations
36
+
37
+ #
38
+ # class 'helpers'
39
+
40
+ def self.doc_id (_id=nil)
41
+
42
+ @doc_id = _id.to_s if _id
43
+ @doc_id
44
+ end
45
+
46
+ include WithH
47
+ include WithDb
48
+
49
+ #
50
+ # constructor and instance methods
51
+
52
+ attr_reader :h
53
+ attr_reader :attachment
54
+
55
+ def initialize (h, doc=nil)
56
+
57
+ @h = h.inject({}) { |hh, (k, v)| hh[k.to_s] = v; hh }
58
+
59
+ if doc && atts = doc['_attachments']
60
+
61
+ @attachment, details = atts.find { |k, v|
62
+ File.basename(k, File.extname(k)) == @h['_id']
63
+ }
64
+ end
65
+ end
66
+
67
+ def _id
68
+
69
+ @h['_id']
70
+ end
71
+
72
+ def save!
73
+
74
+ raise ActiveRecord::RecordInvalid.new(self) unless valid?
75
+
76
+ doc = self.class.do_get(self.class.doc_id)
77
+ doc[self.class.doc_id][@h['_id']] = @h
78
+
79
+ db.put(doc)
80
+ end
81
+
82
+ def delete
83
+
84
+ doc = self.class.do_get(self.class.doc_id)
85
+ doc[self.class.doc_id].delete(@h['_id'])
86
+
87
+ # TODO : raise when the put fails
88
+
89
+ db.put(doc)
90
+ end
91
+
92
+ def destroy
93
+
94
+ delete
95
+ end
96
+
97
+ #--
98
+ # class methods
99
+ #++
100
+
101
+ def self.create! (h)
102
+
103
+ self.new(h).save!
104
+ end
105
+
106
+ def self.all
107
+
108
+ doc = do_get(@doc_id)
109
+
110
+ doc[@doc_id].values.collect { |h| self.new(h, doc) }
111
+ end
112
+
113
+ def self.destroy_all
114
+
115
+ doc = db.get(@doc_id)
116
+ db.delete(doc) if doc
117
+ end
118
+
119
+ def self.find (_id)
120
+
121
+ doc = do_get(@doc_id)
122
+
123
+ h = doc[@doc_id][_id]
124
+ h ? self.new(h, doc) : nil
125
+ end
126
+
127
+ protected
128
+
129
+ def self.do_get (doc_id)
130
+
131
+ db.get(doc_id) || { '_id' => doc_id, doc_id => {} }
132
+ end
133
+ end
134
+ end
135
+ end
136
+
@@ -0,0 +1,7 @@
1
+
2
+ module Rufus
3
+ module Doric
4
+ VERSION = '0.1.0'
5
+ end
6
+ end
7
+
@@ -0,0 +1,4 @@
1
+
2
+ require 'rufus/doric/couch'
3
+ require 'rufus/doric/models'
4
+
@@ -0,0 +1,3 @@
1
+
2
+ require 'rufus/doric'
3
+
@@ -0,0 +1,90 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{rufus-doric}
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["John Mettraux"]
12
+ s.date = %q{2010-03-17}
13
+ s.description = %q{
14
+ something at the intersection of Rails3, CouchDB and rufus-jig
15
+ }
16
+ s.email = %q{jmettraux@gmail.com}
17
+ s.extra_rdoc_files = [
18
+ "LICENSE.txt",
19
+ "README.rdoc"
20
+ ]
21
+ s.files = [
22
+ "CHANGELOG.txt",
23
+ "LICENSE.txt",
24
+ "README.rdoc",
25
+ "Rakefile",
26
+ "TODO.txt",
27
+ "lib/rufus-doric.rb",
28
+ "lib/rufus/doric.rb",
29
+ "lib/rufus/doric/couch.rb",
30
+ "lib/rufus/doric/fixtures.rb",
31
+ "lib/rufus/doric/model.rb",
32
+ "lib/rufus/doric/models.rb",
33
+ "lib/rufus/doric/one_doc_model.rb",
34
+ "lib/rufus/doric/version.rb",
35
+ "rufus-doric.gemspec",
36
+ "test/base.rb",
37
+ "test/fixtures/test/doric/69247b__picture.jpg",
38
+ "test/fixtures/test/doric/69249__picture.jpg",
39
+ "test/fixtures/test/doric/product0.json",
40
+ "test/fixtures/test/doric/product1.json",
41
+ "test/fixtures/test/doric/users.json",
42
+ "test/fixtures/test/doric/users__jami.png",
43
+ "test/fixtures/test/doric/users__john.jpg",
44
+ "test/fixtures/test/doric_ENV_workitems/workitem0.json",
45
+ "test/test.rb",
46
+ "test/ut_0_fixtures.rb",
47
+ "test/ut_1_model.rb",
48
+ "test/ut_2_model_view.rb",
49
+ "test/ut_3_model_lint.rb",
50
+ "test/ut_4_one_doc_model.rb"
51
+ ]
52
+ s.homepage = %q{http://github.com/jmettraux/rufus-doric/}
53
+ s.rdoc_options = ["--charset=UTF-8"]
54
+ s.require_paths = ["lib"]
55
+ s.rubyforge_project = %q{rufus}
56
+ s.rubygems_version = %q{1.3.6}
57
+ s.summary = %q{something at the intersection of Rails3, CouchDB and rufus-jig}
58
+ s.test_files = [
59
+ "test/test.rb"
60
+ ]
61
+
62
+ if s.respond_to? :specification_version then
63
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
64
+ s.specification_version = 3
65
+
66
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
67
+ s.add_runtime_dependency(%q<activerecord>, ["~> 3.0.0.beta"])
68
+ s.add_runtime_dependency(%q<rufus-jig>, [">= 0.1.16"])
69
+ s.add_runtime_dependency(%q<mime-types>, [">= 1.16"])
70
+ s.add_development_dependency(%q<rake>, [">= 0"])
71
+ s.add_development_dependency(%q<yard>, [">= 0"])
72
+ s.add_development_dependency(%q<jeweler>, [">= 0"])
73
+ else
74
+ s.add_dependency(%q<activerecord>, ["~> 3.0.0.beta"])
75
+ s.add_dependency(%q<rufus-jig>, [">= 0.1.16"])
76
+ s.add_dependency(%q<mime-types>, [">= 1.16"])
77
+ s.add_dependency(%q<rake>, [">= 0"])
78
+ s.add_dependency(%q<yard>, [">= 0"])
79
+ s.add_dependency(%q<jeweler>, [">= 0"])
80
+ end
81
+ else
82
+ s.add_dependency(%q<activerecord>, ["~> 3.0.0.beta"])
83
+ s.add_dependency(%q<rufus-jig>, [">= 0.1.16"])
84
+ s.add_dependency(%q<mime-types>, [">= 1.16"])
85
+ s.add_dependency(%q<rake>, [">= 0"])
86
+ s.add_dependency(%q<yard>, [">= 0"])
87
+ s.add_dependency(%q<jeweler>, [">= 0"])
88
+ end
89
+ end
90
+
data/test/base.rb ADDED
@@ -0,0 +1,16 @@
1
+
2
+ lib = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+ $:.unshift(lib) unless $:.include?(lib)
4
+
5
+ require 'test/unit'
6
+
7
+ require 'rubygems'
8
+ require 'yajl'
9
+ require 'patron'
10
+
11
+ require 'rufus/jig'
12
+
13
+ ENV['RAILS_ENV'] = 'test'
14
+ require 'active_support'
15
+ require 'rufus/doric'
16
+
@@ -0,0 +1,8 @@
1
+ {
2
+ "_id": "69249",
3
+ "serial_number": "69249",
4
+ "type": "AH0305 MAC Board",
5
+ "sub_type": "ATM",
6
+ "barcode": "76076P05936",
7
+ "roma_type": "products"
8
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "_id": "69247b",
3
+ "serial_number": "69247b",
4
+ "type": "AH0305 Shaun Smith SnowBoard",
5
+ "sub_type": "STM",
6
+ "barcode": "7607SF6P05936",
7
+ "roma_type": "products"
8
+ }