lhs 3.0.0 → 3.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.localch.yml +190 -0
- data/.rubocop.yml +7 -0
- data/cider-ci.yml +2 -1
- data/cider-ci/jobs/rspec.yml +48 -0
- data/cider-ci/jobs/rubocop.yml +55 -0
- data/cider-ci/scripts/bundle.yml +2 -0
- data/cider-ci/scripts/github_comment.yml +6 -0
- data/cider-ci/scripts/rspec.yml +4 -0
- data/cider-ci/scripts/rubocop.yml +5 -0
- data/cider-ci/scripts/ruby-version.yml +2 -0
- data/cider-ci/scripts/tmp-cache.yml +2 -0
- data/lhs.gemspec +1 -0
- data/lib/lhs/collection.rb +2 -6
- data/lib/lhs/concerns/data/json.rb +1 -1
- data/lib/lhs/concerns/item/save.rb +11 -10
- data/lib/lhs/concerns/item/update.rb +1 -1
- data/lib/lhs/concerns/item/validation.rb +2 -2
- data/lib/lhs/concerns/record/all.rb +1 -2
- data/lib/lhs/concerns/record/batch.rb +2 -3
- data/lib/lhs/concerns/record/create.rb +7 -8
- data/lib/lhs/concerns/record/endpoints.rb +2 -3
- data/lib/lhs/concerns/record/find.rb +6 -6
- data/lib/lhs/concerns/record/find_by.rb +8 -8
- data/lib/lhs/concerns/record/first.rb +0 -1
- data/lib/lhs/concerns/record/includes.rb +0 -1
- data/lib/lhs/concerns/record/mapping.rb +0 -1
- data/lib/lhs/concerns/record/model.rb +0 -1
- data/lib/lhs/concerns/record/request.rb +18 -14
- data/lib/lhs/concerns/record/where.rb +0 -1
- data/lib/lhs/data.rb +3 -4
- data/lib/lhs/endpoint.rb +1 -2
- data/lib/lhs/errors.rb +7 -13
- data/lib/lhs/item.rb +5 -9
- data/lib/lhs/record.rb +12 -8
- data/lib/lhs/version.rb +1 -1
- data/spec/collection/delegate_spec.rb +0 -2
- data/spec/collection/enumerable_spec.rb +2 -4
- data/spec/collection/meta_data_spec.rb +0 -1
- data/spec/collection/respond_to_spec.rb +0 -1
- data/spec/collection/without_object_items_spec.rb +0 -1
- data/spec/data/collection_spec.rb +4 -7
- data/spec/data/item_spec.rb +3 -5
- data/spec/data/merge_spec.rb +7 -9
- data/spec/data/raw_spec.rb +3 -5
- data/spec/data/respond_to_spec.rb +3 -5
- data/spec/data/root_spec.rb +3 -5
- data/spec/data/select_spec.rb +2 -4
- data/spec/data/to_json_spec.rb +5 -7
- data/spec/dummy/bin/rails +1 -1
- data/spec/dummy/config.ru +1 -1
- data/spec/dummy/config/initializers/cookies_serializer.rb +1 -1
- data/spec/endpoint/for_url_spec.rb +3 -5
- data/spec/item/delegate_spec.rb +0 -2
- data/spec/item/destroy_spec.rb +3 -5
- data/spec/item/errors_spec.rb +20 -23
- data/spec/item/getter_spec.rb +1 -3
- data/spec/item/internal_data_structure_spec.rb +2 -3
- data/spec/item/respond_to_spec.rb +1 -1
- data/spec/item/save_spec.rb +7 -10
- data/spec/item/setter_spec.rb +2 -4
- data/spec/item/update_spec.rb +4 -7
- data/spec/item/validation_spec.rb +7 -9
- data/spec/proxy/load_spec.rb +0 -2
- data/spec/record/all_spec.rb +10 -12
- data/spec/record/build_spec.rb +0 -2
- data/spec/record/create_spec.rb +8 -10
- data/spec/record/creation_failed_spec.rb +4 -6
- data/spec/record/definitions_spec.rb +1 -3
- data/spec/record/endpoint_misconfiguration_spec.rb +2 -4
- data/spec/record/endpoint_options_spec.rb +0 -2
- data/spec/record/endpoints_spec.rb +1 -6
- data/spec/record/find_each_spec.rb +2 -4
- data/spec/record/find_in_batches_spec.rb +4 -6
- data/spec/record/find_spec.rb +10 -13
- data/spec/record/first_spec.rb +0 -3
- data/spec/record/includes_spec.rb +15 -20
- data/spec/record/mapping_spec.rb +13 -15
- data/spec/record/model_name_spec.rb +0 -2
- data/spec/record/new_spec.rb +4 -2
- data/spec/record/request_spec.rb +1 -3
- data/spec/record/where_spec.rb +1 -3
- data/spec/spec_helper.rb +1 -1
- data/spec/support/cleanup_configuration.rb +0 -2
- data/spec/support/cleanup_endpoints.rb +0 -1
- data/spec/support/cleanup_records.rb +0 -2
- metadata +26 -4
- data/cider-ci/contexts/rspec.yml +0 -16
- data/cider-ci/jobs/tests.yml +0 -27
@@ -6,7 +6,6 @@ class LHS::Record
|
|
6
6
|
extend ActiveSupport::Concern
|
7
7
|
|
8
8
|
module ClassMethods
|
9
|
-
|
10
9
|
# Should be an edge case but sometimes all objects from a certain resource
|
11
10
|
# are required. In this case we load the first page with the default max limit,
|
12
11
|
# compute the amount of left over requests, do all the the left over requests
|
@@ -22,7 +21,7 @@ class LHS::Record
|
|
22
21
|
if limit > 0
|
23
22
|
requests = total_left / limit
|
24
23
|
requests.times do |i|
|
25
|
-
offset = limit * (i+1) + 1
|
24
|
+
offset = limit * (i + 1) + 1
|
26
25
|
all.concat request(params: params.merge(limit: limit, offset: offset))._raw[:items]
|
27
26
|
end
|
28
27
|
end
|
@@ -6,13 +6,12 @@ class LHS::Record
|
|
6
6
|
extend ActiveSupport::Concern
|
7
7
|
|
8
8
|
module ClassMethods
|
9
|
-
|
10
9
|
# Process single entries fetched in batches
|
11
10
|
def find_each(options = {})
|
12
11
|
find_in_batches(options) do |data|
|
13
12
|
data.each do |record|
|
14
13
|
item = LHS::Item.new(LHS::Data.new(record, data, self))
|
15
|
-
yield
|
14
|
+
yield new(LHS::Data.new(item, data, self))
|
16
15
|
end
|
17
16
|
end
|
18
17
|
end
|
@@ -27,7 +26,7 @@ class LHS::Record
|
|
27
26
|
data = request(params: params.merge(limit: batch_size, offset: start))
|
28
27
|
batch_size = data._raw[:limit]
|
29
28
|
left = data._raw[:total].to_i - data._raw[:offset].to_i - data._raw[:limit].to_i
|
30
|
-
yield
|
29
|
+
yield new(data)
|
31
30
|
break if left <= 0
|
32
31
|
start += batch_size
|
33
32
|
end
|
@@ -6,20 +6,19 @@ class LHS::Record
|
|
6
6
|
extend ActiveSupport::Concern
|
7
7
|
|
8
8
|
module ClassMethods
|
9
|
-
|
10
9
|
def create(data = {})
|
11
10
|
create!(data)
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
11
|
+
rescue LHC::Error => e
|
12
|
+
json = JSON.parse(data.to_json)
|
13
|
+
data = LHS::Data.new(json, nil, self, e.response.request)
|
14
|
+
item = LHS::Item.new(data)
|
15
|
+
item.errors = LHS::Errors.new(e.response)
|
16
|
+
data._record_class.new(LHS::Data.new(item, data))
|
18
17
|
end
|
19
18
|
|
20
19
|
def create!(data = {})
|
21
20
|
url = compute_url!(data)
|
22
|
-
data = request(url: url, method: :post, body: data.to_json, headers: {'Content-Type' => 'application/json'})
|
21
|
+
data = request(url: url, method: :post, body: data.to_json, headers: { 'Content-Type' => 'application/json' })
|
23
22
|
data._record_class.new(data)
|
24
23
|
end
|
25
24
|
end
|
@@ -12,7 +12,6 @@ class LHS::Record
|
|
12
12
|
mattr_accessor :all
|
13
13
|
|
14
14
|
module ClassMethods
|
15
|
-
|
16
15
|
def endpoints
|
17
16
|
@endpoints ||= []
|
18
17
|
end
|
@@ -32,7 +31,7 @@ class LHS::Record
|
|
32
31
|
|
33
32
|
def for_url(url)
|
34
33
|
return unless url
|
35
|
-
|
34
|
+
_template, record = LHS::Record::Endpoints.all.detect do |template, _record_class|
|
36
35
|
LHC::Endpoint.match?(url, template)
|
37
36
|
end
|
38
37
|
record
|
@@ -75,7 +74,7 @@ class LHS::Record
|
|
75
74
|
|
76
75
|
# Sort endpoints by number of placeholders, heighest first
|
77
76
|
def sorted_endpoints
|
78
|
-
endpoints.sort{|a, b| b.placeholders.count <=> a.placeholders.count }
|
77
|
+
endpoints.sort { |a, b| b.placeholders.count <=> a.placeholders.count }
|
79
78
|
end
|
80
79
|
|
81
80
|
# Finds the base endpoint.
|
@@ -6,14 +6,14 @@ class LHS::Record
|
|
6
6
|
extend ActiveSupport::Concern
|
7
7
|
|
8
8
|
module ClassMethods
|
9
|
-
|
10
9
|
# Find a single uniqe record
|
11
10
|
def find(args)
|
12
|
-
data =
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
11
|
+
data =
|
12
|
+
if args.is_a? Hash
|
13
|
+
find_with_parameters(args)
|
14
|
+
else
|
15
|
+
find_by_id(args)
|
16
|
+
end
|
17
17
|
data._record_class.new(data)
|
18
18
|
end
|
19
19
|
|
@@ -6,12 +6,11 @@ class LHS::Record
|
|
6
6
|
extend ActiveSupport::Concern
|
7
7
|
|
8
8
|
module ClassMethods
|
9
|
-
|
10
9
|
# Fetch some record by parameters
|
11
10
|
def find_by(params = {})
|
12
11
|
_find_by(params)
|
13
|
-
|
14
|
-
|
12
|
+
rescue LHC::NotFound
|
13
|
+
nil
|
15
14
|
end
|
16
15
|
|
17
16
|
# Raise if no record was found
|
@@ -24,11 +23,12 @@ class LHS::Record
|
|
24
23
|
def _find_by(params)
|
25
24
|
params = params.dup.merge(limit: 1)
|
26
25
|
data = request(params: params)
|
27
|
-
data =
|
28
|
-
data.
|
29
|
-
|
30
|
-
|
31
|
-
|
26
|
+
data =
|
27
|
+
if data._proxy.is_a?(LHS::Collection)
|
28
|
+
data.first || fail(LHC::NotFound.new('No item was found.', data._request.response))
|
29
|
+
else
|
30
|
+
data
|
31
|
+
end
|
32
32
|
data._record_class.new(data)
|
33
33
|
end
|
34
34
|
end
|
@@ -6,7 +6,6 @@ class LHS::Record
|
|
6
6
|
extend ActiveSupport::Concern
|
7
7
|
|
8
8
|
module ClassMethods
|
9
|
-
|
10
9
|
def request(options)
|
11
10
|
if options.is_a? Array
|
12
11
|
multiple_requests(options)
|
@@ -29,7 +28,8 @@ class LHS::Record
|
|
29
28
|
def convert_option_to_endpoints(option)
|
30
29
|
new_options = option.dup
|
31
30
|
url = option[:url]
|
32
|
-
|
31
|
+
endpoint = LHS::Endpoint.for_url(url)
|
32
|
+
return unless endpoint
|
33
33
|
template = endpoint.url
|
34
34
|
new_options = new_options.merge(params: LHC::Endpoint.values_as_params(template, url))
|
35
35
|
new_options[:url] = template
|
@@ -50,23 +50,26 @@ class LHS::Record
|
|
50
50
|
|
51
51
|
def handle_includes(includes, data)
|
52
52
|
if includes.is_a? Hash
|
53
|
-
includes.each { |
|
53
|
+
includes.each { |included, sub_includes| handle_include(included, data, sub_includes) }
|
54
54
|
elsif includes.is_a? Array
|
55
|
-
includes.each { |
|
55
|
+
includes.each { |included| handle_includes(included, data) }
|
56
56
|
else
|
57
57
|
handle_include(includes, data)
|
58
58
|
end
|
59
59
|
end
|
60
60
|
|
61
|
-
def handle_include(
|
61
|
+
def handle_include(included, data, sub_includes = nil)
|
62
62
|
return unless data.present?
|
63
|
-
options =
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
63
|
+
options =
|
64
|
+
if data._proxy.is_a? LHS::Collection
|
65
|
+
options_for_multiple(data, included)
|
66
|
+
options_for_multiple(data, included)
|
67
|
+
else
|
68
|
+
url_option_for(data, included)
|
69
|
+
url_option_for(data, included)
|
70
|
+
end
|
68
71
|
addition = load_include(options, data, sub_includes)
|
69
|
-
extend_raw_data(data, addition,
|
72
|
+
extend_raw_data(data, addition, included)
|
70
73
|
end
|
71
74
|
|
72
75
|
# Load additional resources that are requested with include
|
@@ -89,9 +92,9 @@ class LHS::Record
|
|
89
92
|
end
|
90
93
|
|
91
94
|
def multiple_requests(options)
|
92
|
-
options = options.map { |
|
95
|
+
options = options.map { |option| process_options(option) }
|
93
96
|
responses = LHC.request(options)
|
94
|
-
data = responses.map{ |response| LHS::Data.new(response.body, nil, self, response.request) }
|
97
|
+
data = responses.map { |response| LHS::Data.new(response.body, nil, self, response.request) }
|
95
98
|
data = LHS::Data.new(data, nil, self)
|
96
99
|
handle_includes(including, data) if including
|
97
100
|
data
|
@@ -120,7 +123,8 @@ class LHS::Record
|
|
120
123
|
records = []
|
121
124
|
if options.is_a?(Array)
|
122
125
|
options.each do |option|
|
123
|
-
|
126
|
+
record = LHS::Record.for_url(option[:url])
|
127
|
+
next unless record
|
124
128
|
records.push(record)
|
125
129
|
end
|
126
130
|
fail 'Found more than one record that could be used to do the request' if records.uniq.count > 1
|
data/lib/lhs/data.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
require File.join(__dir__, 'proxy.rb')
|
2
|
-
Dir[File.dirname(__FILE__) + '/concerns/data/*.rb'].each {|file| require file }
|
2
|
+
Dir[File.dirname(__FILE__) + '/concerns/data/*.rb'].each { |file| require file }
|
3
3
|
|
4
4
|
# Data provides functionalities to accesses information
|
5
5
|
class LHS::Data
|
@@ -51,7 +51,7 @@ class LHS::Data
|
|
51
51
|
|
52
52
|
def respond_to_missing?(name, include_all = false)
|
53
53
|
(root_item? && _root._record_class.instance_methods.include?(name)) ||
|
54
|
-
|
54
|
+
_proxy.respond_to?(name, include_all)
|
55
55
|
end
|
56
56
|
|
57
57
|
private
|
@@ -61,14 +61,13 @@ class LHS::Data
|
|
61
61
|
end
|
62
62
|
|
63
63
|
def root_item
|
64
|
-
return if
|
64
|
+
return if _proxy.class != LHS::Item
|
65
65
|
root = root_item = self
|
66
66
|
loop do
|
67
67
|
root = root._parent
|
68
68
|
root_item = root if root && root._proxy.is_a?(LHS::Item)
|
69
69
|
if !(root && root._parent)
|
70
70
|
break
|
71
|
-
else
|
72
71
|
end
|
73
72
|
end
|
74
73
|
root_item
|
data/lib/lhs/endpoint.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# An endpoint is used as source to fetch objects
|
2
2
|
class LHS::Endpoint
|
3
|
-
|
3
|
+
|
4
4
|
def self.for_url(url)
|
5
5
|
template, record = LHS::Record::Endpoints.all.detect do |template, _record_class|
|
6
6
|
LHC::Endpoint.match?(url, template)
|
@@ -8,4 +8,3 @@ class LHS::Endpoint
|
|
8
8
|
record.endpoints.detect { |endpoint| endpoint.url == template } if record
|
9
9
|
end
|
10
10
|
end
|
11
|
-
|
data/lib/lhs/errors.rb
CHANGED
@@ -8,14 +8,14 @@ class LHS::Errors
|
|
8
8
|
@messages = messages_from_response(response)
|
9
9
|
@message = message_from_response(response)
|
10
10
|
@raw = response.body
|
11
|
-
|
11
|
+
rescue JSON::ParserError # rubocop:disable Lint/HandleExceptions
|
12
12
|
end
|
13
13
|
|
14
14
|
def include?(attribute)
|
15
15
|
messages[attribute].present?
|
16
16
|
end
|
17
|
-
alias
|
18
|
-
alias
|
17
|
+
alias has_key? include?
|
18
|
+
alias key? include?
|
19
19
|
|
20
20
|
def get(key)
|
21
21
|
messages[key]
|
@@ -25,9 +25,7 @@ class LHS::Errors
|
|
25
25
|
messages[key] = value
|
26
26
|
end
|
27
27
|
|
28
|
-
|
29
|
-
messages.delete(key)
|
30
|
-
end
|
28
|
+
delegate :delete, to: :messages
|
31
29
|
|
32
30
|
def [](attribute)
|
33
31
|
get(attribute.to_sym) || set(attribute.to_sym, [])
|
@@ -47,20 +45,16 @@ class LHS::Errors
|
|
47
45
|
values.flatten.size
|
48
46
|
end
|
49
47
|
|
50
|
-
|
51
|
-
messages.values
|
52
|
-
end
|
48
|
+
delegate :values, to: :messages
|
53
49
|
|
54
|
-
|
55
|
-
messages.keys
|
56
|
-
end
|
50
|
+
delegate :keys, to: :messages
|
57
51
|
|
58
52
|
def count
|
59
53
|
to_a.size
|
60
54
|
end
|
61
55
|
|
62
56
|
def empty?
|
63
|
-
all? { |
|
57
|
+
all? { |_k, v| v && v.empty? && !v.is_a?(String) }
|
64
58
|
end
|
65
59
|
|
66
60
|
private
|
data/lib/lhs/item.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
require File.join(__dir__, 'proxy.rb')
|
2
|
-
Dir[File.dirname(__FILE__) + '/concerns/item/*.rb'].each {|file| require file }
|
2
|
+
Dir[File.dirname(__FILE__) + '/concerns/item/*.rb'].each { |file| require file }
|
3
3
|
|
4
4
|
# An item is a concrete record.
|
5
5
|
# It can be part of another proxy like collection.
|
@@ -14,13 +14,11 @@ class LHS::Item < LHS::Proxy
|
|
14
14
|
# prevent clashing with attributes of underlying data
|
15
15
|
attr_accessor :errors
|
16
16
|
|
17
|
-
|
18
|
-
_data._raw
|
19
|
-
end
|
17
|
+
delegate :_raw, to: :_data
|
20
18
|
|
21
19
|
protected
|
22
20
|
|
23
|
-
def method_missing(name, *args, &
|
21
|
+
def method_missing(name, *args, &_block)
|
24
22
|
return set(name, args.try(&:first)) if name.to_s[/=$/]
|
25
23
|
name = args.first if name == :[]
|
26
24
|
value = _data._raw[name.to_s]
|
@@ -36,7 +34,7 @@ class LHS::Item < LHS::Proxy
|
|
36
34
|
end
|
37
35
|
end
|
38
36
|
|
39
|
-
def respond_to_missing?(name,
|
37
|
+
def respond_to_missing?(name, _include_all = false)
|
40
38
|
# We accept every message that does not belong to set of keywords
|
41
39
|
BLACKLISTED_KEYWORDS.exclude?(name.to_s)
|
42
40
|
end
|
@@ -49,7 +47,7 @@ class LHS::Item < LHS::Proxy
|
|
49
47
|
def convert(value)
|
50
48
|
return value unless value.is_a?(String)
|
51
49
|
if date_time?(value)
|
52
|
-
|
50
|
+
Time.zone.parse(value)
|
53
51
|
elsif date?(value)
|
54
52
|
Date.parse(value)
|
55
53
|
else
|
@@ -72,8 +70,6 @@ class LHS::Item < LHS::Proxy
|
|
72
70
|
_data._raw[key.to_sym] = value
|
73
71
|
end
|
74
72
|
|
75
|
-
private
|
76
|
-
|
77
73
|
def date?(value)
|
78
74
|
value[date_time_regex, :date].presence
|
79
75
|
end
|
data/lib/lhs/record.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
Dir[File.dirname(__FILE__) + '/concerns/record/*.rb'].each {|file| require file }
|
1
|
+
Dir[File.dirname(__FILE__) + '/concerns/record/*.rb'].each { |file| require file }
|
2
2
|
|
3
3
|
class LHS::Record
|
4
4
|
include All
|
@@ -18,15 +18,19 @@ class LHS::Record
|
|
18
18
|
data = LHS::Data.new({}, nil, self.class) unless data
|
19
19
|
data = LHS::Data.new(data, nil, self.class) unless data.is_a?(LHS::Data)
|
20
20
|
define_singleton_method(:_data) { data }
|
21
|
-
|
22
|
-
data.
|
23
|
-
|
24
|
-
|
25
|
-
|
21
|
+
instance_data =
|
22
|
+
if data._proxy.is_a?(LHS::Item) && data._raw.is_a?(Hash)
|
23
|
+
data._raw
|
24
|
+
elsif data._proxy.is_a?(LHS::Collection) && data._raw.is_a?(Hash)
|
25
|
+
data._raw.fetch(:items, [])
|
26
|
+
else
|
27
|
+
data._raw
|
28
|
+
end
|
29
|
+
instance_variable_set('@data', instance_data)
|
26
30
|
end
|
27
31
|
|
28
32
|
def self.build(data = nil)
|
29
|
-
|
33
|
+
new(data)
|
30
34
|
end
|
31
35
|
|
32
36
|
protected
|
@@ -37,6 +41,6 @@ class LHS::Record
|
|
37
41
|
|
38
42
|
def respond_to_missing?(name, include_all = false)
|
39
43
|
(_data.root_item? && _data._root._record_class.instance_methods.include?(name)) ||
|
40
|
-
|
44
|
+
_data._proxy.respond_to?(name, include_all)
|
41
45
|
end
|
42
46
|
end
|