arstotzka 1.4.0 → 1.4.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/arstotzka.gemspec +1 -1
- data/config/yardstick.yml +8 -0
- data/lib/arstotzka/class_methods.rb +59 -0
- data/lib/arstotzka/config.rb +38 -2
- data/lib/arstotzka/fetcher.rb +14 -10
- data/lib/arstotzka/key_changer.rb +54 -0
- data/lib/arstotzka/key_reader.rb +5 -16
- data/lib/arstotzka/options.rb +13 -34
- data/lib/arstotzka/post_processor.rb +105 -0
- data/lib/arstotzka/reader.rb +2 -3
- data/lib/arstotzka/version.rb +1 -1
- data/lib/arstotzka.rb +2 -0
- data/spec/integration/yard/arstotzka/config_spec.rb +35 -2
- data/spec/integration/yard/arstotzka/key_changer_spec.rb +15 -0
- data/spec/integration/yard/arstotzka/post_processor_spec.rb +45 -0
- data/spec/lib/arstotzka/key_changer_spec.rb +36 -0
- data/spec/lib/arstotzka/post_processor_spec.rb +55 -0
- data/spec/support/models/company.rb +9 -0
- data/spec/support/models/employe.rb +22 -0
- data/spec/support/models/office.rb +12 -0
- metadata +20 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2f85777523a289161b592aa87351fa89b9e255ff4af446b365699aa23f208f72
|
4
|
+
data.tar.gz: c9a7e10955ec91d3a1c356c1a0da69082e36f1c5b89abb76964cbdd92ba7a28f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ae4b737d7c698ec03482a30b7ca5a715c413aa08bccd4863f2cdf081dc984f242171028deaa5256156b6b6b62555a23239327ba808523d5df1f0f756d57c5c5d
|
7
|
+
data.tar.gz: bd2e82f32e3cbdbc09178d8ae9224a9432b90c2d0ec2edef2148c99279a8617dc688f969d2b47d0510f2ff42c4b605cd5ab2c5e562fa54feee8cbc9c6a24a008
|
data/README.md
CHANGED
data/arstotzka.gemspec
CHANGED
@@ -20,7 +20,7 @@ Gem::Specification.new do |gem|
|
|
20
20
|
gem.require_paths = ['lib']
|
21
21
|
|
22
22
|
gem.add_runtime_dependency 'activesupport', '~> 5.x'
|
23
|
-
gem.add_runtime_dependency 'sinclair', '>= 1.4.
|
23
|
+
gem.add_runtime_dependency 'sinclair', '>= 1.4.1'
|
24
24
|
|
25
25
|
gem.add_development_dependency 'bundler', '~> 1.16.1'
|
26
26
|
gem.add_development_dependency 'pry-nav', '~> 0.2.4'
|
data/config/yardstick.yml
CHANGED
@@ -27,6 +27,8 @@ rules:
|
|
27
27
|
- Arstotzka::Fetcher::Cache#block
|
28
28
|
- Arstotzka::Fetcher::Cache#options
|
29
29
|
- Arstotzka::FetcherBuilder#options
|
30
|
+
- Arstotzka::KeyChanger#base_key
|
31
|
+
- Arstotzka::KeyChanger#options
|
30
32
|
- Arstotzka::KeyReader#base_key
|
31
33
|
- Arstotzka::KeyReader#hash
|
32
34
|
- Arstotzka::KeyReader#options
|
@@ -35,6 +37,7 @@ rules:
|
|
35
37
|
- Arstotzka::MethodBuilder#options
|
36
38
|
- Arstotzka::Reader#keys
|
37
39
|
- Arstotzka::Reader#options
|
40
|
+
- Arstotzka::PostProcessor#options
|
38
41
|
- Arstotzka::Wrapper#options
|
39
42
|
Summary::Presence:
|
40
43
|
enabled: true
|
@@ -48,6 +51,9 @@ rules:
|
|
48
51
|
- Arstotzka::Fetcher::Cache#options
|
49
52
|
- Arstotzka::Fetcher::Cache#initialize
|
50
53
|
- Arstotzka::FetcherBuilder#options
|
54
|
+
- Arstotzka::KeyChanger#initialize
|
55
|
+
- Arstotzka::KeyChanger#base_key
|
56
|
+
- Arstotzka::KeyChanger#options
|
51
57
|
- Arstotzka::KeyReader#base_key
|
52
58
|
- Arstotzka::KeyReader#hash
|
53
59
|
- Arstotzka::KeyReader#options
|
@@ -56,6 +62,8 @@ rules:
|
|
56
62
|
- Arstotzka::MethodBuilder#options
|
57
63
|
- Arstotzka::Reader#keys
|
58
64
|
- Arstotzka::Reader#options
|
65
|
+
- Arstotzka::PostProcessor#initialize
|
66
|
+
- Arstotzka::PostProcessor#options
|
59
67
|
- Arstotzka::Wrapper#options
|
60
68
|
Summary::Length:
|
61
69
|
enabled: true
|
@@ -69,6 +69,64 @@ module Arstotzka
|
|
69
69
|
#
|
70
70
|
# Expose a field from the json/hash as a method
|
71
71
|
#
|
72
|
+
# @param attr_names [Array<String,Symbol>] attributes being exposed
|
73
|
+
# @param options_hash [Hash] exposing options
|
74
|
+
#
|
75
|
+
# @option options_hash after [String,Symbol] instance method to be called on the
|
76
|
+
# returning value returned by {Crawler} before being returned by {Fetcher}.
|
77
|
+
# The result of the method call will be the actual value
|
78
|
+
# (used by {PostProcessor})
|
79
|
+
#
|
80
|
+
# @option options_hash after_each [String,Symbol] instance method to be
|
81
|
+
# called on each element found when crawling the hash.
|
82
|
+
# If an array should be returned, after_each will be called on each individual
|
83
|
+
# elements (even nil elements) while +after+ is only called on the
|
84
|
+
# collection. (used by {Wrapper})
|
85
|
+
#
|
86
|
+
# @option options_hash cached [Boolean,Symbol] Flag to ensure
|
87
|
+
# there value is cached after first fetching.
|
88
|
+
# (used by {Fetcher::Cache})
|
89
|
+
# - +false+/+nil+ : no cache, fetcher will always look in the hash
|
90
|
+
# for the value
|
91
|
+
# - +true+ : Simple cache, nil values are always considered
|
92
|
+
# not cached
|
93
|
+
# - +:full+ : cache even nil values
|
94
|
+
#
|
95
|
+
# @option options_hash case [Symbol] case type of the keys in the hash
|
96
|
+
# (used by {KeyChanger})
|
97
|
+
# - +:snake+ : snake_cased keys
|
98
|
+
# - +:lower_camel+ : lowerCamelCased keys
|
99
|
+
# - +:upper_camel+ : UperCamelCased keys
|
100
|
+
#
|
101
|
+
# @option options_hash compact [Boolean] flag signallying if nil values should be
|
102
|
+
# removed of array (applying +Array#compact+) (used by {Crawler})
|
103
|
+
#
|
104
|
+
# @option options_hash default [Object] default value to be returned when failing
|
105
|
+
# to fetch a value (used by {Crawler})
|
106
|
+
#
|
107
|
+
# @option options_hash flatten [Boolean] flag signallying if multi levels
|
108
|
+
# arrays should be flattened to one level array (applying +Array#flatten+)
|
109
|
+
# (used by {PostProcessor})
|
110
|
+
#
|
111
|
+
# @option options_hash full_path [String] full path of keys to be used in when
|
112
|
+
# not wanting to have the attribute defined as key. (eg. +'person.name'+)
|
113
|
+
# (used by {Crawler})
|
114
|
+
#
|
115
|
+
# @option options_hash klass [Class] class to wrap the value (used by {Wrapper})
|
116
|
+
#
|
117
|
+
# @option options_hash json [String,Symbol] name of the method or variable
|
118
|
+
# used to fetch the hash to be crawled (eg. +:hash+, +:@hash+, +"@@config"+)
|
119
|
+
# (used by {HashReader})
|
120
|
+
#
|
121
|
+
# @option options_hash path [String,Symbol] base path to be crawled.
|
122
|
+
# (used by {Crawler})
|
123
|
+
#
|
124
|
+
# @option options_hash type [String,Symbol] type to cast the value. The
|
125
|
+
# possible type_cast is defined by {TypeCast} (used by {Wrapper})
|
126
|
+
# - +integer+
|
127
|
+
# - +string+
|
128
|
+
# - +float+
|
129
|
+
#
|
72
130
|
# @example
|
73
131
|
# class MyModel
|
74
132
|
# include Arstotzka
|
@@ -95,6 +153,7 @@ module Arstotzka
|
|
95
153
|
#
|
96
154
|
# @return [Array<Sinclair::MethodDefinition>]
|
97
155
|
#
|
156
|
+
# @see Config
|
98
157
|
# @see MethodBuilder Arstotzka::MethodBuilder
|
99
158
|
# @see
|
100
159
|
# https://www.rubydoc.info/gems/activesupport/5.2.2/ActiveSupport/Concern
|
data/lib/arstotzka/config.rb
CHANGED
@@ -5,12 +5,48 @@ module Arstotzka
|
|
5
5
|
#
|
6
6
|
# Arstotzka configuration
|
7
7
|
#
|
8
|
-
# Configuration is
|
8
|
+
# Configuration of Arstotzka is done through
|
9
|
+
# +Arstotzka.configure+ which configures using
|
10
|
+
# {Arstotzka::Config} by +Sinclar::Configurable+
|
9
11
|
#
|
10
|
-
# @see https://www.rubydoc.info/gems/sinclair/1.4.
|
12
|
+
# @see https://www.rubydoc.info/gems/sinclair/1.4.1/Sinclair/Config
|
13
|
+
# Sinclair::Config
|
14
|
+
#
|
15
|
+
# @example Redefining json method and case type
|
16
|
+
# class Office
|
17
|
+
# include Arstotzka
|
18
|
+
#
|
19
|
+
# expose :employes, full_path: 'employes.first_name',
|
20
|
+
# compact: true
|
21
|
+
#
|
22
|
+
# def initialize(hash)
|
23
|
+
# @hash = hash
|
24
|
+
# end
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
# hash = {
|
28
|
+
# employes: [{
|
29
|
+
# first_name: 'Rob'
|
30
|
+
# }, {
|
31
|
+
# first_name: 'Klara'
|
32
|
+
# }]
|
33
|
+
# }
|
34
|
+
#
|
35
|
+
# office = Office.new(hash)
|
36
|
+
#
|
37
|
+
# office.employes # raises NoMethodError as json is not a method
|
38
|
+
#
|
39
|
+
# Arstotzka.configure { json :@hash }
|
40
|
+
#
|
41
|
+
# office.employes # returns []
|
42
|
+
#
|
43
|
+
# Arstotzka.configure { |c| c.case :snake }
|
44
|
+
#
|
45
|
+
# office.employes # returns %w[Rob Klara]
|
11
46
|
#
|
12
47
|
# @example (see #options)
|
13
48
|
class Config < Sinclair::Config
|
49
|
+
# Default values for {ClassMethods#expose}
|
14
50
|
DEFAULT_CONFIGS = {
|
15
51
|
after: false,
|
16
52
|
after_each: nil,
|
data/lib/arstotzka/fetcher.rb
CHANGED
@@ -96,8 +96,7 @@ module Arstotzka
|
|
96
96
|
# @return [TrueClass,FalseClass]
|
97
97
|
def ==(other)
|
98
98
|
return false unless other.class == self.class
|
99
|
-
options == other.options
|
100
|
-
instance == other.instance
|
99
|
+
options == other.options
|
101
100
|
end
|
102
101
|
|
103
102
|
protected
|
@@ -107,7 +106,6 @@ module Arstotzka
|
|
107
106
|
private
|
108
107
|
|
109
108
|
# @private
|
110
|
-
delegate :instance, :after, :flatten, to: :options
|
111
109
|
delegate :wrap, to: :wrapper
|
112
110
|
delegate :hash, to: :hash_reader
|
113
111
|
|
@@ -120,9 +118,16 @@ module Arstotzka
|
|
120
118
|
#
|
121
119
|
# @return [Object]
|
122
120
|
def fetch_value
|
123
|
-
|
124
|
-
|
125
|
-
|
121
|
+
post_processor.process crawler.value(hash)
|
122
|
+
end
|
123
|
+
|
124
|
+
# @private
|
125
|
+
#
|
126
|
+
# post processor for wrapping and filtering collection before return
|
127
|
+
#
|
128
|
+
# @return [PostProcessor]
|
129
|
+
def post_processor
|
130
|
+
@post_processor ||= PostProcessor.new(options)
|
126
131
|
end
|
127
132
|
|
128
133
|
# @private
|
@@ -134,10 +139,9 @@ module Arstotzka
|
|
134
139
|
#
|
135
140
|
# @return [Arstotzka::Crawler] the crawler object
|
136
141
|
def crawler
|
137
|
-
@crawler ||=
|
138
|
-
|
139
|
-
|
140
|
-
end
|
142
|
+
@crawler ||= Crawler.new(options) do |value|
|
143
|
+
wrap(value)
|
144
|
+
end
|
141
145
|
end
|
142
146
|
|
143
147
|
# @private
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Arstotzka
|
4
|
+
# @api private
|
5
|
+
#
|
6
|
+
# Class responsible for changing a key
|
7
|
+
class KeyChanger
|
8
|
+
include Base
|
9
|
+
|
10
|
+
# @param [String] base_key The key to be checked
|
11
|
+
# (before case change)
|
12
|
+
#
|
13
|
+
# @overload initialize(base_key, options_hash)
|
14
|
+
# @param options_hash [Hash] options passed on expose
|
15
|
+
# @option options_hash case [Symbol] case type for key
|
16
|
+
# transformation
|
17
|
+
# - +:snake+ : snake_cased keys
|
18
|
+
# - +:lower_camel+ : lowerCamelCased keys
|
19
|
+
# - +:upper_camel+ : UperCamelCased keys
|
20
|
+
# @overload initialize(base_key, options)
|
21
|
+
# @param options [Option] options passed on expose
|
22
|
+
#
|
23
|
+
# @example
|
24
|
+
# changer = Arstotzka::KeyChanger.new('the_key', case: :upper_camel)
|
25
|
+
# changer.key # returns 'TheKey'
|
26
|
+
def initialize(base_key, options_hash = {})
|
27
|
+
self.options = options_hash
|
28
|
+
@base_key = base_key
|
29
|
+
end
|
30
|
+
|
31
|
+
# Transforms the key to have the correct case
|
32
|
+
#
|
33
|
+
# the possible cases (instance attribute) are
|
34
|
+
# - lower_camel: for cammel case with first letter lowercase
|
35
|
+
# - upper_camel: for cammel case with first letter uppercase
|
36
|
+
# - snake: for snake case
|
37
|
+
#
|
38
|
+
# @return [String] the string transformed
|
39
|
+
def key
|
40
|
+
@key ||= case options.case
|
41
|
+
when :lower_camel
|
42
|
+
base_key.camelize(:lower)
|
43
|
+
when :upper_camel
|
44
|
+
base_key.camelize(:upper)
|
45
|
+
when :snake
|
46
|
+
base_key.underscore
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
attr_reader :base_key, :options
|
53
|
+
end
|
54
|
+
end
|
data/lib/arstotzka/key_reader.rb
CHANGED
@@ -48,6 +48,7 @@ module Arstotzka
|
|
48
48
|
private
|
49
49
|
|
50
50
|
attr_reader :hash, :base_key, :options
|
51
|
+
delegate :key, to: :key_changer
|
51
52
|
|
52
53
|
# @private
|
53
54
|
#
|
@@ -66,23 +67,11 @@ module Arstotzka
|
|
66
67
|
|
67
68
|
# @private
|
68
69
|
#
|
69
|
-
#
|
70
|
+
# Returns key changer for getting the correct key
|
70
71
|
#
|
71
|
-
#
|
72
|
-
|
73
|
-
|
74
|
-
# - snake: for snake case
|
75
|
-
#
|
76
|
-
# @return [String] the string transformed
|
77
|
-
def key
|
78
|
-
@key ||= case options.case
|
79
|
-
when :lower_camel
|
80
|
-
base_key.camelize(:lower)
|
81
|
-
when :upper_camel
|
82
|
-
base_key.camelize(:upper)
|
83
|
-
when :snake
|
84
|
-
base_key.underscore
|
85
|
-
end
|
72
|
+
# @return [KeyChanger]
|
73
|
+
def key_changer
|
74
|
+
@key_changer ||= KeyChanger.new(base_key, options)
|
86
75
|
end
|
87
76
|
end
|
88
77
|
end
|
data/lib/arstotzka/options.rb
CHANGED
@@ -246,47 +246,26 @@ module Arstotzka
|
|
246
246
|
class Options < ::OpenStruct
|
247
247
|
# Creates a new instance of Options
|
248
248
|
#
|
249
|
-
# @param
|
249
|
+
# @param options_hash [Hash] options hash
|
250
250
|
# Options hash are initialized when using {ClassMethods#expose}
|
251
251
|
#
|
252
|
-
# @option
|
253
|
-
# called once the value is fetched for mapping the value
|
252
|
+
# @option (see ClassMethods#expose)
|
254
253
|
#
|
255
|
-
# @option
|
256
|
-
#
|
257
|
-
#
|
258
|
-
#
|
259
|
-
#
|
260
|
-
# the keys be defined
|
261
|
-
# - lower_camel: keys in the hash are lowerCamelCase
|
262
|
-
# - upper_camel: keys in the hash are UpperCamelCase
|
263
|
-
# - snake: keys in the hash are snake_case
|
264
|
-
# @option options [Boolean] compact: {Crawler} flag to apply Array#compact thus
|
265
|
-
# removing nil results
|
266
|
-
# @option options [Boolean] default: {Crawler} option to return default value instead of nil
|
267
|
-
# @option options [Boolean] flatten: {Fetcher} flag to aplly Array#flatten thus
|
268
|
-
# avoing nested arrays
|
269
|
-
# @option options [String,Symbol] full_path: path of hash attributes to find exacttly where the
|
270
|
-
# value live (ignoring the attribute name)
|
271
|
-
# @option options [Class] klass: {Fetcher} option that, when passed, wraps the individual
|
272
|
-
# results in an instance of the given class
|
273
|
-
# @option options [String,Symbol] json: name of the method containing the hash to be crawled
|
274
|
-
# @option options [String,Symbol] path path of hash attributes to find the root
|
275
|
-
# where the attribute live (then fetching it using the attribute name)
|
276
|
-
# @option options [String,Symbol] type: {Fetcher} option declaring the type of the returned
|
277
|
-
# value (to use casting) (see {TypeCast})
|
278
|
-
# - integer
|
279
|
-
# - string
|
280
|
-
# - float
|
254
|
+
# @option options_hash instance [Object] instance whose method
|
255
|
+
# was called
|
256
|
+
#
|
257
|
+
# @option options_hash key [Symbol,String] name of method called.
|
258
|
+
# This will be used as instance variable when caching results
|
281
259
|
#
|
282
|
-
# @see Arstotzka::Options::DEFAULT_OPTIONS
|
283
260
|
# @return [Arstotzka::Options]
|
284
|
-
|
285
|
-
|
261
|
+
#
|
262
|
+
# @see Config
|
263
|
+
def initialize(options_hash)
|
264
|
+
klass = options_hash.delete(:class)
|
286
265
|
warn ":class has been deprecated, prefer 'expose klass: #{klass}'" if klass
|
287
|
-
|
266
|
+
options_hash[:klass] ||= klass
|
288
267
|
|
289
|
-
super(
|
268
|
+
super(options_hash)
|
290
269
|
end
|
291
270
|
|
292
271
|
# @private
|
@@ -0,0 +1,105 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Arstotzka
|
4
|
+
# @api private
|
5
|
+
#
|
6
|
+
# Class reponsible for processing the result of {Crawler#crawl}.
|
7
|
+
#
|
8
|
+
# PostProcessor proccess the whole collection and not
|
9
|
+
# individual results
|
10
|
+
#
|
11
|
+
# @example Simple Usage
|
12
|
+
# class Employe
|
13
|
+
# attr_reader :name, :age, :company
|
14
|
+
#
|
15
|
+
# def initialize(name:, age:, company:)
|
16
|
+
# @name = name
|
17
|
+
# @age = age
|
18
|
+
# @company = company
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# def adult?
|
22
|
+
# age >= 18
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# def ==(other)
|
26
|
+
# return unless other.class == self.class
|
27
|
+
# other.name == name &&
|
28
|
+
# other.age == age &&
|
29
|
+
# other.company == company
|
30
|
+
# end
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
# class Company
|
34
|
+
# def create_employes(people)
|
35
|
+
# people.map do |person|
|
36
|
+
# Employe.new(company: self, **person)
|
37
|
+
# end.select(&:adult?)
|
38
|
+
# end
|
39
|
+
# end
|
40
|
+
#
|
41
|
+
# company = Company.new
|
42
|
+
#
|
43
|
+
# options = {
|
44
|
+
# after: :create_employes,
|
45
|
+
# flatten: true,
|
46
|
+
# instance: company
|
47
|
+
# }
|
48
|
+
#
|
49
|
+
# processor = Arstotzka::PostProcessor.new(options)
|
50
|
+
#
|
51
|
+
# value = [
|
52
|
+
# [
|
53
|
+
# { name: 'Bob', age: 21 },
|
54
|
+
# { name: 'Rose', age: 19 }
|
55
|
+
# ], [
|
56
|
+
# { name: 'Klara', age: 18 },
|
57
|
+
# { name: 'Al', age: 15 }
|
58
|
+
# ]
|
59
|
+
# ]
|
60
|
+
#
|
61
|
+
# processor.process(value)
|
62
|
+
#
|
63
|
+
# # returns [
|
64
|
+
# # Employe.new(name: 'Bob', age: 21, company: company),
|
65
|
+
# # Employe.new(name: 'Rose', age: 19, company: company),
|
66
|
+
# # Employe.new(name: 'Klara', age: 18, company: company)
|
67
|
+
# # ]
|
68
|
+
class PostProcessor
|
69
|
+
include Base
|
70
|
+
|
71
|
+
# @overload initialize(options_hash)
|
72
|
+
# @param options_hash [Hash] options passed by
|
73
|
+
# {ClassMethods#expose}
|
74
|
+
# @option options_hash instance [Objct]
|
75
|
+
# @option options_hash after [String,Symbol] instance method to be called on the
|
76
|
+
# returning value returned by {Crawler} before being returned by {Fetcher}.
|
77
|
+
# @option options_hash flatten [Boolean] flag signallying if multi levels
|
78
|
+
# arrays should be flattened to one level array (applying +Array#flatten+)
|
79
|
+
# @overload initialize(options)
|
80
|
+
# @param options [Hash] options passed by {ClassMethods#expose}
|
81
|
+
def initialize(options_hash = {})
|
82
|
+
self.options = options_hash
|
83
|
+
end
|
84
|
+
|
85
|
+
# Apply transformation and filters on the result
|
86
|
+
#
|
87
|
+
# @param value [Object] value is returned from crawler
|
88
|
+
# being a single object, or an array.
|
89
|
+
#
|
90
|
+
# @return [Object]
|
91
|
+
def process(value)
|
92
|
+
value.flatten! if flatten && value.is_a?(Array)
|
93
|
+
|
94
|
+
return value unless after
|
95
|
+
|
96
|
+
instance.send(after, value)
|
97
|
+
end
|
98
|
+
|
99
|
+
private
|
100
|
+
|
101
|
+
attr_reader :options
|
102
|
+
|
103
|
+
delegate :instance, :after, :flatten, to: :options
|
104
|
+
end
|
105
|
+
end
|
data/lib/arstotzka/reader.rb
CHANGED
@@ -22,8 +22,6 @@ module Arstotzka
|
|
22
22
|
# @return [Arstotzka::Reader]
|
23
23
|
def initialize(options_hash = {})
|
24
24
|
self.options = options_hash
|
25
|
-
|
26
|
-
@keys = options.keys
|
27
25
|
end
|
28
26
|
|
29
27
|
# Reads the value of one key in the hash
|
@@ -81,6 +79,7 @@ module Arstotzka
|
|
81
79
|
private
|
82
80
|
|
83
81
|
# @private
|
84
|
-
attr_reader :
|
82
|
+
attr_reader :options
|
83
|
+
delegate :keys, to: :options
|
85
84
|
end
|
86
85
|
end
|
data/lib/arstotzka/version.rb
CHANGED
data/lib/arstotzka.rb
CHANGED
@@ -175,10 +175,12 @@ module Arstotzka
|
|
175
175
|
autoload :Exception, 'arstotzka/exception'
|
176
176
|
autoload :Fetcher, 'arstotzka/fetcher'
|
177
177
|
autoload :FetcherBuilder, 'arstotzka/fetcher_builder'
|
178
|
+
autoload :KeyChanger, 'arstotzka/key_changer'
|
178
179
|
autoload :KeyReader, 'arstotzka/key_reader'
|
179
180
|
autoload :HashReader, 'arstotzka/hash_reader'
|
180
181
|
autoload :MethodBuilder, 'arstotzka/method_builder'
|
181
182
|
autoload :Options, 'arstotzka/options'
|
183
|
+
autoload :PostProcessor, 'arstotzka/post_processor'
|
182
184
|
autoload :Reader, 'arstotzka/reader'
|
183
185
|
autoload :TypeCast, 'arstotzka/type_cast'
|
184
186
|
autoload :Wrapper, 'arstotzka/wrapper'
|
@@ -6,6 +6,41 @@ describe Arstotzka::Config do
|
|
6
6
|
describe 'yard' do
|
7
7
|
subject(:config) { Arstotzka.config }
|
8
8
|
|
9
|
+
after { Arstotzka.reset_config }
|
10
|
+
|
11
|
+
describe 'Redefining json method and case type' do
|
12
|
+
let(:office) { Office.new(hash) }
|
13
|
+
|
14
|
+
let(:hash) do
|
15
|
+
{
|
16
|
+
employes: [{
|
17
|
+
first_name: 'Rob'
|
18
|
+
}, {
|
19
|
+
first_name: 'Klara'
|
20
|
+
}]
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
let(:error_message) do
|
25
|
+
"undefined method `json' for #{office}\nDid you mean? JSON"
|
26
|
+
end
|
27
|
+
|
28
|
+
it do
|
29
|
+
expect { office.employes }
|
30
|
+
.to raise_error(NoMethodError, error_message)
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'when json option is defined' do
|
34
|
+
before { Arstotzka.configure { json :@hash } }
|
35
|
+
|
36
|
+
it do
|
37
|
+
expect { Arstotzka.configure { |c| c.case :snake } }
|
38
|
+
.to change(office, :employes)
|
39
|
+
.from([]).to(%w[Rob Klara])
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
9
44
|
describe '#options' do
|
10
45
|
let(:overrides) { { klass: Person } }
|
11
46
|
|
@@ -30,8 +65,6 @@ describe Arstotzka::Config do
|
|
30
65
|
end
|
31
66
|
end
|
32
67
|
|
33
|
-
after { Arstotzka.reset_config }
|
34
|
-
|
35
68
|
it 'configure options to use snake case' do
|
36
69
|
expect(config.options(overrides).to_h).to eq(expected)
|
37
70
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Arstotzka::KeyChanger do
|
6
|
+
describe '#yard' do
|
7
|
+
subject(:key_changer) do
|
8
|
+
described_class.new('the_key', case: :upper_camel)
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'converts key case' do
|
12
|
+
expect(key_changer.key).to eq('TheKey')
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Arstotzka::PostProcessor do
|
6
|
+
describe 'yard' do
|
7
|
+
subject(:processor) { described_class.new(options) }
|
8
|
+
|
9
|
+
let(:company) { Company.new }
|
10
|
+
|
11
|
+
let(:options) do
|
12
|
+
{
|
13
|
+
after: :create_employes,
|
14
|
+
flatten: true,
|
15
|
+
instance: company
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
19
|
+
let(:value) do
|
20
|
+
[
|
21
|
+
[
|
22
|
+
{ name: 'Bob', age: 21 },
|
23
|
+
{ name: 'Rose', age: 19 }
|
24
|
+
], [
|
25
|
+
{ name: 'Klara', age: 18 },
|
26
|
+
{ name: 'Al', age: 15 }
|
27
|
+
]
|
28
|
+
]
|
29
|
+
end
|
30
|
+
|
31
|
+
let(:expected) do
|
32
|
+
[
|
33
|
+
Employe.new(name: 'Bob', age: 21, company: company),
|
34
|
+
Employe.new(name: 'Rose', age: 19, company: company),
|
35
|
+
Employe.new(name: 'Klara', age: 18, company: company)
|
36
|
+
]
|
37
|
+
end
|
38
|
+
|
39
|
+
describe 'Simple usage' do
|
40
|
+
it 'maps and filter' do
|
41
|
+
expect(processor.process(value)).to eq(expected)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Arstotzka::KeyChanger do
|
6
|
+
subject(:key_changer) { described_class.new(base_key, options) }
|
7
|
+
|
8
|
+
describe '#key' do
|
9
|
+
let(:base_key) { 'the_key' }
|
10
|
+
|
11
|
+
context 'when no options are given' do
|
12
|
+
let(:options) { {} }
|
13
|
+
|
14
|
+
it 'returns lower camelized key' do
|
15
|
+
expect(key_changer.key).to eq('theKey')
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context 'when setting upper camel' do
|
20
|
+
let(:options) { { case: :upper_camel } }
|
21
|
+
|
22
|
+
it 'returns upper camelized key' do
|
23
|
+
expect(key_changer.key).to eq('TheKey')
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'when setting snake' do
|
28
|
+
let(:options) { { case: :snake } }
|
29
|
+
let(:base_key) { 'TheKey' }
|
30
|
+
|
31
|
+
it 'returns lower snakecased key' do
|
32
|
+
expect(key_changer.key).to eq('the_key')
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Arstotzka::PostProcessor do
|
6
|
+
subject(:processor) { described_class.new(options) }
|
7
|
+
|
8
|
+
let(:options) { {} }
|
9
|
+
let(:value) { [%w[Bob Klara], %w[Rose Aria]] }
|
10
|
+
|
11
|
+
describe '#process' do
|
12
|
+
context 'when no options are given' do
|
13
|
+
it 'returns the value with no change' do
|
14
|
+
expect(processor.process(value)).to eq(value)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
context 'when passing flatten option' do
|
19
|
+
let(:options) { { flatten: true } }
|
20
|
+
|
21
|
+
it 'flattens the array' do
|
22
|
+
expect(processor.process(value))
|
23
|
+
.to eq(%w[Bob Klara Rose Aria])
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'when passing after' do
|
28
|
+
let(:company) { Company.new }
|
29
|
+
|
30
|
+
let(:options) do
|
31
|
+
{ after: :create_employes, instance: company }
|
32
|
+
end
|
33
|
+
|
34
|
+
let(:value) do
|
35
|
+
[
|
36
|
+
{ name: 'Bob', age: 21 },
|
37
|
+
{ name: 'Klara', age: 18 },
|
38
|
+
{ name: 'Rose', age: 16 },
|
39
|
+
{ name: 'Al', age: 15 }
|
40
|
+
]
|
41
|
+
end
|
42
|
+
|
43
|
+
let(:expected) do
|
44
|
+
[
|
45
|
+
Employe.new(name: 'Bob', age: 21, company: company),
|
46
|
+
Employe.new(name: 'Klara', age: 18, company: company)
|
47
|
+
]
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'maps and filtes by method' do
|
51
|
+
expect(processor.process(value)).to eq(expected)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Employe
|
4
|
+
attr_reader :name, :age, :company
|
5
|
+
|
6
|
+
def initialize(name:, age:, company:)
|
7
|
+
@name = name
|
8
|
+
@age = age
|
9
|
+
@company = company
|
10
|
+
end
|
11
|
+
|
12
|
+
def adult?
|
13
|
+
age >= 18
|
14
|
+
end
|
15
|
+
|
16
|
+
def ==(other)
|
17
|
+
return unless other.class == self.class
|
18
|
+
other.name == name &&
|
19
|
+
other.age == age &&
|
20
|
+
other.company == company
|
21
|
+
end
|
22
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: arstotzka
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.4.
|
4
|
+
version: 1.4.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Darthjee
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-07-
|
11
|
+
date: 2019-07-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -30,14 +30,14 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 1.4.
|
33
|
+
version: 1.4.1
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: 1.4.
|
40
|
+
version: 1.4.1
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: bundler
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -227,9 +227,11 @@ files:
|
|
227
227
|
- lib/arstotzka/fetcher/cache.rb
|
228
228
|
- lib/arstotzka/fetcher_builder.rb
|
229
229
|
- lib/arstotzka/hash_reader.rb
|
230
|
+
- lib/arstotzka/key_changer.rb
|
230
231
|
- lib/arstotzka/key_reader.rb
|
231
232
|
- lib/arstotzka/method_builder.rb
|
232
233
|
- lib/arstotzka/options.rb
|
234
|
+
- lib/arstotzka/post_processor.rb
|
233
235
|
- lib/arstotzka/reader.rb
|
234
236
|
- lib/arstotzka/type_cast.rb
|
235
237
|
- lib/arstotzka/version.rb
|
@@ -251,9 +253,11 @@ files:
|
|
251
253
|
- spec/integration/yard/arstotzka/fetcher_builder_spec.rb
|
252
254
|
- spec/integration/yard/arstotzka/fetcher_spec.rb
|
253
255
|
- spec/integration/yard/arstotzka/hash_reader_spec.rb
|
256
|
+
- spec/integration/yard/arstotzka/key_changer_spec.rb
|
254
257
|
- spec/integration/yard/arstotzka/key_reader_spec.rb
|
255
258
|
- spec/integration/yard/arstotzka/method_builder_spec.rb
|
256
259
|
- spec/integration/yard/arstotzka/options_spec.rb
|
260
|
+
- spec/integration/yard/arstotzka/post_processor_spec.rb
|
257
261
|
- spec/integration/yard/arstotzka/reader_spec.rb
|
258
262
|
- spec/integration/yard/arstotzka/type_cast_spec.rb
|
259
263
|
- spec/integration/yard/arstotzka/wrapper_spec.rb
|
@@ -264,9 +268,11 @@ files:
|
|
264
268
|
- spec/lib/arstotzka/fetcher_builder_spec.rb
|
265
269
|
- spec/lib/arstotzka/fetcher_spec.rb
|
266
270
|
- spec/lib/arstotzka/hash_reader_spec.rb
|
271
|
+
- spec/lib/arstotzka/key_changer_spec.rb
|
267
272
|
- spec/lib/arstotzka/key_reader_spec.rb
|
268
273
|
- spec/lib/arstotzka/method_builder_spec.rb
|
269
274
|
- spec/lib/arstotzka/options_spec.rb
|
275
|
+
- spec/lib/arstotzka/post_processor_spec.rb
|
270
276
|
- spec/lib/arstotzka/reader_spec.rb
|
271
277
|
- spec/lib/arstotzka/wrapper_spec.rb
|
272
278
|
- spec/lib/arstotzka_spec.rb
|
@@ -287,14 +293,17 @@ files:
|
|
287
293
|
- spec/support/models/car_collector.rb
|
288
294
|
- spec/support/models/collector.rb
|
289
295
|
- spec/support/models/collector/game.rb
|
296
|
+
- spec/support/models/company.rb
|
290
297
|
- spec/support/models/customer.rb
|
291
298
|
- spec/support/models/drink.rb
|
299
|
+
- spec/support/models/employe.rb
|
292
300
|
- spec/support/models/game.rb
|
293
301
|
- spec/support/models/group.rb
|
294
302
|
- spec/support/models/house.rb
|
295
303
|
- spec/support/models/job_seeker.rb
|
296
304
|
- spec/support/models/my_model.rb
|
297
305
|
- spec/support/models/my_parser.rb
|
306
|
+
- spec/support/models/office.rb
|
298
307
|
- spec/support/models/person.rb
|
299
308
|
- spec/support/models/request.rb
|
300
309
|
- spec/support/models/restaurant.rb
|
@@ -344,9 +353,11 @@ test_files:
|
|
344
353
|
- spec/integration/yard/arstotzka/fetcher_builder_spec.rb
|
345
354
|
- spec/integration/yard/arstotzka/fetcher_spec.rb
|
346
355
|
- spec/integration/yard/arstotzka/hash_reader_spec.rb
|
356
|
+
- spec/integration/yard/arstotzka/key_changer_spec.rb
|
347
357
|
- spec/integration/yard/arstotzka/key_reader_spec.rb
|
348
358
|
- spec/integration/yard/arstotzka/method_builder_spec.rb
|
349
359
|
- spec/integration/yard/arstotzka/options_spec.rb
|
360
|
+
- spec/integration/yard/arstotzka/post_processor_spec.rb
|
350
361
|
- spec/integration/yard/arstotzka/reader_spec.rb
|
351
362
|
- spec/integration/yard/arstotzka/type_cast_spec.rb
|
352
363
|
- spec/integration/yard/arstotzka/wrapper_spec.rb
|
@@ -357,9 +368,11 @@ test_files:
|
|
357
368
|
- spec/lib/arstotzka/fetcher_builder_spec.rb
|
358
369
|
- spec/lib/arstotzka/fetcher_spec.rb
|
359
370
|
- spec/lib/arstotzka/hash_reader_spec.rb
|
371
|
+
- spec/lib/arstotzka/key_changer_spec.rb
|
360
372
|
- spec/lib/arstotzka/key_reader_spec.rb
|
361
373
|
- spec/lib/arstotzka/method_builder_spec.rb
|
362
374
|
- spec/lib/arstotzka/options_spec.rb
|
375
|
+
- spec/lib/arstotzka/post_processor_spec.rb
|
363
376
|
- spec/lib/arstotzka/reader_spec.rb
|
364
377
|
- spec/lib/arstotzka/wrapper_spec.rb
|
365
378
|
- spec/lib/arstotzka_spec.rb
|
@@ -380,14 +393,17 @@ test_files:
|
|
380
393
|
- spec/support/models/car_collector.rb
|
381
394
|
- spec/support/models/collector.rb
|
382
395
|
- spec/support/models/collector/game.rb
|
396
|
+
- spec/support/models/company.rb
|
383
397
|
- spec/support/models/customer.rb
|
384
398
|
- spec/support/models/drink.rb
|
399
|
+
- spec/support/models/employe.rb
|
385
400
|
- spec/support/models/game.rb
|
386
401
|
- spec/support/models/group.rb
|
387
402
|
- spec/support/models/house.rb
|
388
403
|
- spec/support/models/job_seeker.rb
|
389
404
|
- spec/support/models/my_model.rb
|
390
405
|
- spec/support/models/my_parser.rb
|
406
|
+
- spec/support/models/office.rb
|
391
407
|
- spec/support/models/person.rb
|
392
408
|
- spec/support/models/request.rb
|
393
409
|
- spec/support/models/restaurant.rb
|