openlogic-couchrest_model 1.0.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.
- data/.gitignore +11 -0
- data/.rspec +4 -0
- data/Gemfile +4 -0
- data/LICENSE +176 -0
- data/README.md +137 -0
- data/Rakefile +38 -0
- data/THANKS.md +21 -0
- data/VERSION +1 -0
- data/benchmarks/dirty.rb +118 -0
- data/couchrest_model.gemspec +36 -0
- data/history.md +309 -0
- data/init.rb +1 -0
- data/lib/couchrest/model.rb +10 -0
- data/lib/couchrest/model/associations.rb +231 -0
- data/lib/couchrest/model/base.rb +129 -0
- data/lib/couchrest/model/callbacks.rb +28 -0
- data/lib/couchrest/model/casted_array.rb +83 -0
- data/lib/couchrest/model/casted_by.rb +33 -0
- data/lib/couchrest/model/casted_hash.rb +84 -0
- data/lib/couchrest/model/class_proxy.rb +135 -0
- data/lib/couchrest/model/collection.rb +273 -0
- data/lib/couchrest/model/configuration.rb +67 -0
- data/lib/couchrest/model/connection.rb +70 -0
- data/lib/couchrest/model/core_extensions/hash.rb +9 -0
- data/lib/couchrest/model/core_extensions/time_parsing.rb +66 -0
- data/lib/couchrest/model/design_doc.rb +128 -0
- data/lib/couchrest/model/designs.rb +91 -0
- data/lib/couchrest/model/designs/view.rb +513 -0
- data/lib/couchrest/model/dirty.rb +39 -0
- data/lib/couchrest/model/document_queries.rb +99 -0
- data/lib/couchrest/model/embeddable.rb +78 -0
- data/lib/couchrest/model/errors.rb +25 -0
- data/lib/couchrest/model/extended_attachments.rb +83 -0
- data/lib/couchrest/model/persistence.rb +178 -0
- data/lib/couchrest/model/properties.rb +228 -0
- data/lib/couchrest/model/property.rb +114 -0
- data/lib/couchrest/model/property_protection.rb +71 -0
- data/lib/couchrest/model/proxyable.rb +183 -0
- data/lib/couchrest/model/support/couchrest_database.rb +13 -0
- data/lib/couchrest/model/support/couchrest_design.rb +33 -0
- data/lib/couchrest/model/typecast.rb +154 -0
- data/lib/couchrest/model/validations.rb +80 -0
- data/lib/couchrest/model/validations/casted_model.rb +16 -0
- data/lib/couchrest/model/validations/locale/en.yml +5 -0
- data/lib/couchrest/model/validations/uniqueness.rb +69 -0
- data/lib/couchrest/model/views.rb +151 -0
- data/lib/couchrest/railtie.rb +24 -0
- data/lib/couchrest_model.rb +66 -0
- data/lib/rails/generators/couchrest_model.rb +16 -0
- data/lib/rails/generators/couchrest_model/config/config_generator.rb +18 -0
- data/lib/rails/generators/couchrest_model/config/templates/couchdb.yml +21 -0
- data/lib/rails/generators/couchrest_model/model/model_generator.rb +27 -0
- data/lib/rails/generators/couchrest_model/model/templates/model.rb +2 -0
- data/spec/.gitignore +1 -0
- data/spec/fixtures/attachments/README +3 -0
- data/spec/fixtures/attachments/couchdb.png +0 -0
- data/spec/fixtures/attachments/test.html +11 -0
- data/spec/fixtures/config/couchdb.yml +10 -0
- data/spec/fixtures/models/article.rb +36 -0
- data/spec/fixtures/models/base.rb +164 -0
- data/spec/fixtures/models/card.rb +19 -0
- data/spec/fixtures/models/cat.rb +23 -0
- data/spec/fixtures/models/client.rb +6 -0
- data/spec/fixtures/models/course.rb +27 -0
- data/spec/fixtures/models/event.rb +8 -0
- data/spec/fixtures/models/invoice.rb +14 -0
- data/spec/fixtures/models/key_chain.rb +5 -0
- data/spec/fixtures/models/membership.rb +4 -0
- data/spec/fixtures/models/person.rb +11 -0
- data/spec/fixtures/models/project.rb +6 -0
- data/spec/fixtures/models/question.rb +7 -0
- data/spec/fixtures/models/sale_entry.rb +9 -0
- data/spec/fixtures/models/sale_invoice.rb +14 -0
- data/spec/fixtures/models/service.rb +10 -0
- data/spec/fixtures/models/user.rb +22 -0
- data/spec/fixtures/views/lib.js +3 -0
- data/spec/fixtures/views/test_view/lib.js +3 -0
- data/spec/fixtures/views/test_view/only-map.js +4 -0
- data/spec/fixtures/views/test_view/test-map.js +3 -0
- data/spec/fixtures/views/test_view/test-reduce.js +3 -0
- data/spec/functional/validations_spec.rb +8 -0
- data/spec/spec_helper.rb +60 -0
- data/spec/unit/active_model_lint_spec.rb +30 -0
- data/spec/unit/assocations_spec.rb +242 -0
- data/spec/unit/attachment_spec.rb +176 -0
- data/spec/unit/base_spec.rb +537 -0
- data/spec/unit/casted_spec.rb +72 -0
- data/spec/unit/class_proxy_spec.rb +167 -0
- data/spec/unit/collection_spec.rb +86 -0
- data/spec/unit/configuration_spec.rb +77 -0
- data/spec/unit/connection_spec.rb +148 -0
- data/spec/unit/core_extensions/time_parsing.rb +77 -0
- data/spec/unit/design_doc_spec.rb +241 -0
- data/spec/unit/designs/view_spec.rb +831 -0
- data/spec/unit/designs_spec.rb +134 -0
- data/spec/unit/dirty_spec.rb +436 -0
- data/spec/unit/embeddable_spec.rb +498 -0
- data/spec/unit/inherited_spec.rb +33 -0
- data/spec/unit/persistence_spec.rb +481 -0
- data/spec/unit/property_protection_spec.rb +192 -0
- data/spec/unit/property_spec.rb +481 -0
- data/spec/unit/proxyable_spec.rb +376 -0
- data/spec/unit/subclass_spec.rb +85 -0
- data/spec/unit/typecast_spec.rb +521 -0
- data/spec/unit/validations_spec.rb +140 -0
- data/spec/unit/view_spec.rb +367 -0
- metadata +301 -0
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
module CouchRest
|
|
2
|
+
module Model
|
|
3
|
+
# Warning! The Collection module is seriously depricated.
|
|
4
|
+
# Use the new Design Views instead, as this code copies many other parts
|
|
5
|
+
# of CouchRest Model.
|
|
6
|
+
#
|
|
7
|
+
# Expect this to be removed soon.
|
|
8
|
+
#
|
|
9
|
+
module Collection
|
|
10
|
+
|
|
11
|
+
def self.included(base)
|
|
12
|
+
base.extend(ClassMethods)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
module ClassMethods
|
|
16
|
+
|
|
17
|
+
# Creates a new class method, find_all_<collection_name>, that will
|
|
18
|
+
# execute the view specified with the design_doc and view_name
|
|
19
|
+
# parameters, along with the specified view_options. This method will
|
|
20
|
+
# return the results of the view as an Array of objects which are
|
|
21
|
+
# instances of the class.
|
|
22
|
+
#
|
|
23
|
+
# This method is handy for objects that do not use the view_by method
|
|
24
|
+
# to declare their views.
|
|
25
|
+
def provides_collection(collection_name, design_doc, view_name, view_options)
|
|
26
|
+
class_eval <<-END, __FILE__, __LINE__ + 1
|
|
27
|
+
def self.find_all_#{collection_name}(options = {})
|
|
28
|
+
view_options = #{view_options.inspect} || {}
|
|
29
|
+
CollectionProxy.new(options[:database] || database, "#{design_doc}", "#{view_name}", view_options.merge(options), Kernel.const_get('#{self}'))
|
|
30
|
+
end
|
|
31
|
+
END
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Fetch a group of objects from CouchDB. Options can include:
|
|
35
|
+
# :page - Specifies the page to load (starting at 1)
|
|
36
|
+
# :per_page - Specifies the number of objects to load per page
|
|
37
|
+
#
|
|
38
|
+
# Defaults are used if these options are not specified.
|
|
39
|
+
def paginate(options)
|
|
40
|
+
proxy = create_collection_proxy(options)
|
|
41
|
+
proxy.paginate(options)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Iterate over the objects in a collection, fetching them from CouchDB
|
|
45
|
+
# in groups. Options can include:
|
|
46
|
+
# :page - Specifies the page to load
|
|
47
|
+
# :per_page - Specifies the number of objects to load per page
|
|
48
|
+
#
|
|
49
|
+
# Defaults are used if these options are not specified.
|
|
50
|
+
def paginated_each(options, &block)
|
|
51
|
+
search = options.delete(:search)
|
|
52
|
+
unless search == true
|
|
53
|
+
proxy = create_collection_proxy(options)
|
|
54
|
+
else
|
|
55
|
+
proxy = create_search_collection_proxy(options)
|
|
56
|
+
end
|
|
57
|
+
proxy.paginated_each(options, &block)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Create a CollectionProxy for the specified view and options.
|
|
61
|
+
# CollectionProxy behaves just like an Array, but offers support for
|
|
62
|
+
# pagination.
|
|
63
|
+
def collection_proxy_for(design_doc, view_name, view_options = {})
|
|
64
|
+
options = view_options.merge(:design_doc => design_doc, :view_name => view_name)
|
|
65
|
+
create_collection_proxy(options)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
private
|
|
69
|
+
|
|
70
|
+
def create_collection_proxy(options)
|
|
71
|
+
design_doc, view_name, view_options = parse_view_options(options)
|
|
72
|
+
CollectionProxy.new(options[:database] || database, design_doc, view_name, view_options, self)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def create_search_collection_proxy(options)
|
|
76
|
+
design_doc, search_name, search_options = parse_search_options(options)
|
|
77
|
+
CollectionProxy.new(options[:database] || database, design_doc, search_name, search_options, self, :search)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def parse_view_options(options)
|
|
81
|
+
design_doc = options.delete(:design_doc)
|
|
82
|
+
raise ArgumentError, 'design_doc is required' if design_doc.nil?
|
|
83
|
+
|
|
84
|
+
view_name = options.delete(:view_name)
|
|
85
|
+
raise ArgumentError, 'view_name is required' if view_name.nil?
|
|
86
|
+
|
|
87
|
+
default_view_options = (design_doc.class == Design &&
|
|
88
|
+
design_doc['views'][view_name.to_s] &&
|
|
89
|
+
design_doc['views'][view_name.to_s]["couchrest-defaults"]) || {}
|
|
90
|
+
view_options = default_view_options.merge(options)
|
|
91
|
+
view_options.delete(:database)
|
|
92
|
+
|
|
93
|
+
[design_doc, view_name, view_options]
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def parse_search_options(options)
|
|
97
|
+
design_doc = options.delete(:design_doc)
|
|
98
|
+
raise ArgumentError, 'design_doc is required' if design_doc.nil?
|
|
99
|
+
|
|
100
|
+
search_name = options.delete(:view_name)
|
|
101
|
+
raise ArgumentError, 'search_name is required' if search_name.nil?
|
|
102
|
+
|
|
103
|
+
search_options = options.clone
|
|
104
|
+
search_options.delete(:database)
|
|
105
|
+
|
|
106
|
+
[design_doc, search_name, search_options]
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
class CollectionProxy
|
|
112
|
+
alias_method :proxy_respond_to?, :respond_to?
|
|
113
|
+
instance_methods.each { |m| undef_method m unless m =~ /(^__|^nil\?$|^send$|proxy_|^object_id$)/ }
|
|
114
|
+
|
|
115
|
+
DEFAULT_PAGE = 1
|
|
116
|
+
DEFAULT_PER_PAGE = 30
|
|
117
|
+
|
|
118
|
+
# Create a new CollectionProxy to represent the specified view. If a
|
|
119
|
+
# container class is specified, the proxy will create an object of the
|
|
120
|
+
# given type for each row that comes back from the view. If no
|
|
121
|
+
# container class is specified, the raw results are returned.
|
|
122
|
+
#
|
|
123
|
+
# The CollectionProxy provides support for paginating over a collection
|
|
124
|
+
# via the paginate, and paginated_each methods.
|
|
125
|
+
def initialize(database, design_doc, view_name, view_options = {}, container_class = nil, query_type = :view)
|
|
126
|
+
raise ArgumentError, "database is a required parameter" if database.nil?
|
|
127
|
+
|
|
128
|
+
@database = database
|
|
129
|
+
@container_class = container_class
|
|
130
|
+
@query_type = query_type
|
|
131
|
+
|
|
132
|
+
strip_pagination_options(view_options)
|
|
133
|
+
@view_options = view_options
|
|
134
|
+
|
|
135
|
+
if design_doc.class == Design
|
|
136
|
+
@view_name = "#{design_doc.name}/#{view_name}"
|
|
137
|
+
else
|
|
138
|
+
@view_name = "#{design_doc}/#{view_name}"
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# Save the design doc, ready for use
|
|
142
|
+
@container_class.save_design_doc(@database)
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# See Collection.paginate
|
|
146
|
+
def paginate(options = {})
|
|
147
|
+
page, per_page = parse_options(options)
|
|
148
|
+
results = @database.send(@query_type, @view_name, pagination_options(page, per_page))
|
|
149
|
+
remember_where_we_left_off(results, page)
|
|
150
|
+
instances = convert_to_container_array(results)
|
|
151
|
+
|
|
152
|
+
begin
|
|
153
|
+
if Kernel.const_get('WillPaginate')
|
|
154
|
+
total_rows = results['total_rows'].to_i
|
|
155
|
+
paginated = WillPaginate::Collection.create(page, per_page, total_rows) do |pager|
|
|
156
|
+
pager.replace(instances)
|
|
157
|
+
end
|
|
158
|
+
return paginated
|
|
159
|
+
end
|
|
160
|
+
rescue NameError
|
|
161
|
+
# When not using will_paginate, not much we could do about this. :x
|
|
162
|
+
end
|
|
163
|
+
return instances
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# See Collection.paginated_each
|
|
167
|
+
def paginated_each(options = {}, &block)
|
|
168
|
+
page, per_page = parse_options(options)
|
|
169
|
+
|
|
170
|
+
begin
|
|
171
|
+
collection = paginate({:page => page, :per_page => per_page})
|
|
172
|
+
collection.each(&block)
|
|
173
|
+
page += 1
|
|
174
|
+
end until collection.size < per_page
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def respond_to?(*args)
|
|
178
|
+
proxy_respond_to?(*args) || (load_target && @target.respond_to?(*args))
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
# Explicitly proxy === because the instance method removal above
|
|
182
|
+
# doesn't catch it.
|
|
183
|
+
def ===(other)
|
|
184
|
+
load_target
|
|
185
|
+
other === @target
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
private
|
|
189
|
+
|
|
190
|
+
def method_missing(method, *args)
|
|
191
|
+
if load_target
|
|
192
|
+
if block_given?
|
|
193
|
+
@target.send(method, *args) { |*block_args| yield(*block_args) }
|
|
194
|
+
else
|
|
195
|
+
@target.send(method, *args)
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
def load_target
|
|
201
|
+
unless loaded?
|
|
202
|
+
@view_options.merge!({:include_docs => true}) if @query_type == :search
|
|
203
|
+
results = @database.send(@query_type, @view_name, @view_options)
|
|
204
|
+
@target = convert_to_container_array(results)
|
|
205
|
+
end
|
|
206
|
+
@loaded = true
|
|
207
|
+
@target
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
def loaded?
|
|
211
|
+
@loaded
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
def reload
|
|
215
|
+
reset
|
|
216
|
+
load_target
|
|
217
|
+
self unless @target.nil?
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
def reset
|
|
221
|
+
@loaded = false
|
|
222
|
+
@target = nil
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
def inspect
|
|
226
|
+
load_target
|
|
227
|
+
@target.inspect
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
def convert_to_container_array(results)
|
|
231
|
+
if @container_class.nil?
|
|
232
|
+
results
|
|
233
|
+
else
|
|
234
|
+
results['rows'].collect { |row| @container_class.build_from_database(row['doc']) } unless results['rows'].nil?
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
def pagination_options(page, per_page)
|
|
239
|
+
view_options = @view_options.clone
|
|
240
|
+
if @query_type == :view && @last_key && @last_docid && @last_page == page - 1
|
|
241
|
+
key = view_options.delete(:key)
|
|
242
|
+
end_key = view_options[:endkey] || key
|
|
243
|
+
options = { :startkey => @last_key, :endkey => end_key, :startkey_docid => @last_docid, :limit => per_page, :skip => 1 }
|
|
244
|
+
else
|
|
245
|
+
options = { :limit => per_page, :skip => per_page * (page - 1) }
|
|
246
|
+
end
|
|
247
|
+
options[:include_docs] = true
|
|
248
|
+
view_options.merge(options)
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
def parse_options(options)
|
|
252
|
+
page = options.delete(:page) || DEFAULT_PAGE
|
|
253
|
+
per_page = options.delete(:per_page) || DEFAULT_PER_PAGE
|
|
254
|
+
[page.to_i, per_page.to_i]
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
def strip_pagination_options(options)
|
|
258
|
+
parse_options(options)
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
def remember_where_we_left_off(results, page)
|
|
262
|
+
last_row = results['rows'].last
|
|
263
|
+
if last_row
|
|
264
|
+
@last_key = last_row['key']
|
|
265
|
+
@last_docid = last_row['id']
|
|
266
|
+
end
|
|
267
|
+
@last_page = page
|
|
268
|
+
end
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
end
|
|
272
|
+
end
|
|
273
|
+
end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
module CouchRest
|
|
2
|
+
|
|
3
|
+
# CouchRest Model Configuration support, stolen from Carrierwave by jnicklas
|
|
4
|
+
# http://github.com/jnicklas/carrierwave/blob/master/lib/carrierwave/uploader/configuration.rb
|
|
5
|
+
|
|
6
|
+
module Model
|
|
7
|
+
module Configuration
|
|
8
|
+
extend ActiveSupport::Concern
|
|
9
|
+
|
|
10
|
+
included do
|
|
11
|
+
add_config :model_type_key
|
|
12
|
+
add_config :mass_assign_any_attribute
|
|
13
|
+
add_config :auto_update_design_doc
|
|
14
|
+
add_config :environment
|
|
15
|
+
add_config :connection
|
|
16
|
+
add_config :connection_config_file
|
|
17
|
+
|
|
18
|
+
configure do |config|
|
|
19
|
+
config.model_type_key = 'type' # was 'couchrest-type'
|
|
20
|
+
config.mass_assign_any_attribute = false
|
|
21
|
+
config.auto_update_design_doc = true
|
|
22
|
+
|
|
23
|
+
config.environment = :development
|
|
24
|
+
config.connection_config_file = File.join(Dir.pwd, 'config', 'couchdb.yml')
|
|
25
|
+
config.connection = {
|
|
26
|
+
:protocol => 'http',
|
|
27
|
+
:host => 'localhost',
|
|
28
|
+
:port => '5984',
|
|
29
|
+
:prefix => 'couchrest',
|
|
30
|
+
:suffix => nil,
|
|
31
|
+
:join => '_',
|
|
32
|
+
:username => nil,
|
|
33
|
+
:password => nil
|
|
34
|
+
}
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
module ClassMethods
|
|
39
|
+
|
|
40
|
+
def add_config(name)
|
|
41
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
|
42
|
+
def self.#{name}(value=nil)
|
|
43
|
+
@#{name} = value if value
|
|
44
|
+
return @#{name} if self.object_id == #{self.object_id} || defined?(@#{name})
|
|
45
|
+
name = superclass.#{name}
|
|
46
|
+
return nil if name.nil? && !instance_variable_defined?("@#{name}")
|
|
47
|
+
@#{name} = name && !name.is_a?(Module) && !name.is_a?(Symbol) && !name.is_a?(Numeric) && !name.is_a?(TrueClass) && !name.is_a?(FalseClass) ? name.dup : name
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def self.#{name}=(value)
|
|
51
|
+
@#{name} = value
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def #{name}
|
|
55
|
+
self.class.#{name}
|
|
56
|
+
end
|
|
57
|
+
RUBY
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def configure
|
|
61
|
+
yield self
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
module CouchRest
|
|
2
|
+
module Model
|
|
3
|
+
module Connection
|
|
4
|
+
extend ActiveSupport::Concern
|
|
5
|
+
|
|
6
|
+
def server
|
|
7
|
+
self.class.server
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
module ClassMethods
|
|
11
|
+
|
|
12
|
+
# Overwrite the normal use_database method so that a database
|
|
13
|
+
# name can be provided instead of a full connection.
|
|
14
|
+
def use_database(db)
|
|
15
|
+
@database = prepare_database(db)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Overwrite the default database method so that it always
|
|
19
|
+
# provides something from the configuration
|
|
20
|
+
def database
|
|
21
|
+
super || (@database ||= prepare_database)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def server
|
|
25
|
+
@server ||= CouchRest::Server.new(prepare_server_uri)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def prepare_database(db = nil)
|
|
29
|
+
unless db.is_a?(CouchRest::Database)
|
|
30
|
+
conf = connection_configuration
|
|
31
|
+
db = [conf[:prefix], db.to_s, conf[:suffix]].reject{|s| s.to_s.empty?}.join(conf[:join])
|
|
32
|
+
self.server.database!(db)
|
|
33
|
+
else
|
|
34
|
+
db
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
protected
|
|
39
|
+
|
|
40
|
+
def prepare_server_uri
|
|
41
|
+
conf = connection_configuration
|
|
42
|
+
userinfo = [conf[:username], conf[:password]].compact.join(':')
|
|
43
|
+
userinfo += '@' unless userinfo.empty?
|
|
44
|
+
"#{conf[:protocol]}://#{userinfo}#{conf[:host]}:#{conf[:port]}"
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def connection_configuration
|
|
48
|
+
@connection_configuration ||=
|
|
49
|
+
self.connection.update(
|
|
50
|
+
(load_connection_config_file[environment.to_sym] || {}).symbolize_keys
|
|
51
|
+
)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def load_connection_config_file
|
|
55
|
+
file = connection_config_file
|
|
56
|
+
connection_config_cache[file] ||=
|
|
57
|
+
(File.exists?(file) ?
|
|
58
|
+
YAML::load(ERB.new(IO.read(file)).result) :
|
|
59
|
+
{ }).symbolize_keys
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def connection_config_cache
|
|
63
|
+
Thread.current[:connection_config_cache] ||= {}
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# This file contains various hacks for Rails compatibility.
|
|
2
|
+
class Hash
|
|
3
|
+
# Hack so that CouchRest::Document, which descends from Hash,
|
|
4
|
+
# doesn't appear to Rails routing as a Hash of options
|
|
5
|
+
def self.===(other)
|
|
6
|
+
return false if self == Hash && other.is_a?(CouchRest::Document)
|
|
7
|
+
super
|
|
8
|
+
end
|
|
9
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
module CouchRest
|
|
2
|
+
module Model
|
|
3
|
+
module CoreExtensions
|
|
4
|
+
module TimeParsing
|
|
5
|
+
|
|
6
|
+
if RUBY_VERSION < "1.9.0"
|
|
7
|
+
# Overrwrite Ruby's standard new method to provide compatible support
|
|
8
|
+
# of 1.9.2's Time.new method.
|
|
9
|
+
#
|
|
10
|
+
# Only supports syntax like:
|
|
11
|
+
#
|
|
12
|
+
# Time.new(2011, 4, 1, 18, 50, 32, "+02:00")
|
|
13
|
+
# # or
|
|
14
|
+
# Time.new(2011, 4, 1, 18, 50, 32)
|
|
15
|
+
#
|
|
16
|
+
def new(*args)
|
|
17
|
+
return super() if (args.empty?)
|
|
18
|
+
zone = args.delete_at(6)
|
|
19
|
+
time = mktime(*args)
|
|
20
|
+
if zone =~ /([\+|\-]?)(\d{2}):?(\d{2})/
|
|
21
|
+
tz_difference = ("#{$1 == '-' ? '+' : '-'}#{$2}".to_i * 3600) + ($3.to_i * 60)
|
|
22
|
+
time + tz_difference + zone_offset(time.zone)
|
|
23
|
+
else
|
|
24
|
+
time
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Attemtps to parse a time string in ISO8601 format.
|
|
30
|
+
# If no match is found, the standard time parse will be used.
|
|
31
|
+
#
|
|
32
|
+
# Times, unless provided with a time zone, are assumed to be in
|
|
33
|
+
# UTC.
|
|
34
|
+
#
|
|
35
|
+
def parse_iso8601(string)
|
|
36
|
+
if (string =~ /(\d{4})[\-|\/](\d{2})[\-|\/](\d{2})[T|\s](\d{2}):(\d{2}):(\d{2})(Z| ?([\+|\s|\-])?(\d{2}):?(\d{2}))?/)
|
|
37
|
+
# $1 = year
|
|
38
|
+
# $2 = month
|
|
39
|
+
# $3 = day
|
|
40
|
+
# $4 = hours
|
|
41
|
+
# $5 = minutes
|
|
42
|
+
# $6 = seconds
|
|
43
|
+
# $7 = UTC or Timezone
|
|
44
|
+
# $8 = time zone direction
|
|
45
|
+
# $9 = tz difference hours
|
|
46
|
+
# $10 = tz difference minutes
|
|
47
|
+
|
|
48
|
+
if (!$7.to_s.empty? && $7 != 'Z')
|
|
49
|
+
new($1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i, "#{$8 == '-' ? '-' : '+'}#{$9}:#{$10}")
|
|
50
|
+
else
|
|
51
|
+
utc($1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i)
|
|
52
|
+
end
|
|
53
|
+
else
|
|
54
|
+
parse(string)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
Time.class_eval do
|
|
64
|
+
extend CouchRest::Model::CoreExtensions::TimeParsing
|
|
65
|
+
end
|
|
66
|
+
|