dolly 1.1.7 → 3.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.
Files changed (78) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +35 -0
  3. data/lib/dolly.rb +1 -23
  4. data/lib/dolly/attachment.rb +29 -0
  5. data/lib/dolly/bulk_document.rb +27 -26
  6. data/lib/dolly/class_methods_delegation.rb +15 -0
  7. data/lib/dolly/collection.rb +26 -69
  8. data/lib/dolly/configuration.rb +35 -10
  9. data/lib/dolly/connection.rb +91 -22
  10. data/lib/dolly/depracated_database.rb +24 -0
  11. data/lib/dolly/document.rb +32 -206
  12. data/lib/dolly/document_creation.rb +20 -0
  13. data/lib/dolly/document_state.rb +65 -0
  14. data/lib/dolly/document_type.rb +28 -0
  15. data/lib/dolly/exceptions.rb +21 -0
  16. data/lib/dolly/identity_properties.rb +29 -0
  17. data/lib/dolly/properties.rb +31 -0
  18. data/lib/dolly/property.rb +58 -47
  19. data/lib/dolly/property_manager.rb +47 -0
  20. data/lib/dolly/property_set.rb +18 -0
  21. data/lib/dolly/query.rb +39 -67
  22. data/lib/dolly/query_arguments.rb +35 -0
  23. data/lib/dolly/request.rb +12 -107
  24. data/lib/dolly/request_header.rb +26 -0
  25. data/lib/dolly/timestamp.rb +24 -0
  26. data/lib/dolly/version.rb +1 -1
  27. data/lib/{dolly → railties}/railtie.rb +2 -1
  28. data/lib/refinements/string_refinements.rb +28 -0
  29. data/lib/tasks/db.rake +4 -3
  30. data/test/bulk_document_test.rb +8 -5
  31. data/test/document_test.rb +137 -53
  32. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  33. data/test/dummy/log/test.log +46417 -46858
  34. data/test/test_helper.rb +14 -20
  35. metadata +42 -145
  36. data/Rakefile +0 -11
  37. data/lib/dolly/bulk_error.rb +0 -16
  38. data/lib/dolly/db_config.rb +0 -20
  39. data/lib/dolly/interpreter.rb +0 -5
  40. data/lib/dolly/logger.rb +0 -9
  41. data/lib/dolly/name_space.rb +0 -28
  42. data/lib/dolly/timestamps.rb +0 -21
  43. data/lib/exceptions/dolly.rb +0 -47
  44. data/test/collection_test.rb +0 -59
  45. data/test/configuration_test.rb +0 -9
  46. data/test/dummy/README.rdoc +0 -28
  47. data/test/dummy/Rakefile +0 -6
  48. data/test/dummy/app/assets/javascripts/application.js +0 -13
  49. data/test/dummy/app/assets/stylesheets/application.css +0 -13
  50. data/test/dummy/app/controllers/application_controller.rb +0 -5
  51. data/test/dummy/app/helpers/application_helper.rb +0 -2
  52. data/test/dummy/app/views/layouts/application.html.erb +0 -14
  53. data/test/dummy/bin/bundle +0 -3
  54. data/test/dummy/bin/rails +0 -4
  55. data/test/dummy/bin/rake +0 -4
  56. data/test/dummy/config.ru +0 -4
  57. data/test/dummy/config/application.rb +0 -27
  58. data/test/dummy/config/boot.rb +0 -5
  59. data/test/dummy/config/couchdb.yml +0 -13
  60. data/test/dummy/config/environment.rb +0 -5
  61. data/test/dummy/config/environments/development.rb +0 -29
  62. data/test/dummy/config/environments/production.rb +0 -80
  63. data/test/dummy/config/environments/test.rb +0 -36
  64. data/test/dummy/config/initializers/backtrace_silencers.rb +0 -7
  65. data/test/dummy/config/initializers/inflections.rb +0 -16
  66. data/test/dummy/config/initializers/mime_types.rb +0 -5
  67. data/test/dummy/config/initializers/secret_token.rb +0 -12
  68. data/test/dummy/config/initializers/session_store.rb +0 -3
  69. data/test/dummy/config/initializers/wrap_parameters.rb +0 -14
  70. data/test/dummy/config/locales/en.yml +0 -23
  71. data/test/dummy/config/routes.rb +0 -56
  72. data/test/dummy/lib/couch_rest_adapter/railtie.rb +0 -10
  73. data/test/dummy/public/404.html +0 -58
  74. data/test/dummy/public/422.html +0 -58
  75. data/test/dummy/public/500.html +0 -57
  76. data/test/dummy/public/favicon.ico +0 -0
  77. data/test/factories/factories.rb +0 -8
  78. data/test/request_test.rb +0 -25
@@ -0,0 +1,24 @@
1
+ module Dolly
2
+ module DepracatedDatabase
3
+ Database = Struct.new(:connection) do
4
+ def request *args
5
+ connection.request *args
6
+ end
7
+
8
+ def post *args
9
+ connection.post *args
10
+ end
11
+ end
12
+
13
+ def view *args
14
+ opts = args.pop if args.last.is_a? Hash
15
+ opts ||= {}
16
+ connection.view *args, opts.merge(include_docs: true)
17
+ end
18
+
19
+ def database
20
+ warn "[DEPRECATION] `database` is deprecated. Please use `connection` instead."
21
+ Database.new(connection)
22
+ end
23
+ end
24
+ end
@@ -1,217 +1,43 @@
1
- require "dolly/query"
2
- require "dolly/property"
3
- require 'dolly/timestamps'
1
+ require 'dolly/query'
2
+ require 'dolly/connection'
3
+ require 'dolly/request'
4
+ require 'dolly/depracated_database'
5
+ require 'dolly/document_state'
6
+ require 'dolly/properties'
7
+ require 'dolly/identity_properties'
8
+ require 'dolly/attachment'
9
+ require 'dolly/property_manager'
10
+ require 'dolly/timestamp'
11
+ require 'dolly/query_arguments'
12
+ require 'dolly/document_creation'
13
+ require 'dolly/class_methods_delegation'
14
+ require 'refinements/string_refinements'
4
15
 
5
16
  module Dolly
6
17
  class Document
7
- extend Dolly::Connection
8
- include Dolly::Query
9
- extend Dolly::Timestamps
18
+ extend Query
19
+ extend Request
20
+ extend DepracatedDatabase
21
+ extend Properties
22
+ extend DocumentCreation
23
+ include PropertyManager
24
+ include Timestamp
25
+ include DocumentState
26
+ include IdentityProperties
27
+ include Attachment
28
+ include QueryArguments
29
+ include ClassMethodsDelegation
10
30
 
11
- attr_accessor :rows, :doc, :key
12
- class_attribute :properties
13
- cattr_accessor :timestamps do
14
- {}
15
- end
16
-
17
- def initialize options = {}
18
- @doc ||= {}
19
- options = options.with_indifferent_access
20
- init_properties options
21
- end
22
-
23
- def persisted?
24
- !doc['_rev'].blank?
25
- end
26
-
27
- def update_properties options = {}
28
- raise InvalidProperty unless valid_properties?(options)
29
- options.each do |property, value|
30
- send(:"#{property}=", value)
31
- end
32
- end
33
-
34
- def update_properties! options = {}
35
- update_properties(options)
36
- save
37
- end
38
-
39
- def reload
40
- self.doc = self.class.find(id).doc
41
- end
42
-
43
- def id
44
- doc['_id'] ||= self.class.next_id
45
- end
46
-
47
- def id= base_value
48
- doc ||= {}
49
- doc['_id'] = self.class.namespace(base_value)
50
- end
51
-
52
- def rev
53
- doc['_rev']
54
- end
55
-
56
- def rev= value
57
- doc['_rev'] = value
58
- end
59
-
60
- def save options = {}
61
- return false unless options[:validate] == false || valid?
62
- self.doc['_id'] = self.id if self.id.present?
63
- self.doc['_id'] = self.class.next_id if self.doc['_id'].blank?
64
- set_created_at if timestamps[self.class.name]
65
- set_updated_at if timestamps[self.class.name]
66
- response = database.put(id_as_resource, self.doc.to_json)
67
- obj = JSON::parse response.parsed_response
68
- doc['_rev'] = obj['rev'] if obj['rev']
69
- obj['ok']
70
- end
71
-
72
- def save!
73
- raise DocumentInvalidError unless valid?
74
- save
75
- end
76
-
77
- def destroy hard = true
78
- if hard
79
- q = id_as_resource + "?rev=#{rev}"
80
- response = database.delete(q)
81
- JSON::parse response.parsed_response
82
- else
83
- self.doc['_deleted'] = true
84
- self.save
85
- end
86
- end
87
-
88
- def rows= col
89
- raise Dolly::ResourceNotFound if col.empty?
90
- col.each{ |r| @doc = r['doc'] }
91
- _properties.each do |p|
92
- #TODO: Refactor properties so it is not required
93
- #to be a class property. But something that doesn't
94
- #persist all the inheretence chain
95
- next unless self.respond_to? :"#{p.name}="
96
- self.send "#{p.name}=", doc[p.name]
97
- end
98
- @rows = col
99
- end
100
-
101
- def from_json string
102
- parsed = JSON::parse( string )
103
- self.rows = parsed['rows']
104
- self
105
- end
106
-
107
- def database
108
- self.class.database
109
- end
110
-
111
- def id_as_resource
112
- CGI::escape id
113
- end
114
-
115
- def attach_file! file_name, mime_type, body, opts={}
116
- attach_file file_name, mime_type, body, opts
117
- save
118
- end
119
-
120
- def attach_file file_name, mime_type, body, opts={}
121
- if opts[:inline]
122
- attach_inline_file file_name, mime_type, body
123
- else
124
- attach_standalone_file file_name, mime_type, body
125
- end
126
- end
127
-
128
- def attach_inline_file file_name, mime_type, body
129
- attachment_data = { file_name.to_s => { 'content_type' => mime_type,
130
- 'data' => Base64.encode64(body)} }
131
- doc['_attachments'] ||= {}
132
- doc['_attachments'].merge! attachment_data
133
- end
134
-
135
- def attach_standalone_file file_name, mime_type, body
136
- database.attach id_as_resource, CGI.escape(file_name), body, { 'Content-Type' => mime_type }
137
- end
138
-
139
- def self.create options = {}
140
- obj = new options
141
- obj.save
142
- obj
143
- end
144
-
145
- def self.property *ary
146
- self.properties ||= {}
147
- options = ary.pop if ary.last.kind_of? Hash
148
- options ||= {}
31
+ attr_writer :doc
149
32
 
150
- ary.each do |name|
151
- self.properties[name] = Property.new options.merge(name: name)
152
- self.write_methods name
153
- end
33
+ def initialize attributes = {}
34
+ properties.each(&build_property(attributes))
154
35
  end
155
36
 
156
- private
157
- #TODO: create a PropertiesSet service object, to do all this
158
- def self.write_methods name
159
- property = properties[name]
160
- define_method(name) { read_property name }
161
- define_method("#{name}=") { |value| write_property name, value }
162
- define_method(:"#{name}?") { send name } if property.boolean?
163
- define_method("[]") {|n| send n.to_sym }
164
- end
37
+ protected
165
38
 
166
- def write_property name, value
167
- instance_variable_set(:"@#{name}", value)
168
- @doc[name.to_s] = value
169
- end
170
-
171
- def read_property name
172
- if instance_variable_get(:"@#{name}").nil?
173
- write_property name, (doc[name.to_s] || self.properties[name].value)
174
- end
175
- instance_variable_get(:"@#{name}")
176
- end
177
-
178
- def _properties
179
- self.properties.values
180
- end
181
-
182
- def init_properties options = {}
183
- raise Dolly::ResourceNotFound if options['error'] == 'not_found'
184
- options.each do |k, v|
185
- next unless respond_to? :"#{k}="
186
- send(:"#{k}=", v)
187
- end
188
- initialize_default_properties options if self.properties.present?
189
- init_doc options
190
- end
191
-
192
- def initialize_default_properties options
193
- _properties.reject { |property| options.keys.include? property.name }.each do |property|
194
- property_value = property.default.clone unless Dolly::Property::CANT_CLONE.any? { |klass| property.default.is_a? klass }
195
- property_value ||= property.default
196
- self.doc[property.name] ||= property_value
197
- end
198
- end
199
-
200
- def init_doc options
201
- self.doc ||= {}
202
- #TODO: define what will be the preference _id or id
203
- normalized_id = options[:_id] || options[:id]
204
- self.doc['_id'] = self.class.namespace( normalized_id ) if normalized_id
205
- end
206
-
207
- def valid_properties?(options)
208
- options.keys.any?{ |option| properties_include?(option.to_s) }
209
- end
210
-
211
- def properties_include? property
212
- _properties.map(&:name).include? property
39
+ def doc
40
+ @doc ||= {}
213
41
  end
214
-
215
- def valid?; true; end
216
42
  end
217
43
  end
@@ -0,0 +1,20 @@
1
+ require 'dolly/properties'
2
+
3
+ module Dolly
4
+ module DocumentCreation
5
+ include Properties
6
+
7
+ def from_doc(doc)
8
+ attributes = property_clean_doc(doc)
9
+ new(attributes).tap { |model| model.doc = doc }
10
+ end
11
+
12
+ def from_json(json)
13
+ from_doc(Oj.load(json, symbol_keys: true))
14
+ end
15
+
16
+ def create(attributes)
17
+ new(attributes).tap { |model| model.save }
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,65 @@
1
+ require 'dolly/class_methods_delegation'
2
+
3
+ module Dolly
4
+ module DocumentState
5
+ include ClassMethodsDelegation
6
+
7
+ def save(options = {})
8
+ return false unless options[:validate] == false || valid?
9
+ write_timestamps(persisted?)
10
+ after_save(connection.put(id, doc))
11
+ end
12
+
13
+ def save!
14
+ raise DocumentInvalidError unless valid?
15
+ save
16
+ end
17
+
18
+ def update_properties(properties)
19
+ properties.each(&update_attribute)
20
+ end
21
+
22
+ def update_properties!(properties)
23
+ update_properties(properties)
24
+ save!
25
+ end
26
+
27
+ def destroy is_hard = true
28
+ return connection.delete(id, rev) if is_hard
29
+ doc[:_deleted] = true
30
+ save
31
+ rescue Dolly::ResourceNotFound
32
+ nil
33
+ rescue Dolly::ServerError => error
34
+ raise error unless error.message =~ /conflict/
35
+ self.rev = self.class.safe_find(id)&.rev
36
+ return unless self.rev
37
+ destroy(is_hard)
38
+ end
39
+
40
+ def reload
41
+ reloaded_doc = self.class.find(id).send(:doc)
42
+ attributes = property_clean_doc(reloaded_doc)
43
+
44
+ attributes.each(&update_attribute)
45
+ end
46
+
47
+ def persisted?
48
+ return false unless doc[:_rev]
49
+ !doc[:_rev].empty?
50
+ end
51
+
52
+ def to_h
53
+ doc
54
+ end
55
+
56
+ def valid?
57
+ true
58
+ end
59
+
60
+ def after_save(response)
61
+ self.rev = response[:rev]
62
+ response[:ok]
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,28 @@
1
+ require 'refinements/string_refinements'
2
+
3
+ module Dolly
4
+ module DocumentType
5
+ using StringRefinements
6
+
7
+ def namespace_keys(keys)
8
+ keys.map { |key| namespace_key key }
9
+ end
10
+
11
+ def namespace_key(key)
12
+ return key if key =~ %r{^#{name_paramitized}/}
13
+ "#{name_paramitized}/#{key}"
14
+ end
15
+
16
+ def base_id(id)
17
+ id.sub(%r{^#{name_paramitized}/}, '')
18
+ end
19
+
20
+ def name_paramitized
21
+ class_name.split("::").last.underscore
22
+ end
23
+
24
+ def class_name
25
+ is_a?(Class) ? name : self.class.name
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,21 @@
1
+ module Dolly
2
+ class ResourceNotFound < RuntimeError
3
+ def to_s
4
+ 'The document was not found.'
5
+ end
6
+ end
7
+
8
+ class ServerError < RuntimeError
9
+ def initialize msg
10
+ @msg = msg
11
+ end
12
+
13
+ def to_s
14
+ "There has been an error on the couchdb server: #{@msg.inspect}"
15
+ end
16
+ end
17
+
18
+ class InvalidConfigFileError < RuntimeError; end
19
+ class InvalidProperty < RuntimeError; end
20
+ class DocumentInvalidError < RuntimeError; end
21
+ end
@@ -0,0 +1,29 @@
1
+ require 'dolly/document_type'
2
+ require 'dolly/class_methods_delegation'
3
+
4
+ module Dolly
5
+ module IdentityProperties
6
+ include DocumentType
7
+ include ClassMethodsDelegation
8
+
9
+ def id
10
+ doc[:_id] ||= namespace_key(connection.uuids.last)
11
+ end
12
+
13
+ def id= value
14
+ doc[:_id] = namespace_key(value)
15
+ end
16
+
17
+ def rev
18
+ doc[:_rev]
19
+ end
20
+
21
+ def rev= value
22
+ doc[:_rev] = value
23
+ end
24
+
25
+ def id_as_resource
26
+ CGI.escape(id)
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,31 @@
1
+ require 'dolly/property_set'
2
+ require 'dolly/property'
3
+
4
+ module Dolly
5
+ module Properties
6
+ SPECIAL_KEYS = %i[_id _rev]
7
+
8
+ def property *opts, class_name: nil, default: nil
9
+ opts.each do |opt|
10
+ properties << (prop = Property.new(opt, class_name, default))
11
+ send(:attr_reader, opt)
12
+
13
+ define_method(:"#{opt}=") { |value| write_attribute(opt, value) }
14
+ define_method(:"#{opt}?") { send(opt) } if prop.boolean?
15
+ define_method(:"[]") {|name| send(name) }
16
+ end
17
+ end
18
+
19
+ def properties
20
+ @properties ||= PropertySet.new
21
+ end
22
+
23
+ def property_keys
24
+ properties.map(&:key) - SPECIAL_KEYS
25
+ end
26
+
27
+ def property_clean_doc(doc)
28
+ doc.reject { |key, _value| !property_keys.include?(key) }
29
+ end
30
+ end
31
+ end