enumpath 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +15 -0
- data/.rspec +3 -0
- data/.travis.yml +5 -0
- data/.yardopts +1 -0
- data/CONTRIBUTING.md +91 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +56 -0
- data/LICENSE +201 -0
- data/README.md +492 -0
- data/Rakefile +11 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/enumpath.gemspec +32 -0
- data/lib/enumpath.rb +62 -0
- data/lib/enumpath/logger.rb +76 -0
- data/lib/enumpath/operator.rb +53 -0
- data/lib/enumpath/operator/base.rb +84 -0
- data/lib/enumpath/operator/child.rb +34 -0
- data/lib/enumpath/operator/filter_expression.rb +116 -0
- data/lib/enumpath/operator/recursive_descent.rb +52 -0
- data/lib/enumpath/operator/slice.rb +61 -0
- data/lib/enumpath/operator/subscript_expression.rb +84 -0
- data/lib/enumpath/operator/union.rb +42 -0
- data/lib/enumpath/operator/wildcard.rb +33 -0
- data/lib/enumpath/path.rb +73 -0
- data/lib/enumpath/path/normalized_path.rb +50 -0
- data/lib/enumpath/resolver/property.rb +23 -0
- data/lib/enumpath/resolver/simple.rb +33 -0
- data/lib/enumpath/results.rb +57 -0
- data/lib/enumpath/version.rb +5 -0
- metadata +185 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: f0c82457a7964b42629b6a3ea752d1cb9a3a383b41fde79d9fd249f25a73cdbf
|
4
|
+
data.tar.gz: 57353f0105eb540fc1bf9202811b80c5afba4c6268e00e5fa9d644521fc53911
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1c235bf4d538e646697f21f0411f1f4bf74e982957e1092dd05d41f6363bf37fd7bea5a74c3bca223f2e0521bad82f637f89b8eaff4332ae1354c546986e7af7
|
7
|
+
data.tar.gz: 39d33c4b50f464dfc8de6fe1b5b0d3edd8f528ca1714ff0b69e767aa09c5a4b13211ee78c9d07aad2e5c2f98e450a23c792bca915c2ddcafe5cf57831e1b243b
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/.yardopts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--no-private - LICENSE
|
data/CONTRIBUTING.md
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
# Contributing
|
2
|
+
|
3
|
+
Thanks for being interested in our project! We welcome [all kinds of contributions](https://opensource.guide/how-to-contribute/). There are a few things you should know before contributing to this repository:
|
4
|
+
|
5
|
+
1. All contributors must agree to our Contributor License Agreement before any merge requests will be accepted.
|
6
|
+
2. Please first discuss the change you wish to make via issue, email, or any other method with the owners of this repository before making a change.
|
7
|
+
3. Please note we have a code of conduct, please follow it in all your interactions with the project.
|
8
|
+
|
9
|
+
## Pull Request Process
|
10
|
+
|
11
|
+
1. Fork the repo.
|
12
|
+
2. Make sure the tests pass before you begin working.
|
13
|
+
3. Make your changes, with new passing tests. Follow the [Ruby Style Guide](https://github.com/rubocop-hq/ruby-style-guide).
|
14
|
+
4. Push to your fork. Write a [good commit message](https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html).
|
15
|
+
5. Submit a [good pull request](https://blog.github.com/2015-01-21-how-to-write-the-perfect-pull-request/).
|
16
|
+
6. Others will give constructive feedback. This is a time for discussion and improvements, and making the necessary changes will be required before we can merge the contribution.
|
17
|
+
|
18
|
+
## Code of Conduct
|
19
|
+
|
20
|
+
### Our Pledge
|
21
|
+
|
22
|
+
In the interest of fostering an open and welcoming environment, we as
|
23
|
+
contributors and maintainers pledge to making participation in our project and
|
24
|
+
our community a harassment-free experience for everyone, regardless of age, body
|
25
|
+
size, disability, ethnicity, sex characteristics, gender identity and expression,
|
26
|
+
level of experience, education, socio-economic status, nationality, personal
|
27
|
+
appearance, race, religion, or sexual identity and orientation.
|
28
|
+
|
29
|
+
### Our Standards
|
30
|
+
|
31
|
+
Examples of behavior that contributes to creating a positive environment
|
32
|
+
include:
|
33
|
+
|
34
|
+
* Using welcoming and inclusive language
|
35
|
+
* Being respectful of differing viewpoints and experiences
|
36
|
+
* Gracefully accepting constructive criticism
|
37
|
+
* Focusing on what is best for the community
|
38
|
+
* Showing empathy towards other community members
|
39
|
+
|
40
|
+
Examples of unacceptable behavior by participants include:
|
41
|
+
|
42
|
+
* The use of sexualized language or imagery and unwelcome sexual attention or
|
43
|
+
advances
|
44
|
+
* Trolling, insulting/derogatory comments, and personal or political attacks
|
45
|
+
* Public or private harassment
|
46
|
+
* Publishing others' private information, such as a physical or electronic
|
47
|
+
address, without explicit permission
|
48
|
+
* Other conduct which could reasonably be considered inappropriate in a
|
49
|
+
professional setting
|
50
|
+
|
51
|
+
### Our Responsibilities
|
52
|
+
|
53
|
+
Project maintainers are responsible for clarifying the standards of acceptable
|
54
|
+
behavior and are expected to take appropriate and fair corrective action in
|
55
|
+
response to any instances of unacceptable behavior.
|
56
|
+
|
57
|
+
Project maintainers have the right and responsibility to remove, edit, or
|
58
|
+
reject comments, commits, code, wiki edits, issues, and other contributions
|
59
|
+
that are not aligned to this Code of Conduct, or to ban temporarily or
|
60
|
+
permanently any contributor for other behaviors that they deem inappropriate,
|
61
|
+
threatening, offensive, or harmful.
|
62
|
+
|
63
|
+
### Scope
|
64
|
+
|
65
|
+
This Code of Conduct applies both within project spaces and in public spaces
|
66
|
+
when an individual is representing the project or its community. Examples of
|
67
|
+
representing a project or community include using an official project e-mail
|
68
|
+
address, posting via an official social media account, or acting as an appointed
|
69
|
+
representative at an online or offline event. Representation of a project may be
|
70
|
+
further defined and clarified by project maintainers.
|
71
|
+
|
72
|
+
### Enforcement
|
73
|
+
|
74
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
75
|
+
reported by contacting the project team at [INSERT EMAIL ADDRESS]. All
|
76
|
+
complaints will be reviewed and investigated and will result in a response that
|
77
|
+
is deemed necessary and appropriate to the circumstances. The project team is
|
78
|
+
obligated to maintain confidentiality with regard to the reporter of an incident.
|
79
|
+
Further details of specific enforcement policies may be posted separately.
|
80
|
+
|
81
|
+
Project maintainers who do not follow or enforce the Code of Conduct in good
|
82
|
+
faith may face temporary or permanent repercussions as determined by other
|
83
|
+
members of the project's leadership.
|
84
|
+
|
85
|
+
### Attribution
|
86
|
+
|
87
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
88
|
+
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
89
|
+
|
90
|
+
[homepage]: https://www.contributor-covenant.org
|
91
|
+
[version]: http://contributor-covenant.org/version/1/4/
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
enumpath (0.1.0)
|
5
|
+
mini_cache (~> 1.1.0)
|
6
|
+
to_regexp (~> 0.2.1)
|
7
|
+
|
8
|
+
GEM
|
9
|
+
remote: https://rubygems.org/
|
10
|
+
specs:
|
11
|
+
benchmark-perf (0.2.1)
|
12
|
+
byebug (10.0.2)
|
13
|
+
coderay (1.1.2)
|
14
|
+
diff-lcs (1.3)
|
15
|
+
method_source (0.9.0)
|
16
|
+
mini_cache (1.1.0)
|
17
|
+
null-logger (0.1.5)
|
18
|
+
pry (0.11.3)
|
19
|
+
coderay (~> 1.1.0)
|
20
|
+
method_source (~> 0.9.0)
|
21
|
+
pry-byebug (3.6.0)
|
22
|
+
byebug (~> 10.0)
|
23
|
+
pry (~> 0.10)
|
24
|
+
rake (12.3.1)
|
25
|
+
rspec (3.8.0)
|
26
|
+
rspec-core (~> 3.8.0)
|
27
|
+
rspec-expectations (~> 3.8.0)
|
28
|
+
rspec-mocks (~> 3.8.0)
|
29
|
+
rspec-benchmark (0.3.0)
|
30
|
+
benchmark-perf (~> 0.2.0)
|
31
|
+
rspec (>= 3.0.0, < 4.0.0)
|
32
|
+
rspec-core (3.8.0)
|
33
|
+
rspec-support (~> 3.8.0)
|
34
|
+
rspec-expectations (3.8.1)
|
35
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
36
|
+
rspec-support (~> 3.8.0)
|
37
|
+
rspec-mocks (3.8.0)
|
38
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
39
|
+
rspec-support (~> 3.8.0)
|
40
|
+
rspec-support (3.8.0)
|
41
|
+
to_regexp (0.2.1)
|
42
|
+
|
43
|
+
PLATFORMS
|
44
|
+
ruby
|
45
|
+
|
46
|
+
DEPENDENCIES
|
47
|
+
bundler (~> 1.16)
|
48
|
+
enumpath!
|
49
|
+
null-logger (~> 0.1)
|
50
|
+
pry-byebug (~> 3.6)
|
51
|
+
rake (~> 12.3)
|
52
|
+
rspec (~> 3.8)
|
53
|
+
rspec-benchmark (~> 0.3.0)
|
54
|
+
|
55
|
+
BUNDLED WITH
|
56
|
+
1.16.3
|
data/LICENSE
ADDED
@@ -0,0 +1,201 @@
|
|
1
|
+
Apache License
|
2
|
+
Version 2.0, January 2004
|
3
|
+
http://www.apache.org/licenses/
|
4
|
+
|
5
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
6
|
+
|
7
|
+
1. Definitions.
|
8
|
+
|
9
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
10
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
11
|
+
|
12
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
13
|
+
the copyright owner that is granting the License.
|
14
|
+
|
15
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
16
|
+
other entities that control, are controlled by, or are under common
|
17
|
+
control with that entity. For the purposes of this definition,
|
18
|
+
"control" means (i) the power, direct or indirect, to cause the
|
19
|
+
direction or management of such entity, whether by contract or
|
20
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
21
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
22
|
+
|
23
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
24
|
+
exercising permissions granted by this License.
|
25
|
+
|
26
|
+
"Source" form shall mean the preferred form for making modifications,
|
27
|
+
including but not limited to software source code, documentation
|
28
|
+
source, and configuration files.
|
29
|
+
|
30
|
+
"Object" form shall mean any form resulting from mechanical
|
31
|
+
transformation or translation of a Source form, including but
|
32
|
+
not limited to compiled object code, generated documentation,
|
33
|
+
and conversions to other media types.
|
34
|
+
|
35
|
+
"Work" shall mean the work of authorship, whether in Source or
|
36
|
+
Object form, made available under the License, as indicated by a
|
37
|
+
copyright notice that is included in or attached to the work
|
38
|
+
(an example is provided in the Appendix below).
|
39
|
+
|
40
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
41
|
+
form, that is based on (or derived from) the Work and for which the
|
42
|
+
editorial revisions, annotations, elaborations, or other modifications
|
43
|
+
represent, as a whole, an original work of authorship. For the purposes
|
44
|
+
of this License, Derivative Works shall not include works that remain
|
45
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
46
|
+
the Work and Derivative Works thereof.
|
47
|
+
|
48
|
+
"Contribution" shall mean any work of authorship, including
|
49
|
+
the original version of the Work and any modifications or additions
|
50
|
+
to that Work or Derivative Works thereof, that is intentionally
|
51
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
52
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
53
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
54
|
+
means any form of electronic, verbal, or written communication sent
|
55
|
+
to the Licensor or its representatives, including but not limited to
|
56
|
+
communication on electronic mailing lists, source code control systems,
|
57
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
58
|
+
Licensor for the purpose of discussing and improving the Work, but
|
59
|
+
excluding communication that is conspicuously marked or otherwise
|
60
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
61
|
+
|
62
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
63
|
+
on behalf of whom a Contribution has been received by Licensor and
|
64
|
+
subsequently incorporated within the Work.
|
65
|
+
|
66
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
67
|
+
this License, each Contributor hereby grants to You a perpetual,
|
68
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
69
|
+
copyright license to reproduce, prepare Derivative Works of,
|
70
|
+
publicly display, publicly perform, sublicense, and distribute the
|
71
|
+
Work and such Derivative Works in Source or Object form.
|
72
|
+
|
73
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
74
|
+
this License, each Contributor hereby grants to You a perpetual,
|
75
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
76
|
+
(except as stated in this section) patent license to make, have made,
|
77
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
78
|
+
where such license applies only to those patent claims licensable
|
79
|
+
by such Contributor that are necessarily infringed by their
|
80
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
81
|
+
with the Work to which such Contribution(s) was submitted. If You
|
82
|
+
institute patent litigation against any entity (including a
|
83
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
84
|
+
or a Contribution incorporated within the Work constitutes direct
|
85
|
+
or contributory patent infringement, then any patent licenses
|
86
|
+
granted to You under this License for that Work shall terminate
|
87
|
+
as of the date such litigation is filed.
|
88
|
+
|
89
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
90
|
+
Work or Derivative Works thereof in any medium, with or without
|
91
|
+
modifications, and in Source or Object form, provided that You
|
92
|
+
meet the following conditions:
|
93
|
+
|
94
|
+
(a) You must give any other recipients of the Work or
|
95
|
+
Derivative Works a copy of this License; and
|
96
|
+
|
97
|
+
(b) You must cause any modified files to carry prominent notices
|
98
|
+
stating that You changed the files; and
|
99
|
+
|
100
|
+
(c) You must retain, in the Source form of any Derivative Works
|
101
|
+
that You distribute, all copyright, patent, trademark, and
|
102
|
+
attribution notices from the Source form of the Work,
|
103
|
+
excluding those notices that do not pertain to any part of
|
104
|
+
the Derivative Works; and
|
105
|
+
|
106
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
107
|
+
distribution, then any Derivative Works that You distribute must
|
108
|
+
include a readable copy of the attribution notices contained
|
109
|
+
within such NOTICE file, excluding those notices that do not
|
110
|
+
pertain to any part of the Derivative Works, in at least one
|
111
|
+
of the following places: within a NOTICE text file distributed
|
112
|
+
as part of the Derivative Works; within the Source form or
|
113
|
+
documentation, if provided along with the Derivative Works; or,
|
114
|
+
within a display generated by the Derivative Works, if and
|
115
|
+
wherever such third-party notices normally appear. The contents
|
116
|
+
of the NOTICE file are for informational purposes only and
|
117
|
+
do not modify the License. You may add Your own attribution
|
118
|
+
notices within Derivative Works that You distribute, alongside
|
119
|
+
or as an addendum to the NOTICE text from the Work, provided
|
120
|
+
that such additional attribution notices cannot be construed
|
121
|
+
as modifying the License.
|
122
|
+
|
123
|
+
You may add Your own copyright statement to Your modifications and
|
124
|
+
may provide additional or different license terms and conditions
|
125
|
+
for use, reproduction, or distribution of Your modifications, or
|
126
|
+
for any such Derivative Works as a whole, provided Your use,
|
127
|
+
reproduction, and distribution of the Work otherwise complies with
|
128
|
+
the conditions stated in this License.
|
129
|
+
|
130
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
131
|
+
any Contribution intentionally submitted for inclusion in the Work
|
132
|
+
by You to the Licensor shall be under the terms and conditions of
|
133
|
+
this License, without any additional terms or conditions.
|
134
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
135
|
+
the terms of any separate license agreement you may have executed
|
136
|
+
with Licensor regarding such Contributions.
|
137
|
+
|
138
|
+
6. Trademarks. This License does not grant permission to use the trade
|
139
|
+
names, trademarks, service marks, or product names of the Licensor,
|
140
|
+
except as required for reasonable and customary use in describing the
|
141
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
142
|
+
|
143
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
144
|
+
agreed to in writing, Licensor provides the Work (and each
|
145
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
146
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
147
|
+
implied, including, without limitation, any warranties or conditions
|
148
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
149
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
150
|
+
appropriateness of using or redistributing the Work and assume any
|
151
|
+
risks associated with Your exercise of permissions under this License.
|
152
|
+
|
153
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
154
|
+
whether in tort (including negligence), contract, or otherwise,
|
155
|
+
unless required by applicable law (such as deliberate and grossly
|
156
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
157
|
+
liable to You for damages, including any direct, indirect, special,
|
158
|
+
incidental, or consequential damages of any character arising as a
|
159
|
+
result of this License or out of the use or inability to use the
|
160
|
+
Work (including but not limited to damages for loss of goodwill,
|
161
|
+
work stoppage, computer failure or malfunction, or any and all
|
162
|
+
other commercial damages or losses), even if such Contributor
|
163
|
+
has been advised of the possibility of such damages.
|
164
|
+
|
165
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
166
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
167
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
168
|
+
or other liability obligations and/or rights consistent with this
|
169
|
+
License. However, in accepting such obligations, You may act only
|
170
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
171
|
+
of any other Contributor, and only if You agree to indemnify,
|
172
|
+
defend, and hold each Contributor harmless for any liability
|
173
|
+
incurred by, or claims asserted against, such Contributor by reason
|
174
|
+
of your accepting any such warranty or additional liability.
|
175
|
+
|
176
|
+
END OF TERMS AND CONDITIONS
|
177
|
+
|
178
|
+
APPENDIX: How to apply the Apache License to your work.
|
179
|
+
|
180
|
+
To apply the Apache License to your work, attach the following
|
181
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
182
|
+
replaced with your own identifying information. (Don't include
|
183
|
+
the brackets!) The text should be enclosed in the appropriate
|
184
|
+
comment syntax for the file format. We also recommend that a
|
185
|
+
file or class name and description of purpose be included on the
|
186
|
+
same "printed page" as the copyright notice for easier
|
187
|
+
identification within third-party archives.
|
188
|
+
|
189
|
+
Copyright [yyyy] [name of copyright owner]
|
190
|
+
|
191
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
192
|
+
you may not use this file except in compliance with the License.
|
193
|
+
You may obtain a copy of the License at
|
194
|
+
|
195
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
196
|
+
|
197
|
+
Unless required by applicable law or agreed to in writing, software
|
198
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
199
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
200
|
+
See the License for the specific language governing permissions and
|
201
|
+
limitations under th
|
data/README.md
ADDED
@@ -0,0 +1,492 @@
|
|
1
|
+
# Enumpath
|
2
|
+
|
3
|
+
A JSONPath-compatible library for safely navigating nested Ruby objects using path expressions.
|
4
|
+
|
5
|
+
## Introduction
|
6
|
+
|
7
|
+
Enumpath is an implementation of the [JSONPath][jsonpath] spec for Ruby objects, plus some added sugar. It's like Ruby's native `Enumerable#dig` method, but fancier. It is designed for situations where you need to provide a dynamic way of describing a complex path through nested enumerable objects. This makes it exceptionally well suited for flexible ETL (Extract, Transform, Load) processes by allowing you to define paths through your data in a simple, easily readable, easily storable syntax.
|
8
|
+
|
9
|
+
Enumpath path expressions look like this:
|
10
|
+
|
11
|
+
```
|
12
|
+
$.pets.cats.0.name
|
13
|
+
|
14
|
+
$.pets[cats,dogs].*.name
|
15
|
+
|
16
|
+
pets..name
|
17
|
+
|
18
|
+
['pets']..[?(@.age > 10)].name
|
19
|
+
|
20
|
+
..age
|
21
|
+
|
22
|
+
pets.cats.-1
|
23
|
+
```
|
24
|
+
|
25
|
+
Enumpath has the following benefits over vanilla `Enumerable#dig`:
|
26
|
+
|
27
|
+
- Paths can be described as simple strings
|
28
|
+
- It's smart enough to figure out which path segments are integer indexes versus symbolic keys versus string keys
|
29
|
+
- It enables the use of wildcard, recursive descent, filter, subscript, union, and slice operators to describe complex paths through the data
|
30
|
+
|
31
|
+
Like `Enumerable#dig`, Enumpath protects against missing path segments and returns safely if the full path cannot be resolved.
|
32
|
+
|
33
|
+
## Installation
|
34
|
+
|
35
|
+
Add this line to your application's Gemfile:
|
36
|
+
|
37
|
+
```ruby
|
38
|
+
gem 'enumpath'
|
39
|
+
```
|
40
|
+
|
41
|
+
And then execute:
|
42
|
+
|
43
|
+
$ bundle
|
44
|
+
|
45
|
+
Or install it yourself as:
|
46
|
+
|
47
|
+
$ gem install enumpath
|
48
|
+
|
49
|
+
## Usage
|
50
|
+
|
51
|
+
Enumpath exposes a simple interface via `Enumpath.apply` that takes a path and an enumerable.
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
party = { food: %w(pizza tacos) }
|
55
|
+
Enumpath.apply('food.0', party) # => ["pizza"]
|
56
|
+
```
|
57
|
+
|
58
|
+
The result of `Enumpath.apply` is an array of values that were extracted according to the path. Technically it's an instance of `Enumpath::Results` which is an Array-like object that allows you to chain further calls to `.apply` like this:
|
59
|
+
|
60
|
+
```ruby
|
61
|
+
party = { food: %w(pizza tacos) }
|
62
|
+
results = Enumpath.apply('food.*', party) # => ["pizza", "tacos"]
|
63
|
+
results.apply("[?(@ == 'pizza')]") # => ["pizza"]
|
64
|
+
```
|
65
|
+
|
66
|
+
In the event that the path doesn't match anything, an empty results set is returned:
|
67
|
+
|
68
|
+
```ruby
|
69
|
+
party = { food: %w(pizza tacos) }
|
70
|
+
Enumpath.apply("drinks.*", party) # => []
|
71
|
+
```
|
72
|
+
|
73
|
+
> This is a thoughtful deviation from the original JSONPath spec which would return `false` on no matches.
|
74
|
+
|
75
|
+
## Operator Reference
|
76
|
+
|
77
|
+
Enumpath currently implements the following path operators:
|
78
|
+
|
79
|
+
operator | summary | basic examples
|
80
|
+
:---: | --- | ---
|
81
|
+
`$` | **Root**; only valid at the beginning of a path and it is entirely optional | `$.puppies` is equivalent to `puppies`
|
82
|
+
`.` or `[]` | **[Child](#child-operator)**; can be dot notation or bracket, and bracketed child operators can optionally be wrapped in single quotes | `locations`, `0`, `[departments]`, and `['human resources']` are all valid child operators
|
83
|
+
`*` | **[Wildcard](#wildcard-operator)**; applies the remaining path to each member of the current enumerable | `children.*.name` would result in an array containing the names of all the children
|
84
|
+
`..` | **[Recursive descent](#recursive-descent-operator)**; applies the remaining path to all members at every level of the current enumerable, including to the current enumerable itself | `..name` would find all the `name` members all the way down through the enumerable regardless of nesting level
|
85
|
+
`[start:end:step]` | **[Slice](#slice-operator)**; similar in functionality to Ruby's `Array#slice` method with the addition of a step argument | `[1:8:2]` would operate on indices 1, 3, 5, and 7
|
86
|
+
`[child1,child2[,...]]` | **[Union](#union-operator)**; combines the results from multiple child operators | `authors.*[first_name,last_name]` is equivalent to `authors.*.first_name` + `authors.*.last_name`
|
87
|
+
`?()` | **[Filter expression](#filter-expression-operator)**; evaluates boolean expressions against the current enumerable; only the members of enumerable that meet the criteria are passed through | `[?(@.price > 10 && @.price <= 20 )]` would return all items whose price is greater than 10 and less than or equal to 20
|
88
|
+
`()` | **[Subscript expression](#subscript-expression-operator)**; evaluates an expression as a subscript on the current enumerator | `[(@.length - 1)]` would apply a child operator equal to the `#length` of the current enumerable minus 1 to the current enumerable (i.e. the last member of an array)
|
89
|
+
|
90
|
+
### Child operator
|
91
|
+
|
92
|
+
Syntax: `child` or `[child]` or `['child']`
|
93
|
+
|
94
|
+
Child operators match on an index, key, member, or property of the enumerable. In its non-normalized form a child operator is preceded by `.` or wrapped in '[]'. In bracket notation the child may optionally be wrapped in single quotes. Enumpath will attempt to resolve the data type of the child operator in the following order of precedence:
|
95
|
+
|
96
|
+
1. as an integer key or index (if the segment is integer-like),
|
97
|
+
2. then as a string key,
|
98
|
+
3. then as a symbol key,
|
99
|
+
4. and finally as a public property (i.e. a public method of the target that expects no arguments)
|
100
|
+
|
101
|
+
#### Examples
|
102
|
+
|
103
|
+
```
|
104
|
+
Car = Struct.new(:color, :transmition, :owners)
|
105
|
+
hiundai = Car.new('blue', :automatic, [{ name: 'Bill' }, { name: 'Ted' }])
|
106
|
+
subaru = Car.new('gold', :standard, [{ name: 'Kate' }])
|
107
|
+
jeep = Car.new('black', :automatic, [])
|
108
|
+
garages = [{ 'cars' => [hiundai, subaru] }, { 'cars' => [jeep] }]
|
109
|
+
Enumpath.apply('1', garages) # => [{"cars"=>[#<struct Car color="black", transmition=:automatic, owners=[]>]}]
|
110
|
+
Enumpath.apply('0.cars.-1', garages) # => [#<struct Car color="gold", transmition=:standard, owners=[{:name=>"Kate"}]>]
|
111
|
+
Enumpath.apply('1.cars.0.owners.length', garages) # => [0]
|
112
|
+
```
|
113
|
+
|
114
|
+
### Wildcard operator
|
115
|
+
|
116
|
+
Syntax: `*` or `[*]`
|
117
|
+
|
118
|
+
Wildcards match each immediate member of the enumerable.
|
119
|
+
|
120
|
+
### Recursive descent operator
|
121
|
+
|
122
|
+
Syntax: `..`
|
123
|
+
|
124
|
+
Applies the remaining path expression segments recursively to all members of the enumerable regardless of their nesting level, including the enumerable itself.
|
125
|
+
|
126
|
+
### Slice operator
|
127
|
+
|
128
|
+
Syntax: `[start:end:step]`, `[start:]`, `[start:end]`, `[start::step]`, `[:end]`, `[:end:step]`, `[::step]`, ...
|
129
|
+
|
130
|
+
The slice operator selects a range of elements like Ruby's _`start...end`_ literal, excluding the end value, and then selects each _step_ items. The _start_, _end_, and _step_ arguments default to `0`, `Enumerable#length`, and `1` respectively. The remaining path expression segments are passed to each member whose index is included by the slice operator.
|
131
|
+
|
132
|
+
The operator accepts a mixed bag of argument combinations. For instance, these are all valid slice operators:
|
133
|
+
|
134
|
+
- `[1:8]`: passes through the members of the enumerable at indices 1 – 7
|
135
|
+
- `[1:]`: passes through the members of the enumerable at indices 1 – `Enumerable#length`
|
136
|
+
- `[:8]`: passes through the members of the enumerable at indices 0 – 7
|
137
|
+
- `[:8:2]`: passes through the members of the enumerable at indices 0, 2, 4, and 6
|
138
|
+
- `[::2]`: passes through the members of the enumerable at indices 0, 2, 4, 6, 8, ... up to `Enumerable#length`
|
139
|
+
|
140
|
+
### Union operator
|
141
|
+
|
142
|
+
Syntax: `[child1,child2,...]`
|
143
|
+
|
144
|
+
The union operator combines the results of two or more child operators. There is no limit to the number of child operators you can specify in a single union. Each child operator is separated by a comma (`,`). White space is stripped from around each child operator. Child operators can optionally be wrapped in single quotes. Bracket notation is not supporter in this context.
|
145
|
+
|
146
|
+
The following are all valid union operators:
|
147
|
+
|
148
|
+
- `[first,last]`
|
149
|
+
- `[first,middle,last]`
|
150
|
+
- `['first', middle , last]`
|
151
|
+
|
152
|
+
### Filter expression operator
|
153
|
+
|
154
|
+
Syntax: `[?(expression)]`, `[?(expression && expression)]`, `[?(expression || expression)]`, ...
|
155
|
+
|
156
|
+
A filter expression is made up of one or more boolean expressions. Each boolean expression consists of a child operator (`@.child` or `child`; the leading `@.` is optional), plus an optional pair of comparison operator and operand. The comparison operator can be any one of `==`, `!=`, `>=`, `<=`, `<=>`, `>`, `<`, `=~`, `!~`. The operand can be a string (`'some string'`), symbol (`:some_symbol`), boolean constant (`true` or `false`), nil constant (`nil`), regular expression (`/^Some\s+/i`), or numeric value (`10` or `1.0`). If an operator and operand are not included in an expression then the value located at the child operator is evaluated for truthiness. Multiple expression groups can be chained together with `&&` or `||` logical operators, but note that parenthetical grouping of expressions is not supported; the results of each are applied to the previous running result in order. Any member of the current enumerable that passes the net result of the filter expression will be included in further processing of the path.
|
157
|
+
|
158
|
+
The following are all valid filter expressions:
|
159
|
+
|
160
|
+
- `[?(@.isbn)]`: any member who has an `isbn` value that is not falsey
|
161
|
+
- `[?(isbn)]`: equivalent to the previous example
|
162
|
+
- `[?(@.price == 8)]`: any member whose `price` value is equal to 8
|
163
|
+
- `[?(@.price == 8 || @.price == 10)]`: members with a `price` of 8 _or_ 10
|
164
|
+
- `[?(@.price > 2 && @.price < 10)]`: members with a `price` greater than 8 _and_ less than 10
|
165
|
+
- `[?(@.name =~ /bob/i || @.name == 'Mark')]`: any member whose `name` matches the regex `/bob/i` or equals `'Mark'`
|
166
|
+
|
167
|
+
> Regular expression operands are safely parsed using the `to_regexp` gem
|
168
|
+
|
169
|
+
### Subscript expression operator
|
170
|
+
|
171
|
+
Syntax: `[(expression)]`
|
172
|
+
|
173
|
+
A subscript expression is made up of a singe expression that consists of a child operator (`@.child` or `child`; the leading `@.` is optional), plus an optional pair of arithmetic operator and operand. The arithmetic operator can be any one of `+`, `-`, `**`, `*`, `/`, or `%`. The operand can be a string (`'some string'`), symbol (`:some_symbol`), or numeric value (`10` or `1.0`). The expression is evaluated and the result becomes the subscript. If an operator and operand are not included in an expression then the value located at the child operator is used as the subscript. If the subscript represents a valid child path for the enumerable, the value of that member will be passed along for further processing of the path.
|
174
|
+
|
175
|
+
The following are all valid filter expressions:
|
176
|
+
|
177
|
+
- `[(@.length - 1)]`: the subscript becomes the length of the current enumerable, minus 1
|
178
|
+
- `[(length / 2)]`: the subscript becomes the index at half the length of the enumerable
|
179
|
+
- `[(@.type)]`: the subscript becomes the value at the `type` key, member, or property of the enumerable
|
180
|
+
|
181
|
+
## Examples
|
182
|
+
|
183
|
+
Given the same store example from the JSONPath project:
|
184
|
+
|
185
|
+
```ruby
|
186
|
+
store_info = {
|
187
|
+
store: {
|
188
|
+
book: [
|
189
|
+
{ category: "reference",
|
190
|
+
author: "Nigel Rees",
|
191
|
+
title: "Sayings of the Century",
|
192
|
+
price: 8.95 },
|
193
|
+
{ category: "fiction",
|
194
|
+
author: "Evelyn Waugh",
|
195
|
+
title: "Sword of Honour",
|
196
|
+
price: 12.99 },
|
197
|
+
{ category: "fiction",
|
198
|
+
author: "Herman Melville",
|
199
|
+
title: "Moby Dick",
|
200
|
+
isbn: "0-553-21311-3",
|
201
|
+
price: 8.99 },
|
202
|
+
{ category: "fiction",
|
203
|
+
author: "J. R. R. Tolkien",
|
204
|
+
title: "The Lord of the Rings",
|
205
|
+
isbn: "0-395-19395-8",
|
206
|
+
price: 22.99 }
|
207
|
+
],
|
208
|
+
bicycle: { color: "red", price: 19.95 }
|
209
|
+
}
|
210
|
+
}
|
211
|
+
|
212
|
+
# The authors of all the books in the store
|
213
|
+
Enumpath.apply("$.store.book[*].author", store_info)
|
214
|
+
# => ["Nigel Rees", "Evelyn Waugh", "Herman Melville", "J. R. R. Tolkien"]
|
215
|
+
|
216
|
+
# All prices in the store
|
217
|
+
Enumpath.apply("$..price", store_info)
|
218
|
+
# => [8.95, 12.99, 8.99, 22.99, 19.95]
|
219
|
+
|
220
|
+
# All things in store, which are some books and a red bicycle
|
221
|
+
Enumpath.apply("$.store.*", store_info)
|
222
|
+
# => [[{:category=>"reference",
|
223
|
+
# :author=>"Nigel Rees",
|
224
|
+
# :title=>"Sayings of the Century",
|
225
|
+
# :price=>8.95},
|
226
|
+
# {:category=>"fiction",
|
227
|
+
# :author=>"Evelyn Waugh",
|
228
|
+
# :title=>"Sword of Honour",
|
229
|
+
# :price=>12.99},
|
230
|
+
# {:category=>"fiction",
|
231
|
+
# :author=>"Herman Melville",
|
232
|
+
# :title=>"Moby Dick",
|
233
|
+
# :isbn=>"0-553-21311-3",
|
234
|
+
# :price=>8.99},
|
235
|
+
# {:category=>"fiction",
|
236
|
+
# :author=>"J. R. R. Tolkien",
|
237
|
+
# :title=>"The Lord of the Rings",
|
238
|
+
# :isbn=>"0-395-19395-8",
|
239
|
+
# :price=>22.99}],
|
240
|
+
# {:color=>"red", :price=>19.95}]
|
241
|
+
|
242
|
+
# The third book
|
243
|
+
Enumpath.apply("$..book[2]", store_info)
|
244
|
+
# => [{:category=>"fiction", :author=>"Herman Melville", :title=>"Moby Dick", :isbn=>"0-553-21311-3", :price=>8.99}]
|
245
|
+
|
246
|
+
# The last book in order
|
247
|
+
Enumpath.apply("$..book[(@.length-1)]", store_info)
|
248
|
+
Enumpath.apply("$..book[-1:]", store_info)
|
249
|
+
# => Both return: [{:category=>"fiction", :author=>"J. R. R. Tolkien", :title=>"The Lord of the Rings", :isbn=>"0-395-19395-8", :price=>22.99}]
|
250
|
+
|
251
|
+
# The first two books in order. Both of these path expressions are equivalent
|
252
|
+
Enumpath.apply("$..book[0,1]", store_info)
|
253
|
+
Enumpath.apply("$..book[:2]", store_info)
|
254
|
+
# => [{:category=>"reference",
|
255
|
+
# :author=>"Nigel Rees",
|
256
|
+
# :title=>"Sayings of the Century",
|
257
|
+
# :price=>8.95},
|
258
|
+
# {:category=>"fiction",
|
259
|
+
# :author=>"Evelyn Waugh",
|
260
|
+
# :title=>"Sword of Honour",
|
261
|
+
# :price=>12.99}]
|
262
|
+
|
263
|
+
# All books with an isbn number
|
264
|
+
Enumpath.apply("$..book[?(@.isbn)]", store_info)
|
265
|
+
# => [{:category=>"fiction",
|
266
|
+
# :author=>"Herman Melville",
|
267
|
+
# :title=>"Moby Dick",
|
268
|
+
# :isbn=>"0-553-21311-3",
|
269
|
+
# :price=>8.99},
|
270
|
+
# {:category=>"fiction",
|
271
|
+
# :author=>"J. R. R. Tolkien",
|
272
|
+
# :title=>"The Lord of the Rings",
|
273
|
+
# :isbn=>"0-395-19395-8",
|
274
|
+
# :price=>22.99}]
|
275
|
+
|
276
|
+
# All books with a price less than 10
|
277
|
+
Enumpath.apply("$..book[?(@.price<10)]", store_info)
|
278
|
+
# => [{:category=>"reference",
|
279
|
+
# :author=>"Nigel Rees",
|
280
|
+
# :title=>"Sayings of the Century",
|
281
|
+
# :price=>8.95},
|
282
|
+
# {:category=>"fiction",
|
283
|
+
# :author=>"Herman Melville",
|
284
|
+
# :title=>"Moby Dick",
|
285
|
+
# :isbn=>"0-553-21311-3",
|
286
|
+
# :price=>8.99}]
|
287
|
+
|
288
|
+
# All members of the enumerable, recursively
|
289
|
+
Enumpath.apply("$..*", store_info)
|
290
|
+
# => [{:book=>
|
291
|
+
# [{:category=>"reference",
|
292
|
+
# :author=>"Nigel Rees",
|
293
|
+
# :title=>"Sayings of the Century",
|
294
|
+
# :price=>8.95},
|
295
|
+
# {:category=>"fiction",
|
296
|
+
# :author=>"Evelyn Waugh",
|
297
|
+
# :title=>"Sword of Honour",
|
298
|
+
# :price=>12.99},
|
299
|
+
# {:category=>"fiction",
|
300
|
+
# :author=>"Herman Melville",
|
301
|
+
# :title=>"Moby Dick",
|
302
|
+
# :isbn=>"0-553-21311-3",
|
303
|
+
# :price=>8.99},
|
304
|
+
# {:category=>"fiction",
|
305
|
+
# :author=>"J. R. R. Tolkien",
|
306
|
+
# :title=>"The Lord of the Rings",
|
307
|
+
# :isbn=>"0-395-19395-8",
|
308
|
+
# :price=>22.99}],
|
309
|
+
# :bicycle=>{:color=>"red", :price=>19.95}},
|
310
|
+
# [{:category=>"reference",
|
311
|
+
# :author=>"Nigel Rees",
|
312
|
+
# :title=>"Sayings of the Century",
|
313
|
+
# :price=>8.95},
|
314
|
+
# {:category=>"fiction",
|
315
|
+
# :author=>"Evelyn Waugh",
|
316
|
+
# :title=>"Sword of Honour",
|
317
|
+
# :price=>12.99},
|
318
|
+
# {:category=>"fiction",
|
319
|
+
# :author=>"Herman Melville",
|
320
|
+
# :title=>"Moby Dick",
|
321
|
+
# :isbn=>"0-553-21311-3",
|
322
|
+
# :price=>8.99},
|
323
|
+
# {:category=>"fiction",
|
324
|
+
# :author=>"J. R. R. Tolkien",
|
325
|
+
# :title=>"The Lord of the Rings",
|
326
|
+
# :isbn=>"0-395-19395-8",
|
327
|
+
# :price=>22.99}],
|
328
|
+
# {:color=>"red", :price=>19.95},
|
329
|
+
# {:category=>"reference",
|
330
|
+
# :author=>"Nigel Rees",
|
331
|
+
# :title=>"Sayings of the Century",
|
332
|
+
# :price=>8.95},
|
333
|
+
# {:category=>"fiction",
|
334
|
+
# :author=>"Evelyn Waugh",
|
335
|
+
# :title=>"Sword of Honour",
|
336
|
+
# :price=>12.99},
|
337
|
+
# {:category=>"fiction",
|
338
|
+
# :author=>"Herman Melville",
|
339
|
+
# :title=>"Moby Dick",
|
340
|
+
# :isbn=>"0-553-21311-3",
|
341
|
+
# :price=>8.99},
|
342
|
+
# {:category=>"fiction",
|
343
|
+
# :author=>"J. R. R. Tolkien",
|
344
|
+
# :title=>"The Lord of the Rings",
|
345
|
+
# :isbn=>"0-395-19395-8",
|
346
|
+
# :price=>22.99},
|
347
|
+
# "reference",
|
348
|
+
# "Nigel Rees",
|
349
|
+
# "Sayings of the Century",
|
350
|
+
# 8.95,
|
351
|
+
# "fiction",
|
352
|
+
# "Evelyn Waugh",
|
353
|
+
# "Sword of Honour",
|
354
|
+
# 12.99,
|
355
|
+
# "fiction",
|
356
|
+
# "Herman Melville",
|
357
|
+
# "Moby Dick",
|
358
|
+
# "0-553-21311-3",
|
359
|
+
# 8.99,
|
360
|
+
# "fiction",
|
361
|
+
# "J. R. R. Tolkien",
|
362
|
+
# "The Lord of the Rings",
|
363
|
+
# "0-395-19395-8",
|
364
|
+
# 22.99,
|
365
|
+
# "red",
|
366
|
+
# 19.95]
|
367
|
+
```
|
368
|
+
|
369
|
+
## Options
|
370
|
+
|
371
|
+
### :return_type
|
372
|
+
|
373
|
+
By default, Enumpath returns the values that match the path expression. Like the original JSONPath implementation, Enumpath also supports returning path results instead of values. This can be useful for collecting static paths from dynamic paths.
|
374
|
+
|
375
|
+
```ruby
|
376
|
+
party = { food: %w(pizza tacos) }
|
377
|
+
Enumpath.apply("food.*", party, result_type: :path) # => ["$['food'][0]", "$['food'][1]"]
|
378
|
+
```
|
379
|
+
|
380
|
+
Each returned path is a valid path expression that can be used in calls to `Enumpath.apply`. If you want to be explicit about returning values instead of paths you can specify that with the option `result_type: :value`.
|
381
|
+
|
382
|
+
### :verbose
|
383
|
+
|
384
|
+
Seeing how your path expression is being applied to an enumerable can be helpful in understanding the path expression syntax. Enumpath has a built-in logger to assist with this. It can be enabled by simply passing `verbose: true` as an option on `Enumpath.apply`. By default this will log debugging information to STDOUT, however you can provide your own logger.
|
385
|
+
|
386
|
+
For example:
|
387
|
+
|
388
|
+
```ruby
|
389
|
+
Enumpath.logger.logger = ::Logger.new('log/enumpath.log')
|
390
|
+
```
|
391
|
+
|
392
|
+
Once enabled, it will log debugging information like so:
|
393
|
+
|
394
|
+
```
|
395
|
+
Enumpath.apply('$.store.book', store_info, verbose: true)
|
396
|
+
|
397
|
+
--------------------------------------
|
398
|
+
Enumpath: Path normalized
|
399
|
+
--------------------------------------
|
400
|
+
original : $.store.book
|
401
|
+
normalized: ["store", "book"]
|
402
|
+
--------------------------------------
|
403
|
+
Enumpath: Applying
|
404
|
+
--------------------------------------
|
405
|
+
operator: ["store", "book"]
|
406
|
+
to : {:store=>{:book=>[{:category=>"reference", :author...
|
407
|
+
--------------------------------------
|
408
|
+
Enumpath: Child operator detected
|
409
|
+
--------------------------------------
|
410
|
+
Enumpath: Applying
|
411
|
+
--------------------------------------
|
412
|
+
operator: ["book"]
|
413
|
+
to : {:book=>[{:category=>"reference", :author=>"Nigel ...
|
414
|
+
--------------------------------------
|
415
|
+
Enumpath: Child operator detected
|
416
|
+
--------------------------------------
|
417
|
+
Enumpath: Storing
|
418
|
+
--------------------------------------
|
419
|
+
resolved_path: ["store", "book"]
|
420
|
+
enum : [{:category=>"reference", :author=>"Nigel Rees", :...
|
421
|
+
--------------------------------------
|
422
|
+
Enumpath: New Result
|
423
|
+
--------------------------------------
|
424
|
+
result: [{:category=>"reference", :author=>"Nigel Rees", :...
|
425
|
+
```
|
426
|
+
|
427
|
+
You can also control verbose mode via `Enumpath.verbose = true` and `Enumpath.verbose = false`.
|
428
|
+
|
429
|
+
## Path normalization
|
430
|
+
|
431
|
+
When you give a string path to Enumpath it will automatically normalize it to an array of path segments. You can also pass it an array of path segments to avoid the normalization, for instance if the normalization process is having trouble parsing your path, or you happen to have a pre-normalized path already. For example the path `['pets']..[?(@.age > 10)].name` is represented in normalized form as `['pets', '..', '?(@.age > 10)', 'name']`. For the most part you should stick with string paths and let Enumpath normalize on its own.
|
432
|
+
|
433
|
+
### Normalized path caching
|
434
|
+
|
435
|
+
To save a little bit of time on consecutive calls Enumpath caches the normalized version of each path. This is an implementation detail that can generally be ignored but if you run into trouble with it you can clear the cache with `Enumpath.path_cache.reset`.
|
436
|
+
|
437
|
+
## Deviations from the Original JSONPath Spec
|
438
|
+
|
439
|
+
1. The JSONPath spec required that `false` be returned when no matches were found, but Enumpath will return an empty result set (`[]`) instead. This is a thoughtful divergence based on the principle of least astonishment and the robustness principle.
|
440
|
+
|
441
|
+
2. Enumpath supports relative child indexes, which the original implementation did not support. For instance:
|
442
|
+
|
443
|
+
# Get the last element. Both are equivalent to `$..book[-1:]`
|
444
|
+
Enumpath.apply('$..book.-1', store_info)
|
445
|
+
Enumpath.apply('$..book[-1]', store_info)
|
446
|
+
|
447
|
+
3. The original implementations of JSONPath allowed unchecked evaluation of filter and subscript expressions. Enumpath limits those expressions to a reasonable subset of operations as detailed in the [Operator Reference](#operator-reference) section and uses `public_send` rather than `eval` to resolve expressions as necessary.
|
448
|
+
|
449
|
+
4. The original JSONPath spec did not include support for using logical operators to chain expressions in filter expression operators. This addition was inspired by [Gergely Brautigam's](https://skarlso.github.io/2017/05/28/replace-eval-with-object-send-and-a-parser/) work on [joshbuddy/jsonpath](https://github.com/joshbuddy/jsonpath)
|
450
|
+
|
451
|
+
## Requirements
|
452
|
+
|
453
|
+
Enumpath requires Ruby 2.3.0 or higher.
|
454
|
+
|
455
|
+
## Development
|
456
|
+
|
457
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rspec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
458
|
+
|
459
|
+
## Contributing
|
460
|
+
|
461
|
+
Bug reports and pull requests are welcome on GitHub at [https://github.com/youearnedit/enumpath](). Please read [CONTRIBUTING.md]() for details on our code of conduct, and the process for submitting pull requests to us.
|
462
|
+
|
463
|
+
## Versioning
|
464
|
+
|
465
|
+
We use [SemVer](http://semver.org/) for versioning. For the versions available, see the [tags](https://github.com/youearnedit/enumpath/tags) on this repository.
|
466
|
+
|
467
|
+
## Authors
|
468
|
+
|
469
|
+
- [Chris Bloom](https://github.com/chrisbloom7) - YouEarnedIt
|
470
|
+
|
471
|
+
See also the list of [contributors](https://github.com/youearnedit/enumpath/graphs/contributors) who participated in this project.
|
472
|
+
|
473
|
+
## License
|
474
|
+
|
475
|
+
Copyright 2018 YouEarnedIt.com
|
476
|
+
|
477
|
+
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
|
478
|
+
|
479
|
+
[www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0)
|
480
|
+
|
481
|
+
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
|
482
|
+
|
483
|
+
## Acknowledgements
|
484
|
+
|
485
|
+
This project is maintained by the Engineering team at [YouEarnedIt](http://youearnedit.com), an employee engagement and performance metrics platform, headquartered in Austin, TX.
|
486
|
+
|
487
|
+
Enumpath is based on [Stefan Goessner's JSONPath spec][jsonpath], and was inspired by several similar libraries:
|
488
|
+
|
489
|
+
- [nickcharlton/keypath-ruby](https://github.com/nickcharlton/keypath-ruby)
|
490
|
+
- [joshbuddy/jsonpath](https://github.com/joshbuddy/jsonpath)
|
491
|
+
|
492
|
+
[jsonpath]: http://goessner.net/articles/JsonPath/
|