dolly 1.1.7 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -1,85 +1,96 @@
1
+ require 'refinements/string_refinements'
2
+
1
3
  module Dolly
2
4
  class Property
3
- attr_writer :value
4
- attr_accessor :name
5
- attr_reader :class_name, :default
5
+ attr_reader :key, :class_name, :default
6
+ CANT_CLONE = [NilClass, TrueClass, FalseClass, Integer]
6
7
 
7
- CANT_CLONE = [NilClass, TrueClass, FalseClass, Fixnum].freeze
8
+ using StringRefinements
8
9
 
9
- def initialize opts = {}
10
- @class_name = opts.delete(:class_name) if opts.present?
11
- @name = opts.delete(:name).to_s
12
- @default = opts.delete(:default)
13
- @default = @default.clone if @default && CANT_CLONE.none? { |klass| @default.is_a? klass }
14
- @value = @default if @default
15
- warn 'There are some unprocessed options!' if opts.present?
10
+ def initialize(key, class_name, default = nil)
11
+ @key = key
12
+ @default = default
13
+ @class_name = class_name
16
14
  end
17
15
 
18
- def value
19
- #TODO: tets if this actually sets `doc[ "name" ]`
20
- return @default if @value.nil?
21
- return @value unless self_klass
22
-
23
- klass_sym = :"#{self_klass.name.underscore}_#{__method__}"
16
+ def cast_value(value)
17
+ return set_default if value.nil?
18
+ return value unless class_name
19
+ return self_klass.new(value) unless respond_to?(klass_sym)
20
+ send(klass_sym, value)
21
+ end
24
22
 
25
- return self_klass.new @value unless self.respond_to?(klass_sym)
23
+ def boolean?
24
+ [TrueClass, FalseClass].include?(class_name)
25
+ end
26
26
 
27
- self.send klass_sym
27
+ def string_value(value)
28
+ value.to_s
28
29
  end
29
30
 
30
- def array_value
31
- @value.to_a
31
+ def hash_value(value)
32
+ value.to_h
32
33
  end
33
34
 
34
- def hash_value
35
- @value.to_h
35
+ def integer_value(value)
36
+ value.to_i
36
37
  end
37
38
 
38
- def string_value
39
- @value.to_s
39
+ def float_value(value)
40
+ value.to_f
40
41
  end
41
42
 
42
- def integer_value
43
- @value.to_i
43
+ def date_value(value)
44
+ return value.to_date if value.respond_to?(:to_date)
45
+ Date.parse(value)
44
46
  end
45
47
 
46
- def float_value
47
- @value.to_f
48
+ def time_value(value)
49
+ return value.to_time if value.respond_to?(:to_time)
50
+ DateTime.parse(value).to_time
48
51
  end
49
52
 
50
- def date_value
51
- @value.to_date
53
+ def date_time_value(value)
54
+ return value.to_datetime if value.respond_to?(:to_datetime)
55
+ DateTime.parse(value)
52
56
  end
53
57
 
54
- def time_value
55
- @value.to_time
58
+ def true_class_value(value)
59
+ truthy_value?(value)
56
60
  end
57
61
 
58
- def date_time_value
59
- @value.to_datetime
62
+ def false_class_value(value)
63
+ truthy_value?(value)
60
64
  end
61
65
 
62
- def true_class_value
63
- truthy_value?
66
+ private
67
+
68
+ def truthy_value?(value)
69
+ value =~ /true/ || value === true
64
70
  end
65
71
 
66
- def false_class_value
67
- truthy_value?
72
+ def klass_sym
73
+ :"#{self_klass.name.underscore}_value"
68
74
  end
69
75
 
70
- def boolean?
71
- self_klass == TrueClass || self_klass == FalseClass
76
+ def self_klass
77
+ return unless class_name
78
+ return class_name if class_name.is_a?(Class)
79
+ Object.const_get class_name
72
80
  end
73
81
 
74
- private
75
- def truthy_value?
76
- @value =~ /true/ || @value === true
82
+ def set_default
83
+ return unless default_present?
84
+ return default unless cant_clone_default?
85
+ default.clone
77
86
  end
78
87
 
79
- def self_klass
80
- return unless @class_name
81
- @class_name.is_a?(Class)? @class_name : @class_name.constantize
88
+ def cant_clone_default?
89
+ CANT_CLONE.none? { |klass| default.is_a? klass }
82
90
  end
83
91
 
92
+ def default_present?
93
+ !default.nil?
94
+ end
84
95
  end
85
96
  end
@@ -0,0 +1,47 @@
1
+ module Dolly
2
+ module PropertyManager
3
+ def build_property(attributes)
4
+ assign_identity_properties(attributes)
5
+
6
+ lambda do |property|
7
+ name = property.key.to_sym
8
+ next unless doc[name].nil?
9
+ write_attribute(name, attributes[name])
10
+ end
11
+ end
12
+
13
+ def update_attribute
14
+ lambda do |key, value|
15
+ raise InvalidProperty unless valid_property?(key)
16
+ write_attribute(key, value)
17
+ end
18
+ end
19
+
20
+ def write_attribute key, value
21
+ value = set_property_value(key, value)
22
+ instance_variable_set(:"@#{key}", value)
23
+ update_doc(key, value) unless value.nil?
24
+ end
25
+
26
+ def valid_property?(name)
27
+ properties.include? name
28
+ end
29
+
30
+ def update_doc(key, value)
31
+ doc[key] = value
32
+ end
33
+
34
+ def properties
35
+ self.class.properties
36
+ end
37
+
38
+ def set_property_value(key, value)
39
+ properties[key].cast_value(value)
40
+ end
41
+
42
+ def assign_identity_properties(opts = {})
43
+ id_presence = opts[:id] || opts[:_id] || opts['id'] || opts['_id']
44
+ self.id = id_presence if id_presence
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,18 @@
1
+ module Dolly
2
+ class PropertySet < Set
3
+ def include? key
4
+ keys.include?(key)
5
+ end
6
+
7
+ def [](key)
8
+ return detect {|property| property.key == key } if key.is_a?(Symbol)
9
+ super
10
+ end
11
+
12
+ private
13
+
14
+ def keys
15
+ map(&:key)
16
+ end
17
+ end
18
+ end
@@ -1,85 +1,57 @@
1
- require "dolly/connection"
2
- require "dolly/collection"
3
- require "dolly/name_space"
4
- require "exceptions/dolly"
1
+ require 'dolly/collection'
2
+ require 'dolly/query_arguments'
3
+ require 'dolly/document_type'
5
4
 
6
5
  module Dolly
7
6
  module Query
8
- module ClassMethods
9
- include Dolly::NameSpace
10
- include Dolly::Connection
11
- attr_accessor :properties
7
+ include QueryArguments
8
+ include DocumentType
12
9
 
13
- DESIGN_DOC = "dolly"
10
+ def find *keys
11
+ query_hash = { keys: namespace_keys(keys) }
14
12
 
15
- def find *keys
16
- query_hash = { keys: keys.map{|key| namespace key} }
17
-
18
- if keys.count > 1
19
- build_collection( query_hash )
20
- else
21
- self.new.from_json( database.all_docs(query_hash).parsed_response )
22
- end
23
- rescue NoMethodError => err
24
- if err.message == "undefined method `[]' for nil:NilClass"
25
- raise Dolly::ResourceNotFound
26
- else
27
- raise
28
- end
29
- end
30
-
31
- def default_query_args
32
- {startkey: "#{name_paramitized}/", endkey: "#{name_paramitized}/\ufff0"}
33
- end
34
-
35
- def all
36
- build_collection default_query_args
37
- end
38
-
39
- def first limit = 1
40
- res = build_collection default_query_args.merge( limit: 1)
41
- limit == 1 ? res.first : res
42
- end
13
+ build_collection(query_hash).first_or_all&.itself ||
14
+ raise(Dolly::ResourceNotFound)
15
+ end
43
16
 
44
- def last limit = 1
45
- res = build_collection({startkey: default_query_args[:endkey], endkey: default_query_args[:startkey], limit: limit, descending: true})
46
- limit == 1 ? res.first : res
47
- end
17
+ def safe_find *keys
18
+ find *keys
19
+ rescue Dolly::ResourceNotFound
20
+ nil
21
+ end
48
22
 
49
- def build_collection q
50
- res = database.all_docs(q)
51
- Collection.new res.parsed_response, name_for_class
52
- end
23
+ def all
24
+ build_collection(default_query_args)
25
+ end
53
26
 
54
- def find_with doc, view_name, opts = {}
55
- res = view "_design/#{doc}/_view/#{view_name}", opts
56
- Collection.new res.parsed_response, name_for_class
57
- end
27
+ def first limit = 1
28
+ query_hash = default_query_args.merge(limit: limit)
29
+ build_collection(query_hash).first_or_all(limit > 1)
30
+ end
58
31
 
59
- #TODO: new implementation for collection returning
60
- # multiple types is failling when the class has a namespace
61
- # as the namespace does not exists on the doc id
62
- # we need to reimplement this through a references class method.
63
- def name_for_class
64
- if name.include? '::'
65
- name.constantize
66
- end
67
- end
32
+ def last limit = 1
33
+ query_hash = descending_query_args.merge(limit: limit)
34
+ build_collection(query_hash).first_or_all(limit > 1)
35
+ end
68
36
 
69
- def view doc, options = {}
70
- options.merge! include_docs: true
71
- database.get doc, options
72
- end
37
+ def find_with doc, view_name, opts = {}
38
+ opts = opts.each_with_object({}) { |(k, v), h| h[k] = escape_value(v) }
39
+ query_results = raw_view(doc, view_name, opts)
73
40
 
74
- def raw_view doc, view, opts = {}
75
- JSON.parse database.get "_design/#{doc}/_view/#{view}", opts
76
- end
41
+ Collection.new(query_results).first_or_all
42
+ end
77
43
 
44
+ def raw_view doc, view_name, opts = {}
45
+ design = "_design/#{doc}/_view/#{view_name}"
46
+ connection.view(design, opts)
78
47
  end
79
48
 
80
- def self.included(base)
81
- base.extend ClassMethods
49
+ def build_collection(query)
50
+ Collection.new(connection.get('_all_docs', query.merge(include_docs: true)))
82
51
  end
83
52
 
53
+ def bulk_document
54
+ BulkDocument.new(connection)
55
+ end
84
56
  end
85
57
  end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dolly
4
+ module QueryArguments
5
+ def last_item_in_range
6
+ URI.escape("\ufff0")
7
+ end
8
+
9
+ def default_query_args
10
+ {
11
+ startkey: "#{name_paramitized}/",
12
+ endkey: "#{name_paramitized}/#{last_item_in_range}"
13
+ }
14
+ end
15
+
16
+ def descending_query_args
17
+ {
18
+ startkey: default_query_args[:endkey],
19
+ endkey: default_query_args[:startkey],
20
+ descending: true
21
+ }
22
+ end
23
+
24
+ def escape_value(value)
25
+ return value if value.is_a? Numeric
26
+ return escape_values(value) if value.is_a? Array
27
+ return CGI.escape(value) if value.is_a? String
28
+ value
29
+ end
30
+
31
+ def escape_values *values
32
+ values.flatten.map { |value| escape_value(value) }
33
+ end
34
+ end
35
+ end
@@ -1,119 +1,24 @@
1
- require "httparty"
2
- require "dolly/bulk_document"
3
-
4
1
  module Dolly
5
-
6
- class Request
7
- include HTTParty
8
- REQUIRED_KEYS = %w/host port name/.freeze
9
-
10
- attr_accessor :database_name, :host, :port, :bulk_document
11
-
12
- def initialize options = {}
13
- REQUIRED_KEYS.each do |key|
14
- raise Dolly::MissingRequestConfigSettings.new(key) unless options[key]
15
- end
16
-
17
- @host = options["host"]
18
- @port = options["port"]
19
- @database_name = options["name"]
20
- @username = options["username"]
21
- @password = options["password"]
22
- @protocol = options["protocol"]
23
-
24
- @bulk_document = Dolly::BulkDocument.new []
25
- self.class.base_uri "#{protocol}://#{host}:#{port}"
26
- end
27
-
28
- def get resource, data = nil
29
- q = {query: values_to_json(data)} if data
30
- request :get, full_path(resource), q
31
- end
32
-
33
- def stats
34
- request :get, "/#{database_name}"
35
- end
36
-
37
- def put resource, data
38
- request :put, full_path(resource), {body: data}
39
- end
40
-
41
- def post resource, data
42
- request :post, full_path(resource), {body: data}
43
- end
44
-
45
- def delete resource
46
- request :delete, full_path(resource), {}
2
+ module Request
3
+ def set_namespace name
4
+ @namspace = name
47
5
  end
48
6
 
49
- def attach resource, attachment_name, data, headers = {}
50
- data = StringIO.new(data) if data.is_a?(String)
51
- request :put, attachment_path(resource, attachment_name), {body: data, headers: headers}
7
+ def set_app_env env
8
+ @app_env = env
52
9
  end
53
10
 
54
- def protocol
55
- @protocol ||= 'http'
11
+ def connection
12
+ @connection ||= Connection.new(namespace, app_env)
56
13
  end
57
14
 
58
- def uuids opts = {}
59
- tools("_uuids", opts)["uuids"]
15
+ def namespace
16
+ @namespace ||= :default
60
17
  end
61
18
 
62
- def all_docs data = {}
63
- data = values_to_json data.merge( include_docs: true )
64
- request :get, full_path('_all_docs'), {query: data}
65
- end
66
-
67
- def request method, resource, data = nil
68
- data ||= {}
69
- data.merge!(basic_auth: auth_info) if auth_info.present?
70
- headers = { 'Content-Type' => 'application/json' }
71
- headers.merge! data[:headers] if data[:headers]
72
- response = self.class.send method, resource, data.merge(headers: headers)
73
- log_request(resource, response.code) if Dolly.log_requests?
74
- if response.code == 404
75
- raise Dolly::ResourceNotFound
76
- elsif (400..600).include? response.code
77
- raise Dolly::ServerError.new( response )
78
- else
79
- response
80
- end
81
- end
82
-
83
- private
84
- def tools path, opts = nil
85
- data = {}
86
- q = "?#{CGI.unescape(opts.to_query)}" unless opts.blank?
87
- data.merge!(basic_auth: auth_info) if auth_info.present?
88
- JSON::parse self.class.get("/#{path}#{q}", data)
89
- end
90
-
91
- def auth_info
92
- return nil unless @username.present?
93
- {username: @username, password: @password}
94
- end
95
-
96
- def values_to_json hash
97
- hash.reduce({}){|h, v| h[v.first] = v.last.to_json; h}
98
- end
99
-
100
- def full_path resource
101
- "/#{database_name}/#{resource}"
102
- end
103
-
104
- def attachment_path resource, attachment_name
105
- "#{full_path(resource)}/#{attachment_name}"
106
- end
107
-
108
- def log_request resource, response_code
109
- log_value = ->(resource, response_code) { "Query: #{resource}, Response Code: #{response_code}" }
110
- case response_code
111
- when 200..399
112
- Dolly.logger.info log_value[resource, response_code]
113
- when 400..600
114
- Dolly.logger.warn log_value[resource, response_code]
115
- end
19
+ def app_env
20
+ return Rails.env if defined? Rails.env
21
+ @app_env ||= :development
116
22
  end
117
23
  end
118
-
119
24
  end