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 +4 -4
- data/lib/dolly/connection.rb +31 -46
- data/lib/dolly/mango.rb +45 -13
- data/lib/dolly/mango_index.rb +9 -1
- data/lib/dolly/property_manager.rb +9 -3
- data/lib/dolly/query.rb +20 -2
- data/lib/dolly/query_arguments.rb +1 -1
- data/lib/dolly/request_header.rb +1 -1
- data/lib/dolly/version.rb +1 -1
- data/lib/dolly/view_query.rb +9 -2
- data/lib/tasks/db.rake +9 -2
- data/test/property_manager_test.rb +18 -0
- metadata +24 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2bd1cf887f25cacb3b25be56354cf74c1771cd021514cb8bf733e31aa7198feb
|
4
|
+
data.tar.gz: cd464f54583a1f417fb58e27f504ccce8ddd33f39f1f48eea0f3b66487313445
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5c2bddaf348f5028c10ea464ea3265a9653d741508a103d3bd4d3e83757261ead6de60d630db9a8fa512ba22e13cd36043debc8e8d7be457aa63079018a3d580
|
7
|
+
data.tar.gz: f1c2ede8149132f5d6941fbbe0b4eb1ada18ed060eb76ccd8b4eeffec556590b2b8c8044331d6726de10a0758120a6595b07f6da5afdd86da95be655344ceb8b
|
data/lib/dolly/connection.rb
CHANGED
@@ -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 =
|
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
|
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 = {
|
39
|
-
resource = resource.cgi_escape
|
40
|
-
request :delete, resource
|
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
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
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
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
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.
|
91
|
-
raise Dolly::ServerError.new(res.
|
92
|
-
return res if method == :head
|
93
|
-
Oj.load(res.
|
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))
|
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
|
-
|
63
|
-
|
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
|
-
|
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
|
69
|
-
|
70
|
-
|
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)
|
data/lib/dolly/mango_index.rb
CHANGED
@@ -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
|
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)
|
24
|
+
update_doc(key, value)
|
24
25
|
end
|
25
26
|
|
26
27
|
def valid_property?(name)
|
27
|
-
properties.include?
|
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)
|
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] =
|
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
|
data/lib/dolly/request_header.rb
CHANGED
@@ -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
data/lib/dolly/view_query.rb
CHANGED
@@ -2,13 +2,20 @@ require 'dolly/class_methods_delegation'
|
|
2
2
|
|
3
3
|
module Dolly
|
4
4
|
module ViewQuery
|
5
|
-
def raw_view(
|
6
|
-
design = "_design/#{
|
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
|
-
|
73
|
-
puts
|
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
|
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:
|
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: '
|
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: '
|
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.
|
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
|