json-path-builder 0.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.
@@ -0,0 +1,168 @@
1
+ module JsonPath
2
+ RSpec.describe Builder do
3
+ let(:key) { "some-value" }
4
+ let(:other_key) { "some-other-value" }
5
+ let(:list) { %w[some-list-value-1 some-list-value-2] }
6
+
7
+ let(:input) { { key: key, other_key: other_key, list: list }.as_json }
8
+
9
+ subject(:instance) { described_class.new }
10
+
11
+ describe '.from' do
12
+ it 'handles simple path mapping' do
13
+ %i[key other_key].each { |k| instance.from(k) }
14
+
15
+ expect(instance.build_for(input)).to eql({ key: key, other_key: other_key })
16
+ end
17
+
18
+ it 'handles renaming path name' do
19
+ instance.from(:other_key, to: :another_key)
20
+
21
+ expect(instance.build_for(input)).to eql({ another_key: other_key })
22
+ end
23
+
24
+ it 'handles transformations' do
25
+ instance.from(:key, transform: proc { |v| v.upcase })
26
+
27
+ expect(instance.build_for(input)).to eql({ key: key.upcase })
28
+ end
29
+ end
30
+
31
+ describe '.from_each' do
32
+ it 'handles simple path mapping' do
33
+ instance.from_each(:list)
34
+
35
+ expect(instance.build_for(input)).to eql({ list: list })
36
+ end
37
+
38
+ it 'handles renaming path name' do
39
+ instance.from(:list, to: :another_list_key)
40
+
41
+ expect(instance.build_for(input)).to eql({ another_list_key: list })
42
+ end
43
+
44
+ it 'handles transformations' do
45
+ instance.from_each(:list, transform: proc { |v| v.upcase })
46
+
47
+ expect(instance.build_for(input)).to eql({ list: list.map(&:upcase) })
48
+ end
49
+
50
+ it 'handles skipping items' do
51
+ instance.from_each(:list, skip_if: proc { |v| v == 'some-list-value-1' })
52
+
53
+ expect(instance.build_for(input)).to eql({ list: ['some-list-value-2'] })
54
+ end
55
+ end
56
+
57
+ describe '#within' do
58
+ let(:email) { 'email@domain.com' }
59
+ let(:user_id) { 1 }
60
+ let(:input) { { root: { deep: { profile: { email: email, uid: user_id } } } }.as_json }
61
+
62
+ it 'uses scope to simplify dot notation' do
63
+ builder = described_class.new
64
+ builder.within('root.deep.profile') do |b|
65
+ b.from(:email)
66
+ b.from(:uid, to: :user_id)
67
+ end
68
+
69
+ expect(builder.build_for(input)).to eql({ email: email, user_id: user_id })
70
+ end
71
+ end
72
+
73
+ describe '#with_wrapped_data_class' do
74
+ let(:email) { 'email@domain.com' }
75
+ let(:user_id) { 123 }
76
+ let(:input) { { profile: { email: email } }.as_json }
77
+ let(:user) { double(:user, id: user_id) }
78
+ let(:user_repo) { double(:user_repo, find_by: user) }
79
+
80
+ let(:wrapped_data_class) do
81
+ Class.new(SimpleDelegator) do
82
+ class << self
83
+ attr_accessor :user_repo
84
+ end
85
+
86
+ def user
87
+ self.class.user_repo.find_by(email: dig('profile', 'email'))
88
+ end
89
+ end
90
+ end
91
+
92
+ before do
93
+ wrapped_data_class.user_repo = user_repo
94
+ end
95
+
96
+ it 'wraps input data in wrapped class' do
97
+ builder = described_class.new
98
+ builder.with_wrapped_data_class(wrapped_data_class)
99
+ transform = proc do |_email, path_context|
100
+ path_context.wrapped_source_data.user.id
101
+ end
102
+
103
+ builder.from('profile.email', to: :user_id, transform: transform)
104
+ expect(builder.build_for(input)).to eql({ user_id: user_id })
105
+ end
106
+ end
107
+
108
+ describe '#wrapped_data_class' do
109
+ context 'with custom data class' do
110
+ let(:wrapper_class) { Class.new(DefaultDataWrapper) }
111
+
112
+ it 'returns custom class' do
113
+ instance.with_wrapped_data_class(wrapper_class)
114
+
115
+ expect(instance.data_wrapper_class).to eql(wrapper_class)
116
+ end
117
+ end
118
+
119
+ context 'without custom data class' do
120
+ let(:custom_wrapper) { nil }
121
+
122
+ it 'returns default class' do
123
+ expect(instance.data_wrapper_class).to eql(DefaultDataWrapper)
124
+ end
125
+ end
126
+ end
127
+
128
+ # describe PathsBuilder::PathContext do
129
+ # let(:key) { "some-value" }
130
+ # let(:other_key) { "some-other-value" }
131
+ # let(:list) { %w[some-list-value-1 some-list-value-2] }
132
+ #
133
+ # let(:input) { { key: key, other_key: other_key, list: list }.as_json }
134
+ # let(:builder) { PathsBuilder.new }
135
+ # let(:path_context) { builder.path_context_collection.first }
136
+ #
137
+ # before(:each) do
138
+ # builder.from(:key, to: :another_key, transform: proc { |val| val.upcase })
139
+ # builder.with_source_data(input)
140
+ # end
141
+ #
142
+ #
143
+ # it 'returns path context instance' do
144
+ # expect(path_context).to be_instance_of(described_class)
145
+ # end
146
+ #
147
+ # describe '#wrapped_source_data' do
148
+ # subject { path_context.wrapped_source_data }
149
+ #
150
+ # context 'with custom data class' do
151
+ # let(:wrapper_class) { Sharplaunch::PropertyListingDataWrapper }
152
+ #
153
+ # before(:each) { builder.with_wrapped_data_class(wrapper_class) }
154
+ #
155
+ # it 'returns custom class' do
156
+ # expect(subject).to be_an_instance_of(wrapper_class)
157
+ # end
158
+ # end
159
+ #
160
+ # context 'without custom data wrapper class' do
161
+ # it 'returns default wrapper instance' do
162
+ # expect(subject).to be_instance_of(PathsBuilder::DefaultDataWrapper)
163
+ # end
164
+ # end
165
+ # end
166
+ # end
167
+ end
168
+ end
@@ -0,0 +1,33 @@
1
+ module JsonPath
2
+ RSpec.describe DefaultDataWrapper do
3
+ describe '#[]' do
4
+ context 'when the wrapped object is a hash with string keys' do
5
+ let(:data) { { 'name' => 'John', 'age' => 30 } }
6
+ let(:wrapper) { described_class.new(data) }
7
+
8
+ it 'returns the value associated with the symbol key' do
9
+ expect(wrapper[:name]).to eq('John')
10
+ expect(wrapper[:age]).to eq(30)
11
+ end
12
+
13
+ it 'does not modify the original hash' do
14
+ expect { wrapper[:name] }.not_to(change { data })
15
+ end
16
+ end
17
+
18
+ context 'when the wrapped object is a hash with symbol keys' do
19
+ let(:data) { { name: 'John', age: 30 } }
20
+ let(:wrapper) { described_class.new(data) }
21
+
22
+ it 'returns the value associated with the symbol key' do
23
+ expect(wrapper[:name]).to eq('John')
24
+ expect(wrapper[:age]).to eq(30)
25
+ end
26
+
27
+ it 'does not modify the original hash' do
28
+ expect { wrapper[:name] }.not_to(change { data })
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,46 @@
1
+ module JsonPath
2
+ # rubocop:disable Layout/LineLength,RSpec/MessageSpies,RSpec/StubbedMock
3
+ RSpec.describe PathContextCollection do
4
+ describe '#reject_from_paths!' do
5
+ let(:path_context1) { instance_double(PathContext, from: 'path1') }
6
+ let(:path_context2) { instance_double(PathContext, from: 'path2') }
7
+ let(:path_context3) { instance_double(PathContext, from: 'path3') }
8
+
9
+ it 'removes elements from the collection with matching from paths' do
10
+ collection = described_class.new([])
11
+ collection.push(path_context1, path_context2, path_context3)
12
+
13
+ collection.reject_from_paths!(%w[path1 path3])
14
+
15
+ expect(collection).to include(path_context2)
16
+ expect(collection).not_to include(path_context1, path_context3)
17
+ end
18
+ end
19
+
20
+ describe '#add_path' do
21
+ let(:paths_builder) { instance_double(Builder) }
22
+ let(:iterable_data) { true }
23
+ let(:transform) { proc { |data| data.upcase } }
24
+ let(:defaults) { { foo: 'bar' } }
25
+ let(:fallback_proc) { proc { 'fallback' } }
26
+ let(:skip_if_proc) { proc { |data| data.nil? } }
27
+ let(:path_context) { instance_double(PathContext) }
28
+
29
+ before do
30
+ allow(PathContext).to receive(:new).and_return(path_context)
31
+ end
32
+
33
+ it 'adds a new PathContext object to the collection' do
34
+ collection = described_class.new([])
35
+ expect(PathContext).to receive(:new).with('path', paths_builder, to: nil, iterable_data: iterable_data,
36
+ transform: transform, use_builder: true, defaults: defaults, fallback_proc: fallback_proc, skip_if_proc: skip_if_proc).and_return(path_context)
37
+
38
+ result = collection.add_path('path', paths_builder, iterable_data: iterable_data, transform: transform,
39
+ defaults: defaults, fallback_proc: fallback_proc, skip_if_proc: skip_if_proc)
40
+
41
+ expect(result).to include(path_context)
42
+ end
43
+ end
44
+ end
45
+ # rubocop:enable Layout/LineLength,RSpec/MessageSpies,RSpec/StubbedMock
46
+ end
@@ -0,0 +1,90 @@
1
+ module JsonPath
2
+ RSpec.describe PathContext do
3
+ let(:builder) { double("PathsBuilder", nested_paths: []) }
4
+ let(:data) { { foo: "bar" } }
5
+ let(:transform) { :iso8601 }
6
+ let(:json_path) { "foo" }
7
+ let(:defaults) { nil }
8
+ let(:path_context) do
9
+ described_class.new(json_path, builder, iterable_data: false, transform: transform, use_builder: false,
10
+ defaults: defaults, fallback_proc: nil, skip_if_proc: nil)
11
+ end
12
+
13
+ describe "#initialize" do
14
+ context "when json_path is blank" do
15
+ let(:json_path) { ' ' }
16
+
17
+ it "raises an ArgumentError" do
18
+ expect { path_context }.to raise_error(ArgumentError, "`from` must be filled")
19
+ end
20
+ end
21
+
22
+ context "when transform is not a symbol or a valid transform key" do
23
+ let(:transform) { "not_a_symbol_or_valid_transform" }
24
+
25
+ it "raises an ArgumentError" do
26
+ expect do
27
+ path_context
28
+ end.to raise_error(ArgumentError,
29
+ "`transform`: 'not_a_symbol_or_valid_transform' must be one of [:iso8601, :date]")
30
+ end
31
+ end
32
+ end
33
+
34
+ describe "#parent" do
35
+ context "when the builder has a parent_path_context" do
36
+ let(:parent_path_context) { double("PathContext") }
37
+
38
+ before { allow(builder).to receive(:parent_path_context).and_return(parent_path_context) }
39
+
40
+ it "returns the parent_path_context" do
41
+ expect(path_context.parent).to eq(parent_path_context)
42
+ end
43
+ end
44
+
45
+ context "when the builder does not have a parent_path_context" do
46
+ before { allow(builder).to receive(:parent_path_context).and_return(nil) }
47
+
48
+ it "returns nil" do
49
+ expect(path_context.parent).to be_nil
50
+ end
51
+ end
52
+ end
53
+
54
+ describe "#defaults?" do
55
+ context "when defaults is an empty hash" do
56
+ let(:defaults) { {} }
57
+
58
+ it "returns false" do
59
+ expect(path_context.defaults?).to be false
60
+ end
61
+ end
62
+
63
+ context "when defaults is a non-empty hash" do
64
+ let(:defaults) { { baz: "qux" } }
65
+
66
+ it "returns true" do
67
+ expect(path_context.defaults?).to be true
68
+ end
69
+ end
70
+ end
71
+
72
+ describe "#transformable?" do
73
+ context "when transform is a Proc" do
74
+ let(:transform) { ->(val) { val.to_s.upcase } }
75
+
76
+ it "returns true" do
77
+ expect(path_context.transformable?).to be true
78
+ end
79
+ end
80
+
81
+ context "when transform is not a Proc" do
82
+ let(:transform) { nil }
83
+
84
+ it "returns false" do
85
+ expect(path_context.transformable?).to be false
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe JsonPathBuilder do
4
+ it "has a version number" do
5
+ expect(JsonPathBuilder::VERSION).not_to be_nil
6
+ end
7
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require File.join(__dir__, "..", 'dev', 'setup')
4
+ require Pathname.new(__dir__).realpath.join('coverage_helper').to_s
5
+
6
+ RSpec.configure do |config|
7
+ # Enable flags like --only-failures and --next-failure
8
+ config.example_status_persistence_file_path = ".rspec_status"
9
+
10
+ # Disable RSpec exposing methods globally on `Module` and `main`
11
+ config.disable_monkey_patching!
12
+
13
+ config.expect_with :rspec do |c|
14
+ c.syntax = :expect
15
+ end
16
+ end
metadata ADDED
@@ -0,0 +1,161 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: json-path-builder
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Desmond O'Leary
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-02-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '5'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '5'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rordash
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.1.2
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 0.1.2
41
+ - !ruby/object:Gem::Dependency
42
+ name: codecov
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 0.6.0
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 0.6.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 13.0.6
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 13.0.6
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 3.12.0
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 3.12.0
83
+ - !ruby/object:Gem::Dependency
84
+ name: simplecov
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 0.21.2
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 0.21.2
97
+ description: Declarative mapping JSON/Hash data structures
98
+ email:
99
+ - desoleary@gmail.com
100
+ executables: []
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - ".github/workflows/main.yml"
105
+ - ".gitignore"
106
+ - ".rspec"
107
+ - ".rubocop.yml"
108
+ - CHANGELOG.md
109
+ - CODE_OF_CONDUCT.md
110
+ - Gemfile
111
+ - LICENSE.txt
112
+ - README.md
113
+ - Rakefile
114
+ - bin/console
115
+ - bin/rspec
116
+ - bin/rubocop
117
+ - bin/setup
118
+ - dev/setup.rb
119
+ - json_path_builder.gemspec
120
+ - lib/json-path/builder.rb
121
+ - lib/json-path/default_data_wrapper.rb
122
+ - lib/json-path/path_context.rb
123
+ - lib/json-path/path_context_collection.rb
124
+ - lib/json-path/version.rb
125
+ - lib/json_path.rb
126
+ - sig/json_path_builder.rbs
127
+ - spec/coverage_helper.rb
128
+ - spec/json-path/builder_spec.rb
129
+ - spec/json-path/default_data_wrapper_spec.rb
130
+ - spec/json-path/path_context_collection_spec.rb
131
+ - spec/json-path/path_context_spec.rb
132
+ - spec/json_path_builder_spec.rb
133
+ - spec/spec_helper.rb
134
+ homepage: https://github.com/omnitech-solutions/json-path-builder
135
+ licenses:
136
+ - MIT
137
+ metadata:
138
+ homepage_uri: https://github.com/omnitech-solutions/json-path-builder
139
+ source_code_uri: https://github.com/omnitech-solutions/json-path-builder
140
+ changelog_uri: https://github.com/omnitech-solutions/json-path-builder/CHANGELOG.md
141
+ rubygems_mfa_required: 'true'
142
+ post_install_message:
143
+ rdoc_options: []
144
+ require_paths:
145
+ - lib
146
+ required_ruby_version: !ruby/object:Gem::Requirement
147
+ requirements:
148
+ - - ">="
149
+ - !ruby/object:Gem::Version
150
+ version: '2.7'
151
+ required_rubygems_version: !ruby/object:Gem::Requirement
152
+ requirements:
153
+ - - ">="
154
+ - !ruby/object:Gem::Version
155
+ version: '0'
156
+ requirements: []
157
+ rubygems_version: 3.1.6
158
+ signing_key:
159
+ specification_version: 4
160
+ summary: Declarative mapping JSON/Hash data structures
161
+ test_files: []