dolly 3.0.1 → 3.1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 52fcc7a3f0888239aaff1a6398a81112d031722db14d3070d83d574179414197
4
- data.tar.gz: 20d5efb134448842c4c547d85f0f395993fdd8817898e7b174e3e5f5714a7022
3
+ metadata.gz: 2bd1cf887f25cacb3b25be56354cf74c1771cd021514cb8bf733e31aa7198feb
4
+ data.tar.gz: cd464f54583a1f417fb58e27f504ccce8ddd33f39f1f48eea0f3b66487313445
5
5
  SHA512:
6
- metadata.gz: f684874eaa87448a0ae67fddf5df98f3e86c5c6a53f8fb7b75e1331d16d63f07e6ef8eb2db17e461609433ccaf770a1c3a3c27b1551b5cf7c823f2d010005075
7
- data.tar.gz: f44fa8ea168a1d6ca31042c4eed5b8cf0fdb1fd11dfcc5fb76e177598cce96512e6cca8a842e6c646fa4f65f8e4e0ea9759379ad73954c42fb1694c9803d42c3
6
+ metadata.gz: 5c2bddaf348f5028c10ea464ea3265a9653d741508a103d3bd4d3e83757261ead6de60d630db9a8fa512ba22e13cd36043debc8e8d7be457aa63079018a3d580
7
+ data.tar.gz: f1c2ede8149132f5d6941fbbe0b4eb1ada18ed060eb76ccd8b4eeffec556590b2b8c8044331d6726de10a0758120a6595b07f6da5afdd86da95be655344ceb8b
@@ -1,3 +1,4 @@
1
+ require 'curb'
1
2
  require 'oj'
2
3
  require 'cgi'
3
4
  require 'net/http'
@@ -11,19 +12,20 @@ module Dolly
11
12
  include Dolly::Configuration
12
13
  attr_reader :db, :app_env
13
14
 
14
- DEFAULT_HEADER = { 'Content-Type' => 'application/json' }
15
+ DEFAULT_HEADER = { 'Content-Type' => 'application/json', 'Accept' => 'application/json' }
15
16
  SECURE_PROTOCOL = 'https'
17
+ DEFAULT_DATABASE = :default
16
18
 
17
19
  using StringRefinements
18
20
 
19
- def initialize db = :default, app_env = :development
21
+ def initialize db = DEFAULT_DATABASE, app_env = :development
20
22
  @db = db
21
23
  @app_env = app_env
22
24
  end
23
25
 
24
26
  def get(resource, data = {})
25
27
  query = { query: values_to_json(data) } if data
26
- request :get, resource.cgi_escape, query
28
+ request :get, resource, query
27
29
  end
28
30
 
29
31
  def post resource, data
@@ -35,9 +37,9 @@ module Dolly
35
37
  end
36
38
 
37
39
  def delete resource, rev = nil, escape: true
38
- query = { query: { rev: rev } } if rev
39
- resource = resource.cgi_escape if escape
40
- request :delete, resource, query
40
+ query = "?rev=#{rev}" if rev
41
+ resource = "#{escape ? resource.cgi_escape : resource}#{query}"
42
+ request :delete, resource
41
43
  end
42
44
 
43
45
  def view resource, opts
@@ -61,53 +63,36 @@ module Dolly
61
63
  end
62
64
 
63
65
  def request(method, resource, data = {})
64
- headers = Dolly::HeaderRequest.new data&.delete(:headers)
65
- uri = build_uri(resource, data&.delete(:query))
66
- klass = request_method(method)
67
- req = klass.new(uri, headers)
68
- req.body = format_data(data, headers.json?)
69
- response = start_request(req)
70
-
71
- response_format(response, method)
66
+ headers = Dolly::HeaderRequest.new(data&.delete(:headers))
67
+ data&.merge!(data&.delete(:query) || {})
68
+ db_resource = (resource =~ %r{^/}) ? resource : "/#{db_name}/#{resource}"
69
+ uri = URI("#{base_uri}#{db_resource}")
70
+ conn = curl_method_call(method, uri, data) do |curl|
71
+ if env['username'].present?
72
+ curl.http_auth_types = :basic
73
+ curl.username = env['username']
74
+ curl.password = env['password'].to_s
75
+ end
76
+
77
+ headers.each { |k, v| curl.headers[k] = v } if headers.present?
78
+ end
79
+ response_format(conn, method)
72
80
  end
73
81
 
74
82
  private
75
83
 
76
- def start_request(req)
77
- req.basic_auth env['username'], env['password'] if env['username']&.present?
78
-
79
- http = Net::HTTP.new(req.uri.host, req.uri.port)
80
- http.use_ssl = secure?
81
-
82
- http.request(req)
83
- end
84
-
85
- def secure?
86
- env['protocol'] == SECURE_PROTOCOL
84
+ def curl_method_call(method, uri, data, &block)
85
+ return Curl::Easy.http_head(uri.to_s, &block) if method.to_sym == :head
86
+ return Curl.delete(uri.to_s, &block) if method.to_sym == :delete
87
+ return Curl.send(method, uri, data, &block) if method.to_sym == :get
88
+ Curl.send(method, uri.to_s, data.to_json, &block)
87
89
  end
88
90
 
89
91
  def response_format(res, method)
90
- raise Dolly::ResourceNotFound if res.code.to_i == 404
91
- raise Dolly::ServerError.new(res.body) if (400..600).include? res.code.to_i
92
- return res if method == :head
93
- Oj.load(res.body, symbol_keys: true)
94
- end
95
-
96
- def format_data(data = nil, is_json)
97
- return unless data
98
- body = data.delete(:_body) || data
99
- is_json ? body.to_json : body
100
- end
101
-
102
- def build_uri(resource, query = nil)
103
- query_str = "?#{to_query(query)}" if query
104
- uri = (resource =~ %r{^/}) ? resource : "/#{db_name}/#{resource}"
105
-
106
- URI("#{base_uri}#{uri}#{query_str}")
107
- end
108
-
109
- def request_method(method_name)
110
- Object.const_get("Net::HTTP::#{method_name.capitalize}")
92
+ raise Dolly::ResourceNotFound if res.status.to_i == 404
93
+ raise Dolly::ServerError.new(res.status.to_i) if (400..600).include? res.status.to_i
94
+ return res.header_str if method == :head
95
+ Oj.load(res.body_str, symbol_keys: true)
111
96
  end
112
97
 
113
98
  def values_to_json hash
data/lib/dolly/mango.rb CHANGED
@@ -47,9 +47,10 @@ module Dolly
47
47
  end
48
48
 
49
49
  def find_doc_by(query, opts = {})
50
- raise Dolly::IndexNotFoundError unless index_exists?(query)
51
50
  opts.merge!(limit: 1)
52
- perform_query(build_query(query, opts))[:docs].first
51
+ response = perform_query(build_query(query, opts))
52
+ print_index_warning(query) if response.fetch(:warning, nil)
53
+ response[:docs].first
53
54
  end
54
55
 
55
56
  def where(query, opts = {})
@@ -59,21 +60,55 @@ module Dolly
59
60
  end
60
61
 
61
62
  def docs_where(query, opts = {})
62
- raise Dolly::IndexNotFoundError unless index_exists?(query)
63
- perform_query(build_query(query, opts))[:docs]
63
+ response = perform_query(build_query(query, opts))
64
+ print_index_warning(query) if response.fetch(:warning, nil)
65
+ response[:docs]
64
66
  end
65
67
 
66
- private
68
+ def find_bare(id, fields, options = {})
69
+ q = { _id: id }
70
+ opts = { fields: fields }.merge(options)
71
+ query = build_query(q, opts)
72
+ response = perform_query(query)
73
+ response[:docs]
74
+ end
67
75
 
68
- def build_model_from_doc(doc)
69
- return nil if doc.nil?
70
- new(doc.slice(*all_property_keys))
76
+ def where_bare(selector, fields, options = {})
77
+ opts = { fields: fields }.merge(options)
78
+ query = build_query(selector, opts)
79
+ response = perform_query(query)
80
+ response[:docs]
81
+ end
82
+
83
+ def find_with_metadata(query, options = {})
84
+ opts = options.merge!(limit: 1)
85
+ perform_query(build_query(query, opts))
86
+ end
87
+
88
+ def where_with_metadata(query, options = {})
89
+ perform_query(build_query(query, options))
71
90
  end
72
91
 
73
92
  def perform_query(structured_query)
74
93
  connection.post(DESIGN, structured_query)
75
94
  end
76
95
 
96
+ private
97
+
98
+ def print_index_warning(query)
99
+ message = "Index not found for #{query.inspect}"
100
+ if (defined?(Rails.logger) && Rails&.env&.development?)
101
+ Rails.logger.info(message)
102
+ else
103
+ puts message
104
+ end
105
+ end
106
+
107
+ def build_model_from_doc(doc)
108
+ return nil if doc.nil?
109
+ new(doc.slice(*all_property_keys)).tap { |d| d.rev = doc[:_rev] }
110
+ end
111
+
77
112
  def build_query(query, opts)
78
113
  { 'selector' => build_selectors(query) }.merge(opts)
79
114
  end
@@ -88,15 +123,12 @@ module Dolly
88
123
  end
89
124
 
90
125
  def build_key(key)
126
+ return key if key.to_s.starts_with?(SELECTOR_SYMBOL)
91
127
  "#{SELECTOR_SYMBOL}#{key}"
92
128
  end
93
129
 
94
130
  def is_operator?(key)
95
- ALL_OPERATORS.include?(key)
96
- end
97
-
98
- def index_exists?(query)
99
- Dolly::MangoIndex.find_by_fields(fetch_fields(query))
131
+ ALL_OPERATORS.include?(key) || key.to_s.starts_with?(SELECTOR_SYMBOL)
100
132
  end
101
133
 
102
134
  def fetch_fields(query)
@@ -23,9 +23,13 @@ module Dolly
23
23
  post(DESIGN, build_index_structure(name, fields, type))
24
24
  end
25
25
 
26
+ def create_in_database(database, name, fields, type = 'json')
27
+ connection_for_database(database).post(DESIGN, build_index_structure(name, fields, type))
28
+ end
29
+
26
30
  def find_by_fields(fields)
27
31
  rows = get(ALL_DOCS, key: key_from_fields(fields))[ROWS_KEY]
28
- rows && rows.any?
32
+ (rows && rows.any?)
29
33
  end
30
34
 
31
35
  def delete_all
@@ -42,6 +46,10 @@ module Dolly
42
46
 
43
47
  private
44
48
 
49
+ def connection_for_database(database)
50
+ Dolly::Connection.new(database.to_sym, Rails.env || :development)
51
+ end
52
+
45
53
  def connection
46
54
  @connection ||= Dolly::Document.connection
47
55
  end
@@ -2,6 +2,7 @@ module Dolly
2
2
  module PropertyManager
3
3
  def build_property(attributes)
4
4
  assign_identity_properties(attributes)
5
+ assign_rev_properties(attributes)
5
6
 
6
7
  lambda do |property|
7
8
  name = property.key.to_sym
@@ -17,14 +18,14 @@ module Dolly
17
18
  end
18
19
  end
19
20
 
20
- def write_attribute key, value
21
+ def write_attribute(key, value)
21
22
  value = set_property_value(key, value)
22
23
  instance_variable_set(:"@#{key}", value)
23
- update_doc(key, value) unless value.nil?
24
+ update_doc(key, value)
24
25
  end
25
26
 
26
27
  def valid_property?(name)
27
- properties.include? name
28
+ properties.include?(name)
28
29
  end
29
30
 
30
31
  def update_doc(key, value)
@@ -43,5 +44,10 @@ module Dolly
43
44
  id_presence = opts[:id] || opts[:_id] || opts['id'] || opts['_id']
44
45
  self.id = id_presence if id_presence
45
46
  end
47
+
48
+ def assign_rev_properties(opts = {})
49
+ rev_presence = opts[:rev] || opts [:_rev] || opts['rev'] || opts['_rev']
50
+ self.rev = rev_presence if rev_presence
51
+ end
46
52
  end
47
53
  end
data/lib/dolly/query.rb CHANGED
@@ -11,12 +11,30 @@ module Dolly
11
11
  using StringRefinements
12
12
 
13
13
  def find *keys
14
- query_hash = { keys: namespace_keys(keys).map { |k| k.cgi_escape } }
14
+ query_hash = { keys: namespace_keys(keys) }
15
15
 
16
16
  build_collection(query_hash).first_or_all&.itself ||
17
17
  raise(Dolly::ResourceNotFound)
18
18
  end
19
19
 
20
+ def bulk_find(*keys_to_find)
21
+ data = {
22
+ query: { include_docs: true },
23
+ keys: keys_to_find.map { |key| namespace_key(key) }
24
+ }
25
+
26
+ res = connection.post('_all_docs', data)
27
+ Collection.new(rows: res, options: { doc_type: self.class_name })
28
+ end
29
+
30
+ def find_all(*keys)
31
+ query_hash = { keys: namespace_keys(keys) }
32
+ return [] if query_hash[:keys].none?
33
+
34
+ keys_to_find_counter = query_hash[:keys].length
35
+ build_collection(query_hash).first_or_all(true)&.itself
36
+ end
37
+
20
38
  def safe_find *keys
21
39
  find *keys
22
40
  rescue Dolly::ResourceNotFound
@@ -38,7 +56,7 @@ module Dolly
38
56
  end
39
57
 
40
58
  def find_with doc, view_name, opts = {}
41
- opts = opts.each_with_object({}) { |(k, v), h| h[k] = escape_value(v) }
59
+ opts = opts.each_with_object({}) { |(k, v), h| h[k] = v }
42
60
  query_results = raw_view(doc, view_name, opts)
43
61
 
44
62
  Collection.new({ rows: query_results, options: {} }).first_or_all
@@ -3,7 +3,7 @@
3
3
  module Dolly
4
4
  module QueryArguments
5
5
  def last_item_in_range
6
- URI.escape("\ufff0")
6
+ "\ufff0"
7
7
  end
8
8
 
9
9
  def default_query_args
@@ -7,7 +7,7 @@ module Dolly
7
7
  CONTENT_TYPE_KEY = 'Content-Type'
8
8
  JSON_CONTENT = 'application/json'
9
9
 
10
- def_delegators :@collection, :[], :[]=, :keys, :each
10
+ def_delegators :@collection, :[], :[]=, :keys, :each, :present?, :merge!
11
11
 
12
12
  def initialize hash = nil
13
13
  @collection = hash || default_value
data/lib/dolly/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Dolly
2
- VERSION = "3.0.1"
2
+ VERSION = "3.1.0"
3
3
  end
@@ -2,13 +2,20 @@ require 'dolly/class_methods_delegation'
2
2
 
3
3
  module Dolly
4
4
  module ViewQuery
5
- def raw_view(doc, view_name, opts = {})
6
- design = "_design/#{doc}/_view/#{view_name}"
5
+ def raw_view(design, view_name, opts = {})
6
+ design = "_design/#{design}/_view/#{view_name}"
7
7
  connection.view(design, opts)
8
8
  end
9
9
 
10
10
  def view_value(doc, view_name, opts = {})
11
11
  raw_view(doc, view_name, opts)[:rows].flat_map { |result| result[:value] }
12
12
  end
13
+
14
+ def collection_view(design, view_name, opts = {})
15
+ opts.delete(:include_docs)
16
+ design = "_design/#{design}/_view/#{view_name}"
17
+ response = connection.view(design, opts)
18
+ Dolly::Collection.new(rows: response, options: opts)
19
+ end
13
20
  end
14
21
  end
data/lib/tasks/db.rake CHANGED
@@ -69,8 +69,15 @@ namespace :db do
69
69
 
70
70
  files.each do |file|
71
71
  index_data = JSON.parse(File.read(file))
72
- puts "Creating index: #{index_data["name"]}"
73
- puts Dolly::MangoIndex.create(index_data['name'], index_data['fields'])
72
+ database = index_data.fetch('db', 'default').to_sym
73
+ puts "*" * 100
74
+ puts "Creating index: #{index_data["name"]} for database: #{database}"
75
+
76
+ if database == Dolly::Connection::DEFAULT_DATABASE
77
+ puts Dolly::MangoIndex.create(index_data['name'], index_data['fields'])
78
+ else
79
+ puts Dolly::MangoIndex.create_in_database(database, index_data['name'], index_data['fields'])
80
+ end
74
81
  end
75
82
  end
76
83
  end
@@ -0,0 +1,18 @@
1
+ require 'test_helper'
2
+
3
+ class TestDoc < Dolly::Document
4
+ property :name, class_name: String
5
+ property :email, class_name: String
6
+ property :last_name, class_name: String
7
+ end
8
+
9
+ class PropertyManagerTest < Test::Unit::TestCase
10
+ test 'write_attribute with nil value' do
11
+ doc = TestDoc.new(name: 'name', last_name: nil, email: 'does not change')
12
+ assert_equal(doc.name, 'name')
13
+ doc.update_properties(name: nil)
14
+ assert_equal(doc.name, nil)
15
+ assert_equal(doc.last_name, nil)
16
+ assert_equal(doc.email, 'does not change')
17
+ end
18
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dolly
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.1
4
+ version: 3.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - javierg
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-09-02 00:00:00.000000000 Z
11
+ date: 2021-05-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: oj
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: curb
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '='
32
+ - !ruby/object:Gem::Version
33
+ version: 0.9.8
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '='
39
+ - !ruby/object:Gem::Version
40
+ version: 0.9.8
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: bundler
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -44,14 +58,14 @@ dependencies:
44
58
  requirements:
45
59
  - - "~>"
46
60
  - !ruby/object:Gem::Version
47
- version: '10.0'
61
+ version: '13.0'
48
62
  type: :development
49
63
  prerelease: false
50
64
  version_requirements: !ruby/object:Gem::Requirement
51
65
  requirements:
52
66
  - - "~>"
53
67
  - !ruby/object:Gem::Version
54
- version: '10.0'
68
+ version: '13.0'
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: test-unit-full
57
71
  requirement: !ruby/object:Gem::Requirement
@@ -141,13 +155,14 @@ files:
141
155
  - test/inheritance_test.rb
142
156
  - test/mango_index_test.rb
143
157
  - test/mango_test.rb
158
+ - test/property_manager_test.rb
144
159
  - test/support/test.txt
145
160
  - test/test_helper.rb
146
161
  - test/view_query_test.rb
147
162
  homepage: https://www.amco.me
148
163
  licenses: []
149
164
  metadata: {}
150
- post_install_message:
165
+ post_install_message:
151
166
  rdoc_options: []
152
167
  require_paths:
153
168
  - lib
@@ -162,8 +177,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
162
177
  - !ruby/object:Gem::Version
163
178
  version: '0'
164
179
  requirements: []
165
- rubygems_version: 3.1.4
166
- signing_key:
180
+ rubygems_version: 3.0.3
181
+ signing_key:
167
182
  specification_version: 4
168
183
  summary: will write something
169
184
  test_files:
@@ -178,3 +193,4 @@ test_files:
178
193
  - test/support/test.txt
179
194
  - test/test_helper.rb
180
195
  - test/document_test.rb
196
+ - test/property_manager_test.rb