couch_potato 0.2.24 → 0.2.25
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.
- 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
|