couch_potato 1.4.0 → 1.6.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.ruby-version +1 -0
- data/.travis.yml +12 -8
- data/CHANGES.md +4 -0
- data/Gemfile +1 -1
- data/README.md +396 -276
- data/Rakefile +9 -9
- data/couch_potato-rspec.gemspec +20 -0
- data/couch_potato.gemspec +15 -16
- data/{active_support_4_0 → gemfiles/active_support_4_0} +3 -3
- data/{active_support_3_2 → gemfiles/active_support_4_1} +3 -2
- data/gemfiles/active_support_4_2 +11 -0
- data/lib/couch_potato-rspec.rb +3 -0
- data/lib/couch_potato.rb +3 -1
- data/lib/couch_potato/database.rb +42 -39
- data/lib/couch_potato/persistence/magic_timestamps.rb +5 -5
- data/lib/couch_potato/persistence/properties.rb +8 -2
- data/lib/couch_potato/persistence/simple_property.rb +11 -9
- data/lib/couch_potato/persistence/type_caster.rb +1 -1
- data/lib/couch_potato/railtie.rb +2 -0
- data/lib/couch_potato/version.rb +2 -1
- data/lib/couch_potato/view/base_view_spec.rb +18 -8
- data/lib/couch_potato/view/view_query.rb +2 -3
- data/spec/attachments_spec.rb +3 -3
- data/spec/callbacks_spec.rb +193 -113
- data/spec/conflict_handling_spec.rb +4 -4
- data/spec/create_spec.rb +5 -5
- data/spec/default_property_spec.rb +6 -6
- data/spec/destroy_spec.rb +5 -5
- data/spec/property_spec.rb +71 -61
- data/spec/rails_spec.rb +3 -3
- data/spec/railtie_spec.rb +12 -13
- data/spec/spec_helper.rb +3 -3
- data/spec/unit/active_model_compliance_spec.rb +16 -16
- data/spec/unit/attributes_spec.rb +36 -34
- data/spec/unit/base_view_spec_spec.rb +82 -35
- data/spec/unit/callbacks_spec.rb +2 -2
- data/spec/unit/couch_potato_spec.rb +3 -3
- data/spec/unit/create_spec.rb +12 -12
- data/spec/unit/custom_views_spec.rb +1 -1
- data/spec/unit/database_spec.rb +95 -95
- data/spec/unit/date_spec.rb +3 -3
- data/spec/unit/deep_dirty_attributes_spec.rb +104 -104
- data/spec/unit/dirty_attributes_spec.rb +19 -19
- data/spec/unit/forbidden_attributes_protection_spec.rb +4 -4
- data/spec/unit/initialize_spec.rb +37 -19
- data/spec/unit/json_spec.rb +4 -4
- data/spec/unit/lists_spec.rb +8 -8
- data/spec/unit/model_view_spec_spec.rb +14 -14
- data/spec/unit/persistence_spec.rb +6 -6
- data/spec/unit/properties_view_spec_spec.rb +4 -4
- data/spec/unit/rspec_matchers_spec.rb +73 -73
- data/spec/unit/rspec_stub_db_spec.rb +43 -42
- data/spec/unit/string_spec.rb +1 -1
- data/spec/unit/time_spec.rb +2 -2
- data/spec/unit/validation_spec.rb +1 -1
- data/spec/unit/view_query_spec.rb +54 -59
- data/spec/update_spec.rb +5 -5
- data/spec/view_updates_spec.rb +4 -4
- data/spec/views_spec.rb +43 -43
- metadata +18 -22
- data/lib/couch_potato/rspec.rb +0 -2
- data/lib/couch_potato/rspec/matchers.rb +0 -56
- data/lib/couch_potato/rspec/matchers/json2.js +0 -482
- data/lib/couch_potato/rspec/matchers/list_as_matcher.rb +0 -53
- data/lib/couch_potato/rspec/matchers/map_reduce_to_matcher.rb +0 -166
- data/lib/couch_potato/rspec/matchers/map_to_matcher.rb +0 -61
- data/lib/couch_potato/rspec/matchers/reduce_to_matcher.rb +0 -48
- data/lib/couch_potato/rspec/stub_db.rb +0 -57
data/Rakefile
CHANGED
@@ -1,34 +1,34 @@
|
|
1
1
|
require 'bundler'
|
2
|
-
Bundler::GemHelper.install_tasks
|
2
|
+
Bundler::GemHelper.install_tasks name: 'couch_potato'
|
3
3
|
|
4
4
|
require 'rake'
|
5
|
-
require
|
5
|
+
require 'rspec/core/rake_task'
|
6
6
|
|
7
|
-
task :
|
7
|
+
task default: :spec
|
8
8
|
|
9
|
-
desc
|
9
|
+
desc 'Run functional specs'
|
10
10
|
RSpec::Core::RakeTask.new(:spec_functional) do |spec|
|
11
11
|
spec.pattern = 'spec/*_spec.rb'
|
12
12
|
spec.rspec_opts = ['--options', "\"#{File.dirname(__FILE__)}/spec/spec.opts\""]
|
13
13
|
end
|
14
14
|
|
15
|
-
desc
|
15
|
+
desc 'Run unit specs'
|
16
16
|
RSpec::Core::RakeTask.new(:spec_unit) do |spec|
|
17
17
|
spec.pattern = 'spec/unit/*_spec.rb'
|
18
18
|
spec.rspec_opts = ['--options', "\"#{File.dirname(__FILE__)}/spec/spec.opts\""]
|
19
19
|
end
|
20
20
|
|
21
|
-
desc
|
21
|
+
desc 'Run all specs'
|
22
22
|
task :spec do
|
23
23
|
if ENV['TRAVIS'] # travis handles the environments for us
|
24
24
|
Rake::Task[:spec_unit].execute
|
25
25
|
Rake::Task[:spec_functional].execute
|
26
26
|
else
|
27
|
-
|
27
|
+
%w(4_0 4_1 4_2).each do |version|
|
28
28
|
Bundler.with_clean_env do
|
29
29
|
puts "Running tests with ActiveSupport #{version.sub('_', '.')}"
|
30
|
-
sh "env BUNDLE_GEMFILE=active_support_#{version} bundle install"
|
31
|
-
sh "env BUNDLE_GEMFILE=active_support_#{version} bundle exec rake spec_unit spec_functional"
|
30
|
+
sh "env BUNDLE_GEMFILE=gemfiles/active_support_#{version} bundle install"
|
31
|
+
sh "env BUNDLE_GEMFILE=gemfiles/active_support_#{version} bundle exec rake spec_unit spec_functional"
|
32
32
|
end
|
33
33
|
end
|
34
34
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
$LOAD_PATH.push File.expand_path('../lib', __FILE__)
|
2
|
+
require 'couch_potato/version'
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = 'couch_potato-rspec'
|
6
|
+
s.summary = 'RSpec matchers for Couch Potato'
|
7
|
+
s.email = 'alex@upstre.am'
|
8
|
+
s.homepage = 'http://github.com/langalex/couch_potato'
|
9
|
+
s.description = 'RSpec matchers for Couch Potato'
|
10
|
+
s.authors = ['Alexander Lang']
|
11
|
+
s.version = CouchPotato::RSPEC_VERSION
|
12
|
+
s.platform = Gem::Platform::RUBY
|
13
|
+
|
14
|
+
s.add_dependency 'rspec', '~>3.0'
|
15
|
+
s.add_development_dependency 'rake'
|
16
|
+
|
17
|
+
s.files = `git ls-files | grep "lib/couch_potato/rspec"`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/* | grep rspec_matchers`.split("\n")
|
19
|
+
s.require_paths = ['lib']
|
20
|
+
end
|
data/couch_potato.gemspec
CHANGED
@@ -1,28 +1,27 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require "couch_potato/version"
|
1
|
+
$LOAD_PATH.push File.expand_path('../lib', __FILE__)
|
2
|
+
require 'couch_potato/version'
|
4
3
|
|
5
4
|
Gem::Specification.new do |s|
|
6
|
-
s.name =
|
7
|
-
s.summary =
|
8
|
-
s.email =
|
9
|
-
s.homepage =
|
10
|
-
s.description =
|
11
|
-
s.authors = [
|
5
|
+
s.name = 'couch_potato'
|
6
|
+
s.summary = 'Ruby persistence layer for CouchDB'
|
7
|
+
s.email = 'alex@upstre.am'
|
8
|
+
s.homepage = 'http://github.com/langalex/couch_potato'
|
9
|
+
s.description = 'Ruby persistence layer for CouchDB'
|
10
|
+
s.authors = ['Alexander Lang']
|
12
11
|
s.version = CouchPotato::VERSION
|
13
12
|
s.platform = Gem::Platform::RUBY
|
14
13
|
|
15
14
|
s.add_dependency 'json', '~> 1.6'
|
16
|
-
s.add_dependency 'couchrest', '~>
|
17
|
-
s.add_dependency 'activemodel'
|
15
|
+
s.add_dependency 'couchrest', '~>2.0.0.rc3'
|
16
|
+
s.add_dependency 'activemodel', '~> 4.0'
|
18
17
|
|
19
|
-
s.add_development_dependency 'rspec', '~>2.
|
18
|
+
s.add_development_dependency 'rspec', '~>3.2.0'
|
20
19
|
s.add_development_dependency 'timecop'
|
21
20
|
s.add_development_dependency 'tzinfo'
|
22
21
|
s.add_development_dependency 'rake'
|
23
22
|
|
24
|
-
s.files = `git ls-files`.split("\n")
|
25
|
-
s.test_files = `git ls-files -- {test,spec,features}
|
26
|
-
s.executables = `git ls-files -- bin/*`.split("\n").map{
|
27
|
-
s.require_paths = [
|
23
|
+
s.files = `git ls-files | grep -v "lib/couch_potato/rspec"`.split("\n")
|
24
|
+
s.test_files = `git ls-files -- {test,spec,features}/* | grep -v rspec_matchers`.split("\n")
|
25
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map {|f| File.basename(f) }
|
26
|
+
s.require_paths = ['lib']
|
28
27
|
end
|
@@ -1,11 +1,11 @@
|
|
1
1
|
source 'https://rubygems.org'
|
2
2
|
|
3
|
-
gem 'activemodel', '~>4.0.0
|
4
|
-
gem 'rails', '~>4.0.0
|
3
|
+
gem 'activemodel', '~>4.0.0'
|
4
|
+
gem 'rails', '~>4.0.0'
|
5
5
|
if RUBY_PLATFORM =~ /java/
|
6
6
|
gem 'therubyrhino'
|
7
7
|
else
|
8
8
|
gem 'therubyracer'
|
9
9
|
end
|
10
10
|
|
11
|
-
gemspec
|
11
|
+
gemspec name: 'couch_potato', path: '..'
|
@@ -1,10 +1,11 @@
|
|
1
1
|
source 'https://rubygems.org'
|
2
2
|
|
3
|
-
gem 'activemodel', '~>
|
3
|
+
gem 'activemodel', '~>4.1.0'
|
4
|
+
gem 'rails', '~>4.1.0'
|
4
5
|
if RUBY_PLATFORM =~ /java/
|
5
6
|
gem 'therubyrhino'
|
6
7
|
else
|
7
8
|
gem 'therubyracer'
|
8
9
|
end
|
9
10
|
|
10
|
-
gemspec
|
11
|
+
gemspec name: 'couch_potato', path: '..'
|
data/lib/couch_potato.rb
CHANGED
@@ -7,8 +7,10 @@ JSON.create_id = 'ruby_class'
|
|
7
7
|
CouchRest.decode_json_objects = true
|
8
8
|
|
9
9
|
module CouchPotato
|
10
|
-
Config = Struct.new(:database_host, :database_name, :
|
10
|
+
Config = Struct.new(:database_host, :database_name, :digest_view_names,
|
11
|
+
:split_design_documents_per_view, :default_language).new
|
11
12
|
Config.split_design_documents_per_view = false
|
13
|
+
Config.digest_view_names = false
|
12
14
|
Config.default_language = :javascript
|
13
15
|
Config.database_host = "http://127.0.0.1:5984"
|
14
16
|
|
@@ -37,23 +37,25 @@ module CouchPotato
|
|
37
37
|
#
|
38
38
|
# db.view(User.all(keys: [1, 2, 3]))
|
39
39
|
def view(spec)
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
40
|
+
ActiveSupport::Notifications.instrument('couch_potato.view') do
|
41
|
+
results = CouchPotato::View::ViewQuery.new(
|
42
|
+
couchrest_database,
|
43
|
+
spec.design_document,
|
44
|
+
{spec.view_name => {
|
45
|
+
:map => spec.map_function,
|
46
|
+
:reduce => spec.reduce_function
|
47
|
+
}
|
48
|
+
},
|
49
|
+
({spec.list_name => spec.list_function} unless spec.list_name.nil?),
|
50
|
+
spec.lib,
|
51
|
+
spec.language
|
52
|
+
).query_view!(spec.view_parameters)
|
53
|
+
processed_results = spec.process_results results
|
54
|
+
processed_results.each do |document|
|
55
|
+
document.database = self if document.respond_to?(:database=)
|
56
|
+
end if processed_results.respond_to?(:each)
|
57
|
+
processed_results
|
58
|
+
end
|
57
59
|
end
|
58
60
|
|
59
61
|
# returns the first result from a #view query or nil
|
@@ -71,22 +73,15 @@ module CouchPotato
|
|
71
73
|
# if passed a block will:
|
72
74
|
# * yield the object to be saved to the block and run if once before saving
|
73
75
|
# * on conflict: reload the document, run the block again and retry saving
|
74
|
-
def save_document(document, validate = true, &block)
|
75
|
-
retries = 0
|
76
|
+
def save_document(document, validate = true, retries = 0, &block)
|
76
77
|
begin
|
77
78
|
block.call document if block
|
78
79
|
save_document_without_conflict_handling(document, validate)
|
79
|
-
rescue
|
80
|
+
rescue CouchRest::Conflict => e
|
80
81
|
if block
|
81
|
-
document
|
82
|
-
if retries == 5
|
83
|
-
raise CouchPotato::Conflict.new
|
84
|
-
else
|
85
|
-
retries += 1
|
86
|
-
retry
|
87
|
-
end
|
82
|
+
handle_write_conflict document, validate, retries, &block
|
88
83
|
else
|
89
|
-
raise
|
84
|
+
raise CouchPotato::Conflict.new
|
90
85
|
end
|
91
86
|
end
|
92
87
|
end
|
@@ -101,7 +96,7 @@ module CouchPotato
|
|
101
96
|
def destroy_document(document)
|
102
97
|
begin
|
103
98
|
destroy_document_without_conflict_handling document
|
104
|
-
rescue
|
99
|
+
rescue CouchRest::Conflict
|
105
100
|
retry if document = document.reload
|
106
101
|
end
|
107
102
|
end
|
@@ -115,15 +110,13 @@ module CouchPotato
|
|
115
110
|
def load_document(id)
|
116
111
|
raise "Can't load a document without an id (got nil)" if id.nil?
|
117
112
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
113
|
+
ActiveSupport::Notifications.instrument('couch_potato.load') do
|
114
|
+
if id.is_a?(Array)
|
115
|
+
bulk_load id
|
116
|
+
else
|
122
117
|
instance = couchrest_database.get(id)
|
123
|
-
instance.database = self
|
118
|
+
instance.database = self if instance
|
124
119
|
instance
|
125
|
-
rescue(RestClient::ResourceNotFound)
|
126
|
-
nil
|
127
120
|
end
|
128
121
|
end
|
129
122
|
end
|
@@ -151,6 +144,15 @@ module CouchPotato
|
|
151
144
|
|
152
145
|
private
|
153
146
|
|
147
|
+
def handle_write_conflict(document, validate, retries, &block)
|
148
|
+
document = document.reload
|
149
|
+
if retries == 5
|
150
|
+
raise CouchPotato::Conflict.new
|
151
|
+
else
|
152
|
+
save_document document, validate, retries + 1, &block
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
154
156
|
def destroy_document_without_conflict_handling(document)
|
155
157
|
document.run_callbacks :destroy do
|
156
158
|
document._deleted = true
|
@@ -161,7 +163,6 @@ module CouchPotato
|
|
161
163
|
end
|
162
164
|
|
163
165
|
def save_document_without_conflict_handling(document, validate = true)
|
164
|
-
return true unless document.dirty? || document.new?
|
165
166
|
if document.new?
|
166
167
|
create_document(document, validate)
|
167
168
|
else
|
@@ -211,8 +212,10 @@ module CouchPotato
|
|
211
212
|
|
212
213
|
return false if false == document.run_callbacks(:save) do
|
213
214
|
return false if false == document.run_callbacks(:update) do
|
214
|
-
|
215
|
-
|
215
|
+
if document.dirty?
|
216
|
+
res = couchrest_database.save_doc document.to_hash
|
217
|
+
document._rev = res['rev']
|
218
|
+
end
|
216
219
|
end
|
217
220
|
end
|
218
221
|
true
|
@@ -6,18 +6,18 @@ module CouchPotato
|
|
6
6
|
base.instance_eval do
|
7
7
|
property :created_at, :type => Time
|
8
8
|
property :updated_at, :type => Time
|
9
|
-
|
9
|
+
|
10
10
|
before_create lambda {|model|
|
11
11
|
model.created_at ||= (Time.zone || Time).now
|
12
|
-
@changed_attributes.delete 'created_at'
|
12
|
+
@changed_attributes.try :delete, 'created_at'
|
13
13
|
model.updated_at ||= (Time.zone || Time).now
|
14
|
-
@changed_attributes.delete 'updated_at'
|
14
|
+
@changed_attributes.try :delete, 'updated_at'
|
15
15
|
}
|
16
16
|
before_update lambda {|model|
|
17
17
|
model.updated_at = (Time.zone || Time).now
|
18
|
-
@changed_attributes.delete 'updated_at'
|
18
|
+
@changed_attributes.try :delete, 'updated_at'
|
19
19
|
}
|
20
20
|
end
|
21
21
|
end
|
22
22
|
end
|
23
|
-
end
|
23
|
+
end
|
@@ -12,16 +12,22 @@ module CouchPotato
|
|
12
12
|
def initialize(clazz)
|
13
13
|
@clazz = clazz
|
14
14
|
@list = []
|
15
|
+
@hash = {}
|
15
16
|
end
|
16
17
|
|
17
|
-
def each
|
18
|
-
(list + inherited_properties).each
|
18
|
+
def each(&block)
|
19
|
+
(list + inherited_properties).each(&block)
|
19
20
|
end
|
20
21
|
|
21
22
|
def <<(property)
|
23
|
+
@hash[property.name] = property
|
22
24
|
@list << property
|
23
25
|
end
|
24
26
|
|
27
|
+
def find_property(name)
|
28
|
+
@hash[name]
|
29
|
+
end
|
30
|
+
|
25
31
|
# XXX
|
26
32
|
def inspect
|
27
33
|
list.map(&:name).inspect
|
@@ -4,8 +4,8 @@ module CouchPotato
|
|
4
4
|
private
|
5
5
|
|
6
6
|
def load_attribute_from_document(name)
|
7
|
-
if _document.has_key?(name
|
8
|
-
property = self.class.properties.
|
7
|
+
if _document.has_key?(name)
|
8
|
+
property = self.class.properties.find_property name
|
9
9
|
@skip_dirty_tracking = true
|
10
10
|
value = property.build(self, _document)
|
11
11
|
@skip_dirty_tracking = false
|
@@ -19,6 +19,7 @@ module CouchPotato
|
|
19
19
|
|
20
20
|
def initialize(owner_clazz, name, options = {})
|
21
21
|
self.name = name
|
22
|
+
@setter_name = "#{name}="
|
22
23
|
self.type = options[:type]
|
23
24
|
@type_caster = TypeCaster.new
|
24
25
|
owner_clazz.send :include, PropertyMethods unless owner_clazz.ancestors.include?(PropertyMethods)
|
@@ -27,8 +28,8 @@ module CouchPotato
|
|
27
28
|
end
|
28
29
|
|
29
30
|
def build(object, json)
|
30
|
-
value = json[name
|
31
|
-
object.send
|
31
|
+
value = json[name]
|
32
|
+
object.send @setter_name, value
|
32
33
|
end
|
33
34
|
|
34
35
|
def dirty?(object)
|
@@ -56,10 +57,11 @@ module CouchPotato
|
|
56
57
|
end
|
57
58
|
|
58
59
|
def define_accessors(base, name, options)
|
60
|
+
ivar_name = "@#{name}".freeze
|
59
61
|
base.class_eval do
|
60
|
-
define_method
|
61
|
-
load_attribute_from_document(name) unless instance_variable_defined?(
|
62
|
-
value = instance_variable_get(
|
62
|
+
define_method name do
|
63
|
+
load_attribute_from_document(name) unless instance_variable_defined?(ivar_name)
|
64
|
+
value = instance_variable_get(ivar_name)
|
63
65
|
if value.nil? && !options[:default].nil?
|
64
66
|
default = if options[:default].respond_to?(:call)
|
65
67
|
if options[:default].arity == 1
|
@@ -70,7 +72,7 @@ module CouchPotato
|
|
70
72
|
else
|
71
73
|
clone_attribute(options[:default])
|
72
74
|
end
|
73
|
-
self.instance_variable_set(
|
75
|
+
self.instance_variable_set(ivar_name, default)
|
74
76
|
default
|
75
77
|
else
|
76
78
|
value
|
@@ -80,7 +82,7 @@ module CouchPotato
|
|
80
82
|
define_method "#{name}=" do |value|
|
81
83
|
typecasted_value = type_caster.cast(value, options[:type])
|
82
84
|
send("#{name}_will_change!") unless @skip_dirty_tracking || typecasted_value == send(name)
|
83
|
-
self.instance_variable_set(
|
85
|
+
self.instance_variable_set(ivar_name, typecasted_value)
|
84
86
|
end
|
85
87
|
|
86
88
|
define_method "#{name}?" do
|
data/lib/couch_potato/railtie.rb
CHANGED
@@ -5,12 +5,14 @@ module CouchPotato
|
|
5
5
|
def self.rails_init
|
6
6
|
path = Rails.root.join('config/couchdb.yml')
|
7
7
|
if File.exist?(path)
|
8
|
+
require 'yaml'
|
8
9
|
config = YAML::load(ERB.new(File.read(path)).result)[Rails.env]
|
9
10
|
if config.is_a?(String)
|
10
11
|
CouchPotato::Config.database_name = config
|
11
12
|
else
|
12
13
|
CouchPotato::Config.database_name = config['database']
|
13
14
|
CouchPotato::Config.split_design_documents_per_view = config['split_design_documents_per_view'] if config['split_design_documents_per_view']
|
15
|
+
CouchPotato::Config.digest_view_names = config['digest_view_names'] if config['digest_view_names']
|
14
16
|
CouchPotato::Config.default_language = config['default_language'] if config['default_language']
|
15
17
|
end
|
16
18
|
else
|
data/lib/couch_potato/version.rb
CHANGED