couchrest_model 1.0.0.beta8 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +9 -0
- data/{spec/spec.opts → .rspec} +0 -1
- data/Gemfile +4 -0
- data/Gemfile.lock +77 -0
- data/README.md +144 -57
- data/Rakefile +12 -43
- data/VERSION +1 -0
- data/couchrest_model.gemspec +35 -0
- data/history.txt +23 -1
- data/init.rb +1 -0
- data/lib/couchrest/model/associations.rb +17 -1
- data/lib/couchrest/model/base.rb +5 -5
- data/lib/couchrest/model/casted_model.rb +2 -2
- data/lib/couchrest/model/class_proxy.rb +6 -0
- data/lib/couchrest/model/collection.rb +3 -0
- data/lib/couchrest/model/configuration.rb +51 -0
- data/lib/couchrest/model/design_doc.rb +2 -5
- data/lib/couchrest/model/document_queries.rb +20 -3
- data/lib/couchrest/model/persistence.rb +15 -1
- data/lib/couchrest/model/properties.rb +97 -23
- data/lib/couchrest/model/property.rb +5 -4
- data/lib/couchrest/model/property_protection.rb +71 -0
- data/lib/couchrest/model/typecast.rb +12 -7
- data/lib/couchrest/model/view.rb +190 -0
- data/lib/couchrest/model/views.rb +3 -3
- data/lib/couchrest/model.rb +1 -1
- data/lib/couchrest/railtie.rb +3 -2
- data/lib/couchrest_model.rb +7 -14
- data/lib/rails/generators/couchrest_model/model/model_generator.rb +2 -1
- data/spec/.gitignore +1 -0
- data/spec/couchrest/base_spec.rb +3 -3
- data/spec/couchrest/casted_model_spec.rb +63 -49
- data/spec/couchrest/class_proxy_spec.rb +6 -0
- data/spec/couchrest/configuration_spec.rb +78 -0
- data/spec/couchrest/persistence_spec.rb +10 -4
- data/spec/couchrest/{attribute_protection_spec.rb → property_protection_spec.rb} +29 -2
- data/spec/couchrest/property_spec.rb +61 -0
- data/spec/couchrest/subclass_spec.rb +2 -2
- data/spec/couchrest/view_spec.rb +6 -0
- data/spec/fixtures/more/article.rb +1 -1
- data/spec/spec_helper.rb +4 -3
- metadata +96 -32
- data/lib/couchrest/model/attribute_protection.rb +0 -74
- data/lib/couchrest/model/attributes.rb +0 -75
@@ -0,0 +1,190 @@
|
|
1
|
+
|
2
|
+
#### NOTE Work in progress! Not yet used!
|
3
|
+
|
4
|
+
module CouchRest
|
5
|
+
module Model
|
6
|
+
|
7
|
+
# A proxy class that allows view queries to be created using
|
8
|
+
# chained method calls. After each call a new instance of the method
|
9
|
+
# is created based on the original in a similar fashion to ruby's sequel
|
10
|
+
# library, or Rails 3's Arel.
|
11
|
+
#
|
12
|
+
# CouchDB views have inherent limitations, so joins and filters as used in
|
13
|
+
# a normal relational database are not possible. At least not yet!
|
14
|
+
#
|
15
|
+
#
|
16
|
+
#
|
17
|
+
class View
|
18
|
+
|
19
|
+
attr_accessor :query, :design, :database, :name
|
20
|
+
|
21
|
+
# Initialize a new View object. This method should not be called from outside CouchRest Model.
|
22
|
+
def initialize(parent, new_query = {}, name = nil)
|
23
|
+
if parent.is_a? Base
|
24
|
+
raise "Name must be provided for view to be initialized" if name.nil?
|
25
|
+
@name = name
|
26
|
+
@database = parent.database
|
27
|
+
@query = { :reduce => false }
|
28
|
+
elsif parent.is_a? View
|
29
|
+
@database = parent.database
|
30
|
+
@query = parent.query.dup
|
31
|
+
else
|
32
|
+
raise "View cannot be initialized without a parent Model or View"
|
33
|
+
end
|
34
|
+
@query.update(new_query)
|
35
|
+
super
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
# == View Execution Methods
|
40
|
+
#
|
41
|
+
# Send a request to the CouchDB database using the current query values.
|
42
|
+
|
43
|
+
# Inmediatly send a request to the database for all documents provided by the query.
|
44
|
+
#
|
45
|
+
def all(&block)
|
46
|
+
args = include_docs.query
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
# Inmediatly send a request for the first result of the dataset. This will override
|
51
|
+
# any limit set in the view previously.
|
52
|
+
def first(&block)
|
53
|
+
args = limit(1).include_docs.query
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
def info
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
def offset
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
def total_rows
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
def rows
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
|
74
|
+
# == View Filter Methods
|
75
|
+
#
|
76
|
+
# View filters return an copy of the view instance with the query
|
77
|
+
# modified appropriatly. Errors will be raised if the methods
|
78
|
+
# are combined in an incorrect fashion.
|
79
|
+
#
|
80
|
+
|
81
|
+
|
82
|
+
# Find all entries in the index whose key matches the value provided.
|
83
|
+
#
|
84
|
+
# Cannot be used when the +#startkey+ or +#endkey+ have been set.
|
85
|
+
def key(value)
|
86
|
+
raise "View#key cannot be used when startkey or endkey have been set" unless query[:startkey].nil? && query[:endkey].nil?
|
87
|
+
update_query(:key => value)
|
88
|
+
end
|
89
|
+
|
90
|
+
# Find all index keys that start with the value provided. May or may not be used in
|
91
|
+
# conjunction with the +endkey+ option.
|
92
|
+
#
|
93
|
+
# When the +#descending+ option is used (not the default), the start and end keys should
|
94
|
+
# be reversed.
|
95
|
+
#
|
96
|
+
# Cannot be used if the key has been set.
|
97
|
+
def startkey(value)
|
98
|
+
raise "View#startkey cannot be used when key has been set" unless query[:key].nil?
|
99
|
+
update_query(:startkey => value)
|
100
|
+
end
|
101
|
+
|
102
|
+
# The result set should start from the position of the provided document.
|
103
|
+
# The value may be provided as an object that responds to the +#id+ call
|
104
|
+
# or a string.
|
105
|
+
def startkey_doc(value)
|
106
|
+
update_query(:startkey_docid => value.is_a?(String) ? value : value.id
|
107
|
+
end
|
108
|
+
|
109
|
+
# The opposite of +#startkey+, finds all index entries whose key is before the value specified.
|
110
|
+
#
|
111
|
+
# See the +#startkey+ method for more details and the +#inclusive_end+ option.
|
112
|
+
def endkey(value)
|
113
|
+
raise "View#endkey cannot be used when key has been set" unless query[:key].nil?
|
114
|
+
update_query(:endkey => value)
|
115
|
+
end
|
116
|
+
|
117
|
+
# The result set should end at the position of the provided document.
|
118
|
+
# The value may be provided as an object that responds to the +#id+ call
|
119
|
+
# or a string.
|
120
|
+
def endkey_doc(value)
|
121
|
+
update_query(:endkey_docid => value.is_a?(String) ? value : value.id
|
122
|
+
end
|
123
|
+
|
124
|
+
|
125
|
+
# The results should be provided in descending order.
|
126
|
+
#
|
127
|
+
# Descending is false by default, this method will enable it and cannot be undone.
|
128
|
+
def descending
|
129
|
+
update_query(:descending => true)
|
130
|
+
end
|
131
|
+
|
132
|
+
# Limit the result set to the value supplied.
|
133
|
+
def limit(value)
|
134
|
+
update_query(:limit => value)
|
135
|
+
end
|
136
|
+
|
137
|
+
# Skip the number of entries in the index specified by value. This would be
|
138
|
+
# the equivilent of an offset in SQL.
|
139
|
+
#
|
140
|
+
# The CouchDB documentation states that the skip option should not be used
|
141
|
+
# with large data sets as it is inefficient. Use the +startkey_doc+ method
|
142
|
+
# instead to skip ranges efficiently.
|
143
|
+
def skip(value = 0)
|
144
|
+
update_query(:skip => value)
|
145
|
+
end
|
146
|
+
|
147
|
+
# Use the reduce function on the view. If none is available this method will fail.
|
148
|
+
def reduce
|
149
|
+
update_query(:reduce => true)
|
150
|
+
end
|
151
|
+
|
152
|
+
# Control whether the reduce function reduces to a set of distinct keys or to a single
|
153
|
+
# result row.
|
154
|
+
#
|
155
|
+
# By default the value is false, and can only be set when the view's +#reduce+ option
|
156
|
+
# has been set.
|
157
|
+
def group
|
158
|
+
raise "View#reduce must have been set before grouping is permitted" unless query[:reduce]
|
159
|
+
update_query(:group => true)
|
160
|
+
end
|
161
|
+
|
162
|
+
def group_level(value)
|
163
|
+
raise "View#reduce and View#group must have been set before group_level is called" unless query[:reduce] && query[:group]
|
164
|
+
update_query(:group_level => value.to_i)
|
165
|
+
end
|
166
|
+
|
167
|
+
|
168
|
+
protected
|
169
|
+
|
170
|
+
def update_query(new_query = {})
|
171
|
+
self.class.new(self, new_query)
|
172
|
+
end
|
173
|
+
|
174
|
+
# Used internally to ensure that docs are provided. Should not be used outside of
|
175
|
+
# the view class under normal circumstances.
|
176
|
+
def include_docs
|
177
|
+
raise "Documents cannot be returned from a view that is prepared for a reduce" if query[:reduce]
|
178
|
+
update_query(:include_docs => true)
|
179
|
+
end
|
180
|
+
|
181
|
+
|
182
|
+
def execute(&block)
|
183
|
+
|
184
|
+
|
185
|
+
end
|
186
|
+
|
187
|
+
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
@@ -23,7 +23,7 @@ module CouchRest
|
|
23
23
|
# view_by :tags,
|
24
24
|
# :map =>
|
25
25
|
# "function(doc) {
|
26
|
-
# if (doc['
|
26
|
+
# if (doc['model'] == 'Post' && doc.tags) {
|
27
27
|
# doc.tags.forEach(function(tag){
|
28
28
|
# emit(doc.tag, 1);
|
29
29
|
# });
|
@@ -39,7 +39,7 @@ module CouchRest
|
|
39
39
|
# function:
|
40
40
|
#
|
41
41
|
# function(doc) {
|
42
|
-
# if (doc['
|
42
|
+
# if (doc['model'] == 'Post' && doc.date) {
|
43
43
|
# emit(doc.date, null);
|
44
44
|
# }
|
45
45
|
# }
|
@@ -77,7 +77,7 @@ module CouchRest
|
|
77
77
|
ducktype = opts.delete(:ducktype)
|
78
78
|
unless ducktype || opts[:map]
|
79
79
|
opts[:guards] ||= []
|
80
|
-
opts[:guards].push "(doc['
|
80
|
+
opts[:guards].push "(doc['#{model_type_key}'] == '#{self.to_s}')"
|
81
81
|
end
|
82
82
|
keys.push opts
|
83
83
|
design_doc.view_by(*keys)
|
data/lib/couchrest/model.rb
CHANGED
data/lib/couchrest/railtie.rb
CHANGED
@@ -4,8 +4,9 @@ require "active_model/railtie"
|
|
4
4
|
module CouchrestModel
|
5
5
|
# = Active Record Railtie
|
6
6
|
class Railtie < Rails::Railtie
|
7
|
-
config.generators.orm :
|
7
|
+
config.generators.orm :couchrest_model
|
8
|
+
config.generators.test_framework :test_unit, :fixture => false
|
8
9
|
end
|
9
10
|
|
10
11
|
end
|
11
|
-
|
12
|
+
|
data/lib/couchrest_model.rb
CHANGED
@@ -1,13 +1,3 @@
|
|
1
|
-
gem 'couchrest', ">= 1.0.0.beta"
|
2
|
-
require 'couchrest'
|
3
|
-
|
4
|
-
gem "tzinfo", ">= 0.3.22"
|
5
|
-
|
6
|
-
gem "activesupport", ">= 2.3.5"
|
7
|
-
require 'active_support/core_ext'
|
8
|
-
require 'active_support/json'
|
9
|
-
|
10
|
-
gem "activemodel", ">= 3.0.0.beta4"
|
11
1
|
require 'active_model'
|
12
2
|
require "active_model/callbacks"
|
13
3
|
require "active_model/conversion"
|
@@ -19,7 +9,9 @@ require "active_model/translation"
|
|
19
9
|
require "active_model/validator"
|
20
10
|
require "active_model/validations"
|
21
11
|
|
22
|
-
|
12
|
+
require 'active_support/core_ext'
|
13
|
+
require 'active_support/json'
|
14
|
+
|
23
15
|
require 'mime/types'
|
24
16
|
require "enumerator"
|
25
17
|
require "time"
|
@@ -28,11 +20,14 @@ require 'digest/md5'
|
|
28
20
|
require 'bigdecimal' # used in typecast
|
29
21
|
require 'bigdecimal/util' # used in typecast
|
30
22
|
|
23
|
+
require 'couchrest'
|
24
|
+
|
31
25
|
require 'couchrest/model'
|
32
26
|
require 'couchrest/model/errors'
|
33
27
|
require "couchrest/model/persistence"
|
34
28
|
require "couchrest/model/typecast"
|
35
29
|
require "couchrest/model/property"
|
30
|
+
require "couchrest/model/property_protection"
|
36
31
|
require "couchrest/model/casted_array"
|
37
32
|
require "couchrest/model/properties"
|
38
33
|
require "couchrest/model/validations"
|
@@ -43,9 +38,8 @@ require "couchrest/model/design_doc"
|
|
43
38
|
require "couchrest/model/extended_attachments"
|
44
39
|
require "couchrest/model/class_proxy"
|
45
40
|
require "couchrest/model/collection"
|
46
|
-
require "couchrest/model/attribute_protection"
|
47
|
-
require "couchrest/model/attributes"
|
48
41
|
require "couchrest/model/associations"
|
42
|
+
require "couchrest/model/configuration"
|
49
43
|
|
50
44
|
# Monkey patches applied to couchrest
|
51
45
|
require "couchrest/model/support/couchrest"
|
@@ -58,4 +52,3 @@ require "couchrest/model/base"
|
|
58
52
|
# Add rails support *after* everything has loaded
|
59
53
|
|
60
54
|
require "couchrest/railtie"
|
61
|
-
|
@@ -3,7 +3,6 @@ require 'rails/generators/couchrest_model'
|
|
3
3
|
module CouchrestModel
|
4
4
|
module Generators
|
5
5
|
class ModelGenerator < Base
|
6
|
-
|
7
6
|
check_class_collision
|
8
7
|
|
9
8
|
def create_model_file
|
@@ -14,6 +13,8 @@ module CouchrestModel
|
|
14
13
|
return if class_path.empty?
|
15
14
|
template 'module.rb', File.join('app/models', "#{class_path.join('/')}.rb") if behavior == :invoke
|
16
15
|
end
|
16
|
+
|
17
|
+
hook_for :test_framework
|
17
18
|
|
18
19
|
protected
|
19
20
|
|
data/spec/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
tmp
|
data/spec/couchrest/base_spec.rb
CHANGED
@@ -36,7 +36,7 @@ describe "Model Base" do
|
|
36
36
|
|
37
37
|
it "should not failed on a nil value in argument" do
|
38
38
|
@obj = Basic.new(nil)
|
39
|
-
@obj.
|
39
|
+
@obj.should_not be_nil
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
@@ -210,7 +210,7 @@ describe "Model Base" do
|
|
210
210
|
|
211
211
|
describe "a doc with template values (CR::Model spec)" do
|
212
212
|
before(:all) do
|
213
|
-
WithTemplateAndUniqueID.all.map{|o| o.destroy
|
213
|
+
WithTemplateAndUniqueID.all.map{|o| o.destroy}
|
214
214
|
WithTemplateAndUniqueID.database.bulk_delete
|
215
215
|
@tmpl = WithTemplateAndUniqueID.new
|
216
216
|
@tmpl2 = WithTemplateAndUniqueID.new(:preset => 'not_value', 'important-field' => '1')
|
@@ -233,7 +233,7 @@ describe "Model Base" do
|
|
233
233
|
describe "finding all instances of a model" do
|
234
234
|
before(:all) do
|
235
235
|
WithTemplateAndUniqueID.req_design_doc_refresh
|
236
|
-
WithTemplateAndUniqueID.all.map{|o| o.destroy
|
236
|
+
WithTemplateAndUniqueID.all.map{|o| o.destroy}
|
237
237
|
WithTemplateAndUniqueID.database.bulk_delete
|
238
238
|
WithTemplateAndUniqueID.new('important-field' => '1').save
|
239
239
|
WithTemplateAndUniqueID.new('important-field' => '2').save
|