couch_potato 0.2.24 → 0.2.25
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES.md +5 -0
- data/README.md +1 -1
- data/VERSION.yml +1 -1
- data/lib/couch_potato.rb +1 -1
- data/lib/couch_potato/persistence/properties.rb +5 -4
- data/lib/couch_potato/persistence/simple_property.rb +1 -9
- data/lib/couch_potato/persistence/type_caster.rb +1 -1
- data/lib/couch_potato/railtie.rb +21 -0
- data/lib/couch_potato/view/custom_view_spec.rb +16 -5
- data/lib/couch_potato/view/view_query.rb +29 -8
- data/rails/init.rb +1 -5
- data/spec/custom_view_spec.rb +9 -1
- data/spec/property_spec.rb +63 -7
- data/spec/spec_helper.rb +7 -1
- data/spec/unit/view_query_spec.rb +13 -1
- data/spec/view_updates_spec.rb +28 -0
- metadata +5 -2
data/CHANGES.md
CHANGED
@@ -1,5 +1,10 @@
|
|
1
1
|
## Changes
|
2
2
|
|
3
|
+
### 0.2.25
|
4
|
+
* automatic view updates: when you change the definition of a view couch potato will now update the design document in the database (langalex)
|
5
|
+
* support for properties of type Date, better support for Time (langalex)
|
6
|
+
* support for default reduce count methods in custom views (jweiss)
|
7
|
+
|
3
8
|
### 0.2.24
|
4
9
|
* persistent instances can now be marked as dirty with #is_dirty (langalex)
|
5
10
|
|
data/README.md
CHANGED
@@ -90,7 +90,7 @@ Properties can be typed:
|
|
90
90
|
end
|
91
91
|
|
92
92
|
In this case Address also implements CouchPotato::Persistence which means its JSON representation will be added to the user document.
|
93
|
-
Couch Potato also has support for the basic types (right now Fixnum and :boolean are supported):
|
93
|
+
Couch Potato also has support for the basic types (right now Fixnum, Date, Time and :boolean are supported):
|
94
94
|
|
95
95
|
class User
|
96
96
|
include CouchPotato::Persistence
|
data/VERSION.yml
CHANGED
data/lib/couch_potato.rb
CHANGED
@@ -40,4 +40,4 @@ require File.dirname(__FILE__) + '/core_ext/string'
|
|
40
40
|
require File.dirname(__FILE__) + '/core_ext/symbol'
|
41
41
|
require File.dirname(__FILE__) + '/couch_potato/validation'
|
42
42
|
require File.dirname(__FILE__) + '/couch_potato/persistence'
|
43
|
-
|
43
|
+
require File.dirname(__FILE__) + '/couch_potato/railtie' if defined?(Rails)
|
@@ -66,17 +66,18 @@ module CouchPotato
|
|
66
66
|
instance
|
67
67
|
end
|
68
68
|
|
69
|
-
# Declare a property on a model class.
|
69
|
+
# Declare a property on a model class. Properties are not typed by default.
|
70
|
+
# You can store anything in a property that can be serialized into JSON.
|
71
|
+
# If you want a property to be of a custom class you have to define it using the :type option.
|
70
72
|
#
|
71
73
|
# example:
|
72
74
|
# class Book
|
73
75
|
# property :title
|
74
76
|
# property :year
|
75
|
-
# property :publisher, :
|
77
|
+
# property :publisher, :type => Publisher
|
76
78
|
# end
|
77
79
|
def property(name, options = {})
|
78
|
-
|
79
|
-
properties << (clazz || SimpleProperty).new(self, name, options)
|
80
|
+
properties << SimpleProperty.new(self, name, options)
|
80
81
|
end
|
81
82
|
|
82
83
|
end
|
@@ -13,21 +13,13 @@ module CouchPotato
|
|
13
13
|
|
14
14
|
def build(object, json)
|
15
15
|
value = json[name.to_s].nil? ? json[name.to_sym] : json[name.to_s]
|
16
|
-
object.send "#{name}=",
|
16
|
+
object.send "#{name}=", value
|
17
17
|
end
|
18
18
|
|
19
19
|
def dirty?(object)
|
20
20
|
object.send("#{name}_changed?")
|
21
21
|
end
|
22
22
|
|
23
|
-
def save(object)
|
24
|
-
|
25
|
-
end
|
26
|
-
|
27
|
-
def destroy(object)
|
28
|
-
|
29
|
-
end
|
30
|
-
|
31
23
|
def serialize(json, object)
|
32
24
|
json[name] = object.send name
|
33
25
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../rails/reload_classes')
|
2
|
+
|
3
|
+
module CouchPotato
|
4
|
+
|
5
|
+
def self.rails_init
|
6
|
+
CouchPotato::Config.database_name = YAML::load(File.read(Rails.root.join('config/couchdb.yml')))[Rails.env]
|
7
|
+
end
|
8
|
+
|
9
|
+
if Rails.version >= '3'
|
10
|
+
class Railtie < Rails::Railtie
|
11
|
+
railtie_name :couch_potato
|
12
|
+
|
13
|
+
config.after_initialize do |app|
|
14
|
+
CouchPotato.rails_init
|
15
|
+
end
|
16
|
+
end
|
17
|
+
else
|
18
|
+
rails_init
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
@@ -18,14 +18,25 @@ module CouchPotato
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def process_results(results)
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
21
|
+
if count?
|
22
|
+
results['rows'].first.try(:[], 'value') || 0
|
23
|
+
else
|
24
|
+
results['rows'].map do |row|
|
25
|
+
if row['doc'].instance_of?(klass)
|
26
|
+
row['doc']
|
27
|
+
else
|
28
|
+
klass.json_create row['doc'] || row['value'].merge(:_id => row['id'] || row['key'])
|
29
|
+
end
|
26
30
|
end
|
27
31
|
end
|
28
32
|
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def count?
|
37
|
+
view_parameters[:reduce]
|
38
|
+
end
|
39
|
+
|
29
40
|
end
|
30
41
|
end
|
31
42
|
end
|
@@ -11,24 +11,45 @@ module CouchPotato
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def query_view!(parameters = {})
|
14
|
+
update_view unless view_has_been_updated?
|
14
15
|
begin
|
15
16
|
query_view parameters
|
16
17
|
rescue RestClient::ResourceNotFound# => e
|
17
|
-
|
18
|
+
update_view
|
18
19
|
retry
|
19
20
|
end
|
20
21
|
end
|
21
22
|
|
22
23
|
private
|
23
24
|
|
24
|
-
def
|
25
|
+
def update_view
|
25
26
|
design_doc = @database.get "_design/#{@design_document_name}" rescue nil
|
26
|
-
design_doc
|
27
|
-
design_doc
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
27
|
+
original_views = design_doc && design_doc['views'].dup
|
28
|
+
view_updated unless design_doc.nil?
|
29
|
+
design_doc ||= empty_design_document
|
30
|
+
design_doc['views'][@view_name.to_s] = view_functions
|
31
|
+
@database.save_doc(design_doc) unless original_views == design_doc['views']
|
32
|
+
end
|
33
|
+
|
34
|
+
def view_functions
|
35
|
+
{'map' => @map_function, 'reduce' => @reduce_function}
|
36
|
+
end
|
37
|
+
|
38
|
+
def empty_design_document
|
39
|
+
{'views' => {}, "_id" => "_design/#{@design_document_name}", "language" => "javascript"}
|
40
|
+
end
|
41
|
+
|
42
|
+
def view_has_been_updated?
|
43
|
+
updated_views[[@design_document_name, @view_name]]
|
44
|
+
end
|
45
|
+
|
46
|
+
def view_updated
|
47
|
+
updated_views[[@design_document_name, @view_name]] = true
|
48
|
+
end
|
49
|
+
|
50
|
+
def updated_views
|
51
|
+
@@updated_views ||= {}
|
52
|
+
@@updated_views
|
32
53
|
end
|
33
54
|
|
34
55
|
def query_view(parameters)
|
data/rails/init.rb
CHANGED
@@ -1,8 +1,4 @@
|
|
1
1
|
# this is for rails only
|
2
2
|
|
3
3
|
require File.expand_path(File.dirname(__FILE__) + '/../lib/couch_potato')
|
4
|
-
|
5
|
-
|
6
|
-
CouchPotato::Config.database_name = YAML::load(File.read(Rails.root.to_s + '/config/couchdb.yml'))[RAILS_ENV]
|
7
|
-
|
8
|
-
RAILS_DEFAULT_LOGGER.info "** couch_potato: initialized from #{__FILE__}"
|
4
|
+
Rails.logger.info "** couch_potato: initialized from #{__FILE__}"
|
data/spec/custom_view_spec.rb
CHANGED
@@ -13,6 +13,7 @@ class Build
|
|
13
13
|
view :custom_timeline, :map => "function(doc) { emit(doc._id, {state: 'custom_' + doc.state}); }", :type => :custom
|
14
14
|
view :custom_timeline_returns_docs, :map => "function(doc) { emit(doc._id, null); }", :include_docs => true, :type => :custom
|
15
15
|
view :custom_with_reduce, :map => "function(doc) {if(doc.foreign_key) {emit(doc.foreign_key, 1);} else {emit(doc.id, 1)}}", :reduce => "function(key, values) {return({\"count\": sum(values)});}", :group => true, :type => :custom
|
16
|
+
view :custom_count_with_reduce, :map => "function(doc) {if(doc.foreign_key) {emit(doc.foreign_key, 1);} else {emit(doc.id, 1)}}", :reduce => "function(key, values) {return(sum(values));}", :group => true, :type => :custom
|
16
17
|
view :raw, :type => :raw, :map => "function(doc) {emit(doc._id, doc.state)}"
|
17
18
|
view :filtered_raw, :type => :raw, :map => "function(doc) {emit(doc._id, doc.state)}", :results_filter => lambda{|res| res['rows'].map{|row| row['value']}}
|
18
19
|
view :with_view_options, :group => true, :key => :time
|
@@ -130,7 +131,14 @@ describe 'view' do
|
|
130
131
|
doc = CouchPotato.couchrest_database.save_doc({})
|
131
132
|
CouchPotato.couchrest_database.save_doc({:foreign_key => doc['id']})
|
132
133
|
CouchPotato.database.view(Build.custom_with_reduce).map(&:_id).should == [doc['id']]
|
133
|
-
|
134
|
+
end
|
135
|
+
|
136
|
+
describe "when the additional reduce function is a typical count" do
|
137
|
+
it "should parse the reduce count" do
|
138
|
+
doc = CouchPotato.couchrest_database.save_doc({})
|
139
|
+
CouchPotato.couchrest_database.save_doc({:foreign_key => doc['id']})
|
140
|
+
CouchPotato.database.view(Build.custom_count_with_reduce(:reduce => true)).should == 1
|
141
|
+
end
|
134
142
|
end
|
135
143
|
end
|
136
144
|
end
|
data/spec/property_spec.rb
CHANGED
@@ -6,6 +6,7 @@ class Watch
|
|
6
6
|
include CouchPotato::Persistence
|
7
7
|
|
8
8
|
property :time, :type => Time
|
9
|
+
property :date, :type => Date
|
9
10
|
property :overwritten_read
|
10
11
|
property :overwritten_write
|
11
12
|
|
@@ -98,13 +99,6 @@ describe 'properties' do
|
|
98
99
|
]
|
99
100
|
it_should_persist something_very_complex
|
100
101
|
end
|
101
|
-
|
102
|
-
it "should persist a Time object" do
|
103
|
-
w = Watch.new :time => Time.now
|
104
|
-
CouchPotato.database.save_document! w
|
105
|
-
w = CouchPotato.database.load_document w.id
|
106
|
-
w.time.year.should == Time.now.year
|
107
|
-
end
|
108
102
|
|
109
103
|
it "should persist an object" do
|
110
104
|
p = Person.new
|
@@ -142,6 +136,68 @@ describe 'properties' do
|
|
142
136
|
p.ship_address.should be_false
|
143
137
|
end
|
144
138
|
|
139
|
+
describe "time properties" do
|
140
|
+
it "should persist a Time as utc" do
|
141
|
+
time = Time.now
|
142
|
+
w = Watch.new :time => time
|
143
|
+
CouchPotato.database.save_document! w
|
144
|
+
w = CouchPotato.database.load_document w.id
|
145
|
+
w.time.to_s.should == time.utc.to_s
|
146
|
+
end
|
147
|
+
|
148
|
+
it "should parse a string and persist it as time" do
|
149
|
+
w = Watch.new :time => '2009-01-01 13:25 UTC'
|
150
|
+
CouchPotato.database.save_document! w
|
151
|
+
w = CouchPotato.database.load_document w.id
|
152
|
+
w.time.should be_a(Time)
|
153
|
+
end
|
154
|
+
|
155
|
+
it "should store nil" do
|
156
|
+
w = Watch.new :time => nil
|
157
|
+
CouchPotato.database.save_document! w
|
158
|
+
w = CouchPotato.database.load_document w.id
|
159
|
+
w.time.should be_nil
|
160
|
+
end
|
161
|
+
|
162
|
+
it "should store an empty string as nil" do
|
163
|
+
w = Watch.new :time => ''
|
164
|
+
CouchPotato.database.save_document! w
|
165
|
+
w = CouchPotato.database.load_document w.id
|
166
|
+
w.time.should be_nil
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
describe "date properties" do
|
171
|
+
it "should persist a date" do
|
172
|
+
date = Date.today
|
173
|
+
w = Watch.new :date => date
|
174
|
+
CouchPotato.database.save_document! w
|
175
|
+
w = CouchPotato.database.load_document w.id
|
176
|
+
w.date.should == date
|
177
|
+
end
|
178
|
+
|
179
|
+
it "should parse a string and persist it as a date" do
|
180
|
+
w = Watch.new :date => '2009-01-10'
|
181
|
+
CouchPotato.database.save_document! w
|
182
|
+
w = CouchPotato.database.load_document w.id
|
183
|
+
w.date.should == Date.parse('2009-01-10')
|
184
|
+
end
|
185
|
+
|
186
|
+
it "should store nil" do
|
187
|
+
w = Watch.new :date => nil
|
188
|
+
CouchPotato.database.save_document! w
|
189
|
+
w = CouchPotato.database.load_document w.id
|
190
|
+
w.date.should be_nil
|
191
|
+
end
|
192
|
+
|
193
|
+
it "should store an empty string as nil" do
|
194
|
+
w = Watch.new :date => ''
|
195
|
+
CouchPotato.database.save_document! w
|
196
|
+
w = CouchPotato.database.load_document w.id
|
197
|
+
w.date.should be_nil
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
145
201
|
describe "boolean properties" do
|
146
202
|
it "should persist '0' for false" do
|
147
203
|
a = Address.new
|
data/spec/spec_helper.rb
CHANGED
@@ -5,7 +5,13 @@ $:.unshift(File.dirname(__FILE__) + '/../lib')
|
|
5
5
|
|
6
6
|
require 'couch_potato'
|
7
7
|
|
8
|
-
|
8
|
+
if ENV["RUN_CODE_RUN"]
|
9
|
+
CouchPotato::Config.database_name = 'http://langalex.couch.io/couch_potato_test'
|
10
|
+
else
|
11
|
+
CouchPotato::Config.database_name = 'couch_potato_test'
|
12
|
+
end
|
13
|
+
|
14
|
+
|
9
15
|
CouchPotato::Config.validation_framework = ENV['VALIDATION_FRAMEWORK'].to_sym unless ENV['VALIDATION_FRAMEWORK'].blank?
|
10
16
|
|
11
17
|
# silence deprecation warnings from ActiveModel as the Spec uses Errors#on
|
@@ -2,8 +2,20 @@ require File.dirname(__FILE__) + '/../spec_helper'
|
|
2
2
|
|
3
3
|
describe CouchPotato::View::ViewQuery, 'query_view' do
|
4
4
|
it "should not pass a key if conditions are empty" do
|
5
|
-
db = mock 'db'
|
5
|
+
db = mock 'db', :get => nil, :save_doc => nil
|
6
6
|
db.should_receive(:view).with(anything, {})
|
7
7
|
CouchPotato::View::ViewQuery.new(db, '', '', '', '').query_view!
|
8
8
|
end
|
9
|
+
|
10
|
+
it "should not update a view when the functions haven't changed" do
|
11
|
+
db = mock 'db', :get => {'views' => {'view' => {'map' => 'map', 'reduce' => 'reduce'}}}, :view => nil
|
12
|
+
db.should_not_receive(:save_doc)
|
13
|
+
CouchPotato::View::ViewQuery.new(db, 'design', 'view', 'map', 'reduce').query_view!
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should update a view when the functions have changed" do
|
17
|
+
db = mock 'db', :get => {'views' => {'view2' => {'map' => 'map', 'reduce' => 'reduce'}}}, :view => nil
|
18
|
+
db.should_receive(:save_doc)
|
19
|
+
CouchPotato::View::ViewQuery.new(db, 'design', 'view2', 'mapnew', 'reduce').query_view!
|
20
|
+
end
|
9
21
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
describe "automatic view updates" do
|
4
|
+
before(:each) do
|
5
|
+
recreate_db
|
6
|
+
@db = CouchPotato.couchrest_database
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should update a view that doesn't match the given functions" do
|
10
|
+
CouchPotato::View::ViewQuery.new(@db, 'test_design1', 'test_view', 'function(doc) {}', 'function() {}').query_view! # create view
|
11
|
+
CouchPotato::View::ViewQuery.new(@db, 'test_design1', 'test_view', 'function(doc) {emit(doc.id, null)}', 'function(key, values) {return sum(values)}').query_view!
|
12
|
+
CouchPotato.database.load('_design/test_design1')['views']['test_view'].should == {
|
13
|
+
'map' => 'function(doc) {emit(doc.id, null)}',
|
14
|
+
'reduce' => 'function(key, values) {return sum(values)}'
|
15
|
+
}
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should only update a view once to avoid writing the view for every request" do
|
19
|
+
CouchPotato::View::ViewQuery.new(@db, 'test_design2', 'test_view', 'function(doc) {}', 'function() {}').query_view! # create view
|
20
|
+
CouchPotato::View::ViewQuery.new(@db, 'test_design2', 'test_view', 'function(doc) {emit(doc.id, null)}', 'function(key, values) {return sum(values)}').query_view!
|
21
|
+
CouchPotato::View::ViewQuery.new(@db, 'test_design2', 'test_view', 'function(doc) {}', 'function() {}').query_view!
|
22
|
+
CouchPotato.database.load('_design/test_design2')['views']['test_view'].should == {
|
23
|
+
'map' => 'function(doc) {emit(doc.id, null)}',
|
24
|
+
'reduce' => 'function(key, values) {return sum(values)}'
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: couch_potato
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.25
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alexander Lang
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2010-02-
|
12
|
+
date: 2010-02-17 00:00:00 +01:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -74,6 +74,7 @@ files:
|
|
74
74
|
- lib/couch_potato/persistence/properties.rb
|
75
75
|
- lib/couch_potato/persistence/simple_property.rb
|
76
76
|
- lib/couch_potato/persistence/type_caster.rb
|
77
|
+
- lib/couch_potato/railtie.rb
|
77
78
|
- lib/couch_potato/rspec/matchers.rb
|
78
79
|
- lib/couch_potato/rspec/matchers/map_to_matcher.rb
|
79
80
|
- lib/couch_potato/rspec/matchers/print_r.js
|
@@ -118,6 +119,7 @@ files:
|
|
118
119
|
- spec/unit/validation_spec.rb
|
119
120
|
- spec/unit/view_query_spec.rb
|
120
121
|
- spec/update_spec.rb
|
122
|
+
- spec/view_updates_spec.rb
|
121
123
|
has_rdoc: true
|
122
124
|
homepage: http://github.com/langalex/couch_potato
|
123
125
|
licenses: []
|
@@ -174,3 +176,4 @@ test_files:
|
|
174
176
|
- spec/unit/validation_spec.rb
|
175
177
|
- spec/unit/view_query_spec.rb
|
176
178
|
- spec/update_spec.rb
|
179
|
+
- spec/view_updates_spec.rb
|