dolly 3.0.1 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
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