arstotzka 1.0.1 → 1.0.2
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/.circleci/config.yml +1 -0
- data/.gitignore +2 -1
- data/.rubocop.yml +11 -0
- data/.rubocop_todo.yml +20 -0
- data/Gemfile +3 -2
- data/Guardfile +3 -2
- data/README.md +24 -5
- data/Rakefile +5 -3
- data/arstotzka.gemspec +18 -15
- data/arstotzka.jpg +0 -0
- data/lib/arstotzka.rb +2 -0
- data/lib/arstotzka/builder.rb +109 -47
- data/lib/arstotzka/class_methods.rb +34 -0
- data/lib/arstotzka/crawler.rb +86 -11
- data/lib/arstotzka/exception.rb +7 -2
- data/lib/arstotzka/fetcher.rb +93 -36
- data/lib/arstotzka/reader.rb +59 -11
- data/lib/arstotzka/type_cast.rb +24 -10
- data/lib/arstotzka/version.rb +3 -1
- data/lib/arstotzka/wrapper.rb +59 -34
- data/spec/integration/readme/default_spec.rb +2 -0
- data/spec/integration/readme/my_parser_spec.rb +2 -0
- data/spec/integration/yard/arstotzka/builder_spec.rb +63 -0
- data/spec/integration/yard/arstotzka/class_methods_spec.rb +49 -0
- data/spec/integration/yard/arstotzka/crawler_spec.rb +77 -0
- data/spec/integration/yard/arstotzka/fetcher_spec.rb +51 -0
- data/spec/integration/yard/arstotzka/reader_spec.rb +77 -0
- data/spec/integration/yard/arstotzka/wrapper_spec.rb +29 -0
- data/spec/lib/arstotzka/builder_spec.rb +6 -4
- data/spec/lib/arstotzka/crawler_spec.rb +30 -17
- data/spec/lib/arstotzka/fetcher_spec.rb +4 -2
- data/spec/lib/arstotzka/reader_spec.rb +13 -11
- data/spec/lib/arstotzka/wrapper_spec.rb +10 -10
- data/spec/lib/arstotzka_spec.rb +12 -8
- data/spec/spec_helper.rb +6 -2
- data/spec/support/fixture_helpers.rb +4 -2
- data/spec/support/models.rb +4 -3
- data/spec/support/models/account.rb +9 -0
- data/spec/support/models/arstotzka/dummy.rb +21 -17
- data/spec/support/models/arstotzka/fetcher/dummy.rb +7 -2
- data/spec/support/models/arstotzka/type_cast.rb +7 -4
- data/spec/support/models/arstotzka/wrapper/dummy.rb +10 -5
- data/spec/support/models/game.rb +2 -0
- data/spec/support/models/house.rb +2 -0
- data/spec/support/models/my_model.rb +9 -0
- data/spec/support/models/my_parser.rb +6 -5
- data/spec/support/models/person.rb +7 -1
- data/spec/support/models/star.rb +2 -0
- data/spec/support/models/star_gazer.rb +3 -2
- data/spec/support/models/transaction.rb +19 -0
- data/spec/support/shared_examples/wrapper.rb +6 -5
- metadata +64 -15
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c670a2581e68a1b7e54d5451b6b425fde3cde0db
|
|
4
|
+
data.tar.gz: 0f2d973a287534f68be841ebcbb63fc0e5f5babb
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1826c6c9b9017ce2f302ed930856acb4889eede6a203b25dc1674f1dfe545bae8407acbfaad4cee294782e2afaa11aca50ee39f95700756d6e62ab8cbf18bdc9
|
|
7
|
+
data.tar.gz: 29f5570b7d161c62069512a9605fde0424c6ac9083ad7dc15d25b6a36e86afe747b6e3ad0abefd1a8d908c86b2b9df34415ea23543d8f2744286a017727ea012
|
data/.circleci/config.yml
CHANGED
data/.gitignore
CHANGED
data/.rubocop.yml
ADDED
data/.rubocop_todo.yml
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# This configuration was generated by
|
|
2
|
+
# `rubocop --auto-gen-config`
|
|
3
|
+
# on 2018-07-22 21:24:28 +0000 using RuboCop version 0.58.0.
|
|
4
|
+
# The point is for the user to remove these configuration records
|
|
5
|
+
# one by one as the offenses are removed from the code base.
|
|
6
|
+
# Note that changes in the inspected code, or installation of new
|
|
7
|
+
# versions of RuboCop, may require this file to be generated again.
|
|
8
|
+
|
|
9
|
+
# Offense count: 1
|
|
10
|
+
# Configuration parameters: CountKeywordArgs.
|
|
11
|
+
Metrics/ParameterLists:
|
|
12
|
+
Exclude:
|
|
13
|
+
- 'lib/arstotzka/builder.rb'
|
|
14
|
+
|
|
15
|
+
# Offense count: 1
|
|
16
|
+
Style/Documentation:
|
|
17
|
+
Exclude:
|
|
18
|
+
- 'spec/**/*'
|
|
19
|
+
- 'test/**/*'
|
|
20
|
+
- 'lib/arstotzka.rb'
|
data/Gemfile
CHANGED
data/Guardfile
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
# More info at https://github.com/guard/guard#readme
|
|
2
4
|
|
|
3
5
|
guard 'bundler' do
|
|
@@ -8,6 +10,5 @@ end
|
|
|
8
10
|
guard :rspec, all_after_pass: true, all_on_start: true do
|
|
9
11
|
watch(%r{^spec/.+_spec\.rb$})
|
|
10
12
|
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
|
|
11
|
-
watch('spec/spec_helper.rb') {
|
|
13
|
+
watch('spec/spec_helper.rb') { 'spec' }
|
|
12
14
|
end
|
|
13
|
-
|
data/README.md
CHANGED
|
@@ -4,6 +4,8 @@ Arstotzka
|
|
|
4
4
|
[](https://codeclimate.com/github/darthjee/arstotzka/coverage)
|
|
5
5
|
[](https://codeclimate.com/github/darthjee/arstotzka)
|
|
6
6
|
|
|
7
|
+

|
|
8
|
+
|
|
7
9
|
This project allows for a quick hash / json data fetching in order to avoid code
|
|
8
10
|
that tries to crawl through a hash and has to constantly check for nil values or missing keys
|
|
9
11
|
|
|
@@ -12,21 +14,38 @@ avoids method missing by aways having the declarated methods, even if that means
|
|
|
12
14
|
|
|
13
15
|
Json Parser is also usefull when you need keys case changed or data type cast
|
|
14
16
|
|
|
15
|
-
|
|
17
|
+
Instalation
|
|
16
18
|
---------------
|
|
17
19
|
1. Add Arstotzka to your `Gemfile` and `bundle install`:
|
|
20
|
+
- Install it
|
|
21
|
+
|
|
22
|
+
```ruby
|
|
23
|
+
gem install arstotzka
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
- Or add Arstotka to you `Gemfile` and `bundle install`
|
|
18
27
|
|
|
19
28
|
```ruby
|
|
20
29
|
gem 'arstotzka'
|
|
21
30
|
```
|
|
22
31
|
|
|
23
|
-
|
|
32
|
+
```bash
|
|
33
|
+
bundle install arstotzka
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Yard Documentation
|
|
37
|
+
-------------------
|
|
38
|
+
https://www.rubydoc.info/gems/arstotzka/
|
|
39
|
+
|
|
40
|
+
Getting Started
|
|
41
|
+
---------------
|
|
42
|
+
1. Include in a class that you want to wrap a json/hash
|
|
24
43
|
```ruby
|
|
25
44
|
class MyParser
|
|
26
45
|
include Arstotzka
|
|
27
46
|
```
|
|
28
47
|
|
|
29
|
-
|
|
48
|
+
2. Declare the keys you want to crawl
|
|
30
49
|
```ruby
|
|
31
50
|
class MyParser
|
|
32
51
|
include Arstotzka
|
|
@@ -67,9 +86,9 @@ Getting started
|
|
|
67
86
|
#returns nil
|
|
68
87
|
```
|
|
69
88
|
|
|
70
|
-
|
|
89
|
+
3. fully customise the way you crawl / fetch the information with [Options](#options)
|
|
71
90
|
|
|
72
|
-
|
|
91
|
+
4. Create custom [typecast](#TypeCast)
|
|
73
92
|
|
|
74
93
|
Options
|
|
75
94
|
-------
|
data/Rakefile
CHANGED
data/arstotzka.gemspec
CHANGED
|
@@ -1,29 +1,32 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
lib = File.expand_path('lib', __dir__)
|
|
3
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
5
|
require 'arstotzka/version'
|
|
5
6
|
|
|
6
7
|
Gem::Specification.new do |spec|
|
|
7
|
-
spec.name =
|
|
8
|
+
spec.name = 'arstotzka'
|
|
8
9
|
spec.version = Arstotzka::VERSION
|
|
9
|
-
spec.authors = [
|
|
10
|
-
spec.email = [
|
|
11
|
-
spec.summary =
|
|
10
|
+
spec.authors = ['Darthjee']
|
|
11
|
+
spec.email = ['dev@gmail.com']
|
|
12
|
+
spec.summary = 'Arstotzka'
|
|
12
13
|
spec.description = spec.description
|
|
13
|
-
spec.homepage =
|
|
14
|
+
spec.homepage = 'https://github.com/darthjee/arstotzka'
|
|
14
15
|
|
|
15
|
-
spec.files = `git ls-files`.split(
|
|
16
|
-
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f)
|
|
16
|
+
spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
|
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
|
17
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
|
18
|
-
spec.require_paths = [
|
|
19
|
+
spec.require_paths = ['lib']
|
|
19
20
|
|
|
20
21
|
spec.add_runtime_dependency 'activesupport', '~> 5.x'
|
|
21
|
-
spec.add_runtime_dependency 'sinclair'
|
|
22
|
+
spec.add_runtime_dependency 'sinclair', '>= 1.1.1'
|
|
22
23
|
|
|
24
|
+
spec.add_development_dependency 'bundler', '~> 1.6'
|
|
25
|
+
spec.add_development_dependency 'pry-nav'
|
|
26
|
+
spec.add_development_dependency 'rake', '>= 12.3.1'
|
|
27
|
+
spec.add_development_dependency 'rspec', '>= 3.7'
|
|
28
|
+
spec.add_development_dependency 'rubocop'
|
|
23
29
|
spec.add_development_dependency 'safe_attribute_assignment'
|
|
24
|
-
spec.add_development_dependency "bundler", "~> 1.6"
|
|
25
|
-
spec.add_development_dependency "rake", ">= 12.3.1"
|
|
26
|
-
spec.add_development_dependency "rspec", ">= 3.7"
|
|
27
30
|
spec.add_development_dependency 'simplecov'
|
|
28
|
-
spec.add_development_dependency '
|
|
31
|
+
spec.add_development_dependency 'yard'
|
|
29
32
|
end
|
data/arstotzka.jpg
ADDED
|
Binary file
|
data/lib/arstotzka.rb
CHANGED
data/lib/arstotzka/builder.rb
CHANGED
|
@@ -1,9 +1,38 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
module Arstotzka
|
|
4
|
+
# Class responsible to orchestrate the addtion of method that will
|
|
5
|
+
# crawl the hash for value
|
|
6
|
+
#
|
|
7
|
+
# @example
|
|
8
|
+
# class MyModel
|
|
9
|
+
# attr_reader :json
|
|
10
|
+
#
|
|
11
|
+
# def initialize(json)
|
|
12
|
+
# @json = json
|
|
13
|
+
# end
|
|
14
|
+
# end
|
|
15
|
+
#
|
|
16
|
+
# instance = MyModel.new(
|
|
17
|
+
# 'name' => { first: 'John', last: 'Williams' },
|
|
18
|
+
# :age => '20',
|
|
19
|
+
# 'cars' => 2.0
|
|
20
|
+
# )
|
|
21
|
+
#
|
|
22
|
+
# builder = Arstotzka::Builder.new([ :first_name ], MyModel, full_path: 'name.first')
|
|
23
|
+
# builder.build
|
|
24
|
+
#
|
|
25
|
+
# instance.first_name # returns 'John'
|
|
26
|
+
#
|
|
27
|
+
# builder = Arstotzka::Builder.new([ :age, :cars ], MyModel, type: :integer)
|
|
28
|
+
# builder.build
|
|
29
|
+
#
|
|
30
|
+
# instance.age # returns 20
|
|
31
|
+
# instance.cars # returns 2
|
|
32
|
+
#
|
|
33
|
+
# @see https://www.rubydoc.info/gems/sinclair Sinclair
|
|
34
|
+
class Builder < Sinclair
|
|
35
|
+
DEFAULT_OPTIONS = {
|
|
7
36
|
after: false,
|
|
8
37
|
case: :lower_camel,
|
|
9
38
|
class: nil,
|
|
@@ -12,62 +41,95 @@ class Arstotzka::Builder < Sinclair
|
|
|
12
41
|
flatten: false,
|
|
13
42
|
json: :json,
|
|
14
43
|
type: :none
|
|
15
|
-
}.
|
|
16
|
-
|
|
17
|
-
@attr_names = attr_names
|
|
18
|
-
@path = path
|
|
19
|
-
@full_path = full_path
|
|
20
|
-
@cached = cached
|
|
21
|
-
init
|
|
22
|
-
end
|
|
44
|
+
}.freeze
|
|
23
45
|
|
|
24
|
-
|
|
46
|
+
# @param attr_names [Array] list of attributes to be fetched from the hash/json
|
|
47
|
+
# @param clazz [Class] class to receive the methods
|
|
48
|
+
# @param path [String/Symbol] path of hash attributes to find the root
|
|
49
|
+
# where the attribute live (then fetching it using the attribute name)
|
|
50
|
+
# @param full_path [String/Symbol] path of hash attributes to find exacttly where the
|
|
51
|
+
# value live (ignoring the attribute name)
|
|
52
|
+
# @param cached [Boolean] flag if the result should be memorized instead of repeating
|
|
53
|
+
# the crawling
|
|
54
|
+
# @param json [String/Symbol] name of the method containing the Hash/json to be crawled
|
|
55
|
+
# @param options [Hash] hash containing extra options
|
|
56
|
+
# @option options [String/Symbol] case: {Reader} flag definining on which case will
|
|
57
|
+
# the keys be defined
|
|
58
|
+
# - lower_camel: keys in the hash are lowerCamelCase
|
|
59
|
+
# - upper_camel: keys in the hash are UpperCamelCase
|
|
60
|
+
# - snake: keys in the hash are snake_case
|
|
61
|
+
# @option options [Boolean] compact: {Crawler} flag to apply Array#compact thus
|
|
62
|
+
# removing nil results
|
|
63
|
+
# @option options [Class] class: {Fetcher} option thatwhen passed, wraps the result in an
|
|
64
|
+
# instance of the given class
|
|
65
|
+
# @option options [String/Symbol] after: {Fetcher} option with the name of the method to be
|
|
66
|
+
# called once the value is fetched for mapping the value
|
|
67
|
+
# @option options [Boolean] flatten: {Fetcher} flag to aplly Array#flatten thus
|
|
68
|
+
# avoing nested arrays
|
|
69
|
+
# @option options [String/Symbol] type: {Fetcher} option declaring the type of the returned
|
|
70
|
+
# value (to use casting)
|
|
71
|
+
# - integer
|
|
72
|
+
# - string
|
|
73
|
+
# - float
|
|
74
|
+
def initialize(attr_names, clazz,
|
|
75
|
+
json: :json, path: nil, full_path: nil, cached: false,
|
|
76
|
+
**options)
|
|
77
|
+
super(clazz, DEFAULT_OPTIONS.merge(options.symbolize_keys))
|
|
25
78
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
79
|
+
@attr_names = attr_names
|
|
80
|
+
@path = path
|
|
81
|
+
@full_path = full_path
|
|
82
|
+
@cached = cached
|
|
83
|
+
@json_name = json
|
|
84
|
+
init
|
|
29
85
|
end
|
|
30
|
-
end
|
|
31
86
|
|
|
32
|
-
|
|
33
|
-
full_path || [path, attribute].compact.join('.')
|
|
34
|
-
end
|
|
87
|
+
private
|
|
35
88
|
|
|
36
|
-
|
|
37
|
-
options[:json]
|
|
38
|
-
end
|
|
89
|
+
attr_reader :attr_names, :json_name, :path, :full_path, :cached
|
|
39
90
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
91
|
+
def init
|
|
92
|
+
attr_names.each do |attr|
|
|
93
|
+
add_attr(attr)
|
|
94
|
+
end
|
|
95
|
+
end
|
|
43
96
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
97
|
+
def real_path(attribute)
|
|
98
|
+
full_path || [path, attribute].compact.join('.')
|
|
99
|
+
end
|
|
47
100
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
case_type: case_type,
|
|
52
|
-
path: real_path(attribute)
|
|
53
|
-
})
|
|
54
|
-
end
|
|
101
|
+
def wrapper_clazz
|
|
102
|
+
options[:class]
|
|
103
|
+
end
|
|
55
104
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
105
|
+
def case_type
|
|
106
|
+
options[:case]
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def fetcher_options(attribute)
|
|
110
|
+
options.slice(:compact, :after, :type, :flatten, :default).merge(
|
|
111
|
+
clazz: wrapper_clazz,
|
|
112
|
+
case_type: case_type,
|
|
113
|
+
path: real_path(attribute)
|
|
114
|
+
)
|
|
115
|
+
end
|
|
59
116
|
|
|
60
|
-
|
|
61
|
-
|
|
117
|
+
def add_attr(attribute)
|
|
118
|
+
add_method attribute, (cached ? cached_fetcher(attribute) : attr_fetcher(attribute)).to_s
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def attr_fetcher(attribute)
|
|
122
|
+
<<-CODE
|
|
62
123
|
::Arstotzka::Fetcher.new(
|
|
63
124
|
#{json_name}, self, #{fetcher_options(attribute)}
|
|
64
125
|
).fetch
|
|
65
|
-
|
|
66
|
-
|
|
126
|
+
CODE
|
|
127
|
+
end
|
|
67
128
|
|
|
68
|
-
|
|
69
|
-
|
|
129
|
+
def cached_fetcher(attribute)
|
|
130
|
+
<<-CODE
|
|
70
131
|
@#{attribute} ||= #{attr_fetcher(attribute)}
|
|
71
|
-
|
|
132
|
+
CODE
|
|
133
|
+
end
|
|
72
134
|
end
|
|
73
135
|
end
|
|
@@ -1,5 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Arstotzka
|
|
4
|
+
# As Arstotzka extends ActiveSupport::Concern, Arstotzka::ClassMethods define
|
|
5
|
+
# methods that will be available when defining a class that includes Arstotka
|
|
2
6
|
module ClassMethods
|
|
7
|
+
# expose a field from the json/hash as a method
|
|
8
|
+
#
|
|
9
|
+
# @example
|
|
10
|
+
# class MyModel
|
|
11
|
+
# include Arstotzka
|
|
12
|
+
#
|
|
13
|
+
# attr_reader :json
|
|
14
|
+
#
|
|
15
|
+
# expose :first_name, full_path: 'name.first'
|
|
16
|
+
# expose :age, 'cars', type: :integer
|
|
17
|
+
#
|
|
18
|
+
# def initialize(json)
|
|
19
|
+
# @json = json
|
|
20
|
+
# end
|
|
21
|
+
# end
|
|
22
|
+
#
|
|
23
|
+
# instance = MyModel.new(
|
|
24
|
+
# 'name' => { first: 'John', last: 'Williams' },
|
|
25
|
+
# :age => '20',
|
|
26
|
+
# 'cars' => 2.0
|
|
27
|
+
# )
|
|
28
|
+
#
|
|
29
|
+
# instance.first_name # returns 'John'
|
|
30
|
+
# instance.age # returns 20
|
|
31
|
+
# instance.cars # returns 2
|
|
32
|
+
#
|
|
33
|
+
# @see Builder Arstotzka::Builder
|
|
34
|
+
# @see
|
|
35
|
+
# https://www.rubydoc.info/gems/activesupport/5.0.0.1/ActiveSupport/Concern
|
|
36
|
+
# ActiveSupport::Concern
|
|
3
37
|
def expose(*attr_names, **options)
|
|
4
38
|
Builder.new(attr_names, self, options).build
|
|
5
39
|
end
|
data/lib/arstotzka/crawler.rb
CHANGED
|
@@ -1,28 +1,103 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Arstotzka
|
|
4
|
+
# Crawl a hash through the path of keys
|
|
5
|
+
# @example
|
|
6
|
+
# crawler = Arstotzka::Crawler.new(%w(person information first_name))
|
|
7
|
+
# hash = {
|
|
8
|
+
# person: {
|
|
9
|
+
# 'information' => {
|
|
10
|
+
# 'firstName' => 'John'
|
|
11
|
+
# }
|
|
12
|
+
# }
|
|
13
|
+
# }
|
|
14
|
+
# crawler.value(hash) # returns 'John'
|
|
2
15
|
class Crawler
|
|
3
|
-
|
|
4
|
-
|
|
16
|
+
# @param path [Array] path of keys to be crawled
|
|
17
|
+
# @param case_type [Symbol] case type of the keys
|
|
18
|
+
# - snake: snake_cased keys
|
|
19
|
+
# - lower_camel: lowerCamelCased keys
|
|
20
|
+
# - upper_camel: UperCamelCased keys
|
|
21
|
+
# @param compact [Boolean] flag signallying if nil values should be removed of an array
|
|
22
|
+
# @param default [Object] default value to be returned when failing to fetch a value
|
|
23
|
+
# @param block [Proc] block to be ran over the fetched value before returning it
|
|
5
24
|
def initialize(path:, case_type: :lower_camel, compact: false, default: nil, &block)
|
|
6
25
|
@case_type = case_type
|
|
7
26
|
@compact = compact
|
|
8
27
|
@default = default
|
|
9
28
|
@path = path
|
|
10
|
-
@post_process = block
|
|
29
|
+
@post_process = block || proc { |value| value }
|
|
11
30
|
end
|
|
12
31
|
|
|
13
|
-
|
|
14
|
-
|
|
32
|
+
# crawls into the hash looking for all keys in the given path
|
|
33
|
+
# returning the final value
|
|
34
|
+
#
|
|
35
|
+
# @overload value(hash)
|
|
36
|
+
# @return [Object] value fetched from the last Hash#fetch call using the last part
|
|
37
|
+
# of path
|
|
38
|
+
#
|
|
39
|
+
# @example
|
|
40
|
+
# crawler = Arstotzka::Crawler.new(%w(person information first_name))
|
|
41
|
+
# hash = {
|
|
42
|
+
# person: {
|
|
43
|
+
# 'information' => {
|
|
44
|
+
# 'firstName' => 'John'
|
|
45
|
+
# }
|
|
46
|
+
# }
|
|
47
|
+
# }
|
|
48
|
+
# crawler.value(hash) # returns 'John'
|
|
49
|
+
#
|
|
50
|
+
# @example
|
|
51
|
+
# crawler = Arstotzka::Crawler.new(
|
|
52
|
+
# %w(companies games hero),
|
|
53
|
+
# compact: true, case_type: :snake
|
|
54
|
+
# )
|
|
55
|
+
# games_hash = {
|
|
56
|
+
# 'companies' => [{
|
|
57
|
+
# name: 'Lucas Pope',
|
|
58
|
+
# games: [{
|
|
59
|
+
# 'name' => 'papers, please'
|
|
60
|
+
# }, {
|
|
61
|
+
# 'name' => 'TheNextBigThing',
|
|
62
|
+
# hero_name: 'Rakhar'
|
|
63
|
+
# }]
|
|
64
|
+
# }, {
|
|
65
|
+
# name: 'Old Company'
|
|
66
|
+
# }]
|
|
67
|
+
# }
|
|
68
|
+
#
|
|
69
|
+
# crawler.value(games_hash) # returns [['Rakhar']]
|
|
70
|
+
#
|
|
71
|
+
# @example
|
|
72
|
+
# crawler = Arstotzka::Crawler.new(
|
|
73
|
+
# %w(companies games hero),
|
|
74
|
+
# compact: true, case_type: :snake, default: 'NO HERO'
|
|
75
|
+
# )
|
|
76
|
+
#
|
|
77
|
+
# crawler.value(games_hash) # returns [['NO HERO', 'Rakhar'], 'NO HERO']
|
|
78
|
+
#
|
|
79
|
+
# @example
|
|
80
|
+
# crawler = Arstotzka::Crawler.new(
|
|
81
|
+
# %w(companies games hero),
|
|
82
|
+
# compact: true, case_type: :snake
|
|
83
|
+
# ) { |value| value.&to_sym }
|
|
84
|
+
#
|
|
85
|
+
# crawler.value(games_hash) # returns [[:Rakhar]]
|
|
86
|
+
def value(hash, index = 0)
|
|
87
|
+
crawl(hash, index)
|
|
15
88
|
rescue Exception::KeyNotFound
|
|
16
89
|
wrap(default)
|
|
17
90
|
end
|
|
18
91
|
|
|
19
92
|
private
|
|
20
93
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
94
|
+
attr_reader :post_process, :path, :case_type, :compact, :default
|
|
95
|
+
|
|
96
|
+
def crawl(hash, index = 0)
|
|
97
|
+
return wrap(hash) if reader.ended?(index)
|
|
98
|
+
return crawl_array(hash, index) if hash.is_a?(Array)
|
|
24
99
|
|
|
25
|
-
crawl(reader.read(
|
|
100
|
+
crawl(reader.read(hash, index), index + 1)
|
|
26
101
|
end
|
|
27
102
|
|
|
28
103
|
def reader
|
|
@@ -32,8 +107,8 @@ module Arstotzka
|
|
|
32
107
|
)
|
|
33
108
|
end
|
|
34
109
|
|
|
35
|
-
def wrap(
|
|
36
|
-
post_process.call(
|
|
110
|
+
def wrap(hash)
|
|
111
|
+
post_process.call(hash)
|
|
37
112
|
end
|
|
38
113
|
|
|
39
114
|
def crawl_array(array, index)
|