scripper 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +14 -13
- data/.rspec +3 -3
- data/.rubocop.yml +27 -27
- data/.travis.yml +20 -20
- data/Gemfile +5 -5
- data/LICENSE +21 -21
- data/README.md +164 -164
- data/Rakefile +8 -8
- data/lib/scripper.rb +8 -8
- data/lib/scripper/sequel.rb +25 -25
- data/lib/scripper/sequel/dataset_stripper.rb +14 -14
- data/lib/scripper/sequel/model_stripper.rb +69 -63
- data/lib/scripper/sequel/value_converter.rb +23 -23
- data/scripper.gemspec +35 -35
- metadata +2 -3
- data/Gemfile.lock +0 -104
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 23ae9ed50d0a9424206847fc397358c0e496b45f0ffec8627b0710043fc863de
|
4
|
+
data.tar.gz: 7308b47894cf9d3c430fc21586827e00e6a7eaa249cc65ae7a779c4fa8f53a59
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1810dd1a45876ac927cb652b742fa3b63ecbedbf086ed670a258df9b6707ea539e4dff58f7361f44e13a504cf90e170e40658a6336469776f5f5270abe287278
|
7
|
+
data.tar.gz: 24d08010518e9232d7be7ffcc6d261060803e4bf4f5416e836fa514603fe8fd87e6c56080df3a923e135924615bb665a0a0ae4df8e9b2c445405f5b49f687885
|
data/.gitignore
CHANGED
@@ -1,13 +1,14 @@
|
|
1
|
-
/.bundle/
|
2
|
-
/.yardoc
|
3
|
-
/_yardoc/
|
4
|
-
/coverage/
|
5
|
-
/doc/
|
6
|
-
/pkg/
|
7
|
-
/spec/reports/
|
8
|
-
/tmp/
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
1
|
+
/.bundle/
|
2
|
+
/.yardoc
|
3
|
+
/_yardoc/
|
4
|
+
/coverage/
|
5
|
+
/doc/
|
6
|
+
/pkg/
|
7
|
+
/spec/reports/
|
8
|
+
/tmp/
|
9
|
+
/Gemfile.lock
|
10
|
+
|
11
|
+
# rspec failure tracking
|
12
|
+
.rspec_status
|
13
|
+
|
14
|
+
/scripper-*.gem
|
data/.rspec
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
--format documentation
|
2
|
-
--color
|
3
|
-
--require spec_helper
|
1
|
+
--format documentation
|
2
|
+
--color
|
3
|
+
--require spec_helper
|
data/.rubocop.yml
CHANGED
@@ -1,27 +1,27 @@
|
|
1
|
-
inherit_gem:
|
2
|
-
rubocop-config-umbrellio: lib/rubocop.yml
|
3
|
-
armitage-rubocop:
|
4
|
-
- lib/rubocop.general.yml
|
5
|
-
- lib/rubocop.rspec.yml
|
6
|
-
|
7
|
-
AllCops:
|
8
|
-
TargetRubyVersion: 2.6.3
|
9
|
-
Include:
|
10
|
-
- sequel-connection_guard.gemspec
|
11
|
-
- lib/**/*.rb
|
12
|
-
- spec/**/*.rb
|
13
|
-
- Gemfile
|
14
|
-
- Rakefile
|
15
|
-
- bin/console
|
16
|
-
|
17
|
-
Style/TrailingCommaInArguments:
|
18
|
-
EnforcedStyleForMultiline: comma
|
19
|
-
|
20
|
-
Style/TrailingCommaInArrayLiteral:
|
21
|
-
EnforcedStyleForMultiline: comma
|
22
|
-
|
23
|
-
Style/TrailingCommaInHashLiteral:
|
24
|
-
EnforcedStyleForMultiline: comma
|
25
|
-
|
26
|
-
RSpec/LeakyConstantDeclaration:
|
27
|
-
Enabled: false
|
1
|
+
inherit_gem:
|
2
|
+
rubocop-config-umbrellio: lib/rubocop.yml
|
3
|
+
armitage-rubocop:
|
4
|
+
- lib/rubocop.general.yml
|
5
|
+
- lib/rubocop.rspec.yml
|
6
|
+
|
7
|
+
AllCops:
|
8
|
+
TargetRubyVersion: 2.6.3
|
9
|
+
Include:
|
10
|
+
- sequel-connection_guard.gemspec
|
11
|
+
- lib/**/*.rb
|
12
|
+
- spec/**/*.rb
|
13
|
+
- Gemfile
|
14
|
+
- Rakefile
|
15
|
+
- bin/console
|
16
|
+
|
17
|
+
Style/TrailingCommaInArguments:
|
18
|
+
EnforcedStyleForMultiline: comma
|
19
|
+
|
20
|
+
Style/TrailingCommaInArrayLiteral:
|
21
|
+
EnforcedStyleForMultiline: comma
|
22
|
+
|
23
|
+
Style/TrailingCommaInHashLiteral:
|
24
|
+
EnforcedStyleForMultiline: comma
|
25
|
+
|
26
|
+
RSpec/LeakyConstantDeclaration:
|
27
|
+
Enabled: false
|
data/.travis.yml
CHANGED
@@ -1,20 +1,20 @@
|
|
1
|
-
sudo: false
|
2
|
-
language: ruby
|
3
|
-
rvm:
|
4
|
-
- 2.5.1
|
5
|
-
- 2.6
|
6
|
-
- ruby-head
|
7
|
-
|
8
|
-
services:
|
9
|
-
- postgresql
|
10
|
-
addons:
|
11
|
-
postgresql: 9.6
|
12
|
-
|
13
|
-
before_install: gem install bundler
|
14
|
-
before_script:
|
15
|
-
- psql -c 'create database scripper_test;' -U postgres
|
16
|
-
script:
|
17
|
-
- bundle exec rspec
|
18
|
-
- bundle exec rubocop
|
19
|
-
|
20
|
-
cache: bundler
|
1
|
+
sudo: false
|
2
|
+
language: ruby
|
3
|
+
rvm:
|
4
|
+
- 2.5.1
|
5
|
+
- 2.6
|
6
|
+
- ruby-head
|
7
|
+
|
8
|
+
services:
|
9
|
+
- postgresql
|
10
|
+
addons:
|
11
|
+
postgresql: 9.6
|
12
|
+
|
13
|
+
before_install: gem install bundler
|
14
|
+
before_script:
|
15
|
+
- psql -c 'create database scripper_test;' -U postgres
|
16
|
+
script:
|
17
|
+
- bundle exec rspec
|
18
|
+
- bundle exec rubocop
|
19
|
+
|
20
|
+
cache: bundler
|
data/Gemfile
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
source "https://rubygems.org"
|
4
|
-
|
5
|
-
gemspec
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
4
|
+
|
5
|
+
gemspec
|
data/LICENSE
CHANGED
@@ -1,21 +1,21 @@
|
|
1
|
-
The MIT License (MIT)
|
2
|
-
|
3
|
-
Copyright (c) 2019 Umbrellio
|
4
|
-
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
7
|
-
in the Software without restriction, including without limitation the rights
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
10
|
-
furnished to do so, subject to the following conditions:
|
11
|
-
|
12
|
-
The above copyright notice and this permission notice shall be included in
|
13
|
-
all copies or substantial portions of the Software.
|
14
|
-
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
-
THE SOFTWARE.
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2019 Umbrellio
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
CHANGED
@@ -1,164 +1,164 @@
|
|
1
|
-
# Scripper
|
2
|
-
[![Build Status](https://travis-ci.org/umbrellio/scripper.svg?branch=master)](https://travis-ci.org/umbrellio/scripper)
|
3
|
-
[![Coverage Status](https://coveralls.io/repos/github/umbrellio/scripper/badge.svg?branch=master)](https://coveralls.io/github/umbrellio/scripper?branch=master)
|
4
|
-
[![Gem Version](https://badge.fury.io/rb/scripper.svg)](https://badge.fury.io/rb/scripper)
|
5
|
-
|
6
|
-
This gem allows you to strip down your Sequel model instances and hashes returned by dataset queries
|
7
|
-
to simple Ruby structs.
|
8
|
-
|
9
|
-
**This gem was only tested against PostgreSQL databases.**
|
10
|
-
|
11
|
-
## Why strip models?
|
12
|
-
|
13
|
-
It's often convenient to simply call methods on model objects everywhere: controllers, views,
|
14
|
-
serializers, business logic, and so on. But, by doing so, you're making the whole of your codebase
|
15
|
-
depend on your database!
|
16
|
-
|
17
|
-
This gem is a very basic way to introduce some layer of isolation between your database and the
|
18
|
-
rest of the codebase. As your application grows, it will be much simpler to transition to more
|
19
|
-
mature abstractions with such isolation than without it.
|
20
|
-
|
21
|
-
The behaviour is more predictable since:
|
22
|
-
- There are no unexpected database queries (e.x. when loading associations during view rendering)
|
23
|
-
- There are no leaking database types like `Sequel::Postgres::JSONBHash` (these are converted to hashes automatically)
|
24
|
-
|
25
|
-
## Installation
|
26
|
-
|
27
|
-
Add this line to your application's Gemfile:
|
28
|
-
|
29
|
-
```ruby
|
30
|
-
gem 'scripper'
|
31
|
-
```
|
32
|
-
|
33
|
-
And then execute:
|
34
|
-
```sh
|
35
|
-
$ bundle
|
36
|
-
```
|
37
|
-
|
38
|
-
It's recommended to "wrap" this gem into a separate module:
|
39
|
-
```ruby
|
40
|
-
# lib/stripper.rb
|
41
|
-
|
42
|
-
Stripper = Scripper::Sequel
|
43
|
-
```
|
44
|
-
|
45
|
-
## Usage
|
46
|
-
|
47
|
-
It's very simple! Scripper works both with instances of Sequel::Model and hashes returned when
|
48
|
-
using naked models or datasets.
|
49
|
-
|
50
|
-
```ruby
|
51
|
-
# with models
|
52
|
-
user = User.first
|
53
|
-
Stripper.strip(user) # => #<struct id=2, email="cow@cow.cow", password_hash="...">
|
54
|
-
user.email # => "cow@cow.cow"
|
55
|
-
|
56
|
-
# with datasets
|
57
|
-
user = DB[:users].first # or: User.naked.first
|
58
|
-
Stripper.strip(user) # => #<struct id=2, email="cow@cow.cow", password_hash="...">
|
59
|
-
user.email # => "cow@cow.cow"
|
60
|
-
```
|
61
|
-
|
62
|
-
### Loading associations
|
63
|
-
|
64
|
-
If you'd like to also use associations on your struct (works only with models):
|
65
|
-
|
66
|
-
```ruby
|
67
|
-
user = User.first
|
68
|
-
Stripper.strip(user, with_associations: %w[cookies])
|
69
|
-
# => #<struct id=2, ..., cookies=[#<struct ...>, #<struct ...>, #<struct ...>]>
|
70
|
-
```
|
71
|
-
|
72
|
-
Beware that this will load _all_ cookies associated with your user! If you want to impose some
|
73
|
-
filtering conditions, you can do that:
|
74
|
-
|
75
|
-
```ruby
|
76
|
-
user = User.first
|
77
|
-
Stripper.strip(
|
78
|
-
user,
|
79
|
-
with_associations: { cookies: -> (ds) { ds.where(active: true).limit(10) } },
|
80
|
-
)
|
81
|
-
```
|
82
|
-
|
83
|
-
This will only load no more than 10 active cookies.
|
84
|
-
|
85
|
-
### Providing extra attributes
|
86
|
-
|
87
|
-
Sometimes it's useful to provide some context beyond associations and model/dataset attributes.
|
88
|
-
|
89
|
-
In the example below, we're providing information about user's payment sum, not only user's fields.
|
90
|
-
|
91
|
-
```ruby
|
92
|
-
# with a model
|
93
|
-
user = User
|
94
|
-
.left_join(:payments)
|
95
|
-
.select_all(:users)
|
96
|
-
.select_append(
|
97
|
-
Sequel.function(:sum, Sequel[:payments][:amount]).as(:payment_sum),
|
98
|
-
)
|
99
|
-
.group(Sequel[:users][:id])
|
100
|
-
.first
|
101
|
-
|
102
|
-
Stripper.strip(user, with_attributes: { payment_sum: user[:payment_sum] })
|
103
|
-
# => #<struct id=2, ..., payment_sum=418.0>
|
104
|
-
|
105
|
-
# with a dataset, it's nearly identical
|
106
|
-
user = DB[:users]
|
107
|
-
.left_join(:payments)
|
108
|
-
.select_all(:users)
|
109
|
-
.select_append(
|
110
|
-
Sequel.function(:sum, Sequel[:payments][:amount]).as(:payment_sum),
|
111
|
-
)
|
112
|
-
.group(Sequel[:users][:id])
|
113
|
-
.first
|
114
|
-
|
115
|
-
Stripper.strip(user, with_attributes: { payment_sum: user[:payment_sum] })
|
116
|
-
# => #<struct id=2, ..., payment_sum=418.0>
|
117
|
-
```
|
118
|
-
|
119
|
-
## Default value conversions
|
120
|
-
|
121
|
-
`Sequel::Postgres::JSONHashBase` (`JSONBHash`, `JSONHash`, ...) => `Hash`
|
122
|
-
|
123
|
-
`Sequel::Postgres::JSONArrayBase` (`JSONBArray`, `JSONArray`, ...) => `Array`
|
124
|
-
|
125
|
-
`Sequel::Postgres::PGArray` => `Array`
|
126
|
-
|
127
|
-
`BigDecimal` => `Float`
|
128
|
-
|
129
|
-
Currently, these are not extensible.
|
130
|
-
|
131
|
-
## Roadmap
|
132
|
-
|
133
|
-
It would be lovely to:
|
134
|
-
- Make value conversions extensible
|
135
|
-
- Support ActiveRecord
|
136
|
-
- Test the gem on other databases
|
137
|
-
|
138
|
-
Your contributions and feedback are very welcome!
|
139
|
-
|
140
|
-
## Development
|
141
|
-
|
142
|
-
To run tests, you need to first create a PostgreSQL database, and then set a `DB_URL` variable.
|
143
|
-
|
144
|
-
Example:
|
145
|
-
```sh
|
146
|
-
DB_URL=postgres://localhost/scripper_test bundle exec rspec
|
147
|
-
```
|
148
|
-
|
149
|
-
If you want to enable Sequel's database access logging during spec runs, use `LOG_DB=1`.
|
150
|
-
|
151
|
-
## Contributing
|
152
|
-
|
153
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/umbrellio/scripper.
|
154
|
-
|
155
|
-
## License
|
156
|
-
|
157
|
-
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
158
|
-
|
159
|
-
## Authors
|
160
|
-
Created by [Alexander Komarov](https://github.com/akxcv).
|
161
|
-
|
162
|
-
<a href="https://github.com/umbrellio/">
|
163
|
-
<img style="float: left;" src="https://umbrellio.github.io/Umbrellio/supported_by_umbrellio.svg" alt="Supported by Umbrellio" width="439" height="72">
|
164
|
-
</a>
|
1
|
+
# Scripper
|
2
|
+
[![Build Status](https://travis-ci.org/umbrellio/scripper.svg?branch=master)](https://travis-ci.org/umbrellio/scripper)
|
3
|
+
[![Coverage Status](https://coveralls.io/repos/github/umbrellio/scripper/badge.svg?branch=master)](https://coveralls.io/github/umbrellio/scripper?branch=master)
|
4
|
+
[![Gem Version](https://badge.fury.io/rb/scripper.svg)](https://badge.fury.io/rb/scripper)
|
5
|
+
|
6
|
+
This gem allows you to strip down your Sequel model instances and hashes returned by dataset queries
|
7
|
+
to simple Ruby structs.
|
8
|
+
|
9
|
+
**This gem was only tested against PostgreSQL databases.**
|
10
|
+
|
11
|
+
## Why strip models?
|
12
|
+
|
13
|
+
It's often convenient to simply call methods on model objects everywhere: controllers, views,
|
14
|
+
serializers, business logic, and so on. But, by doing so, you're making the whole of your codebase
|
15
|
+
depend on your database!
|
16
|
+
|
17
|
+
This gem is a very basic way to introduce some layer of isolation between your database and the
|
18
|
+
rest of the codebase. As your application grows, it will be much simpler to transition to more
|
19
|
+
mature abstractions with such isolation than without it.
|
20
|
+
|
21
|
+
The behaviour is more predictable since:
|
22
|
+
- There are no unexpected database queries (e.x. when loading associations during view rendering)
|
23
|
+
- There are no leaking database types like `Sequel::Postgres::JSONBHash` (these are converted to hashes automatically)
|
24
|
+
|
25
|
+
## Installation
|
26
|
+
|
27
|
+
Add this line to your application's Gemfile:
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
gem 'scripper'
|
31
|
+
```
|
32
|
+
|
33
|
+
And then execute:
|
34
|
+
```sh
|
35
|
+
$ bundle
|
36
|
+
```
|
37
|
+
|
38
|
+
It's recommended to "wrap" this gem into a separate module:
|
39
|
+
```ruby
|
40
|
+
# lib/stripper.rb
|
41
|
+
|
42
|
+
Stripper = Scripper::Sequel
|
43
|
+
```
|
44
|
+
|
45
|
+
## Usage
|
46
|
+
|
47
|
+
It's very simple! Scripper works both with instances of Sequel::Model and hashes returned when
|
48
|
+
using naked models or datasets.
|
49
|
+
|
50
|
+
```ruby
|
51
|
+
# with models
|
52
|
+
user = User.first
|
53
|
+
Stripper.strip(user) # => #<struct id=2, email="cow@cow.cow", password_hash="...">
|
54
|
+
user.email # => "cow@cow.cow"
|
55
|
+
|
56
|
+
# with datasets
|
57
|
+
user = DB[:users].first # or: User.naked.first
|
58
|
+
Stripper.strip(user) # => #<struct id=2, email="cow@cow.cow", password_hash="...">
|
59
|
+
user.email # => "cow@cow.cow"
|
60
|
+
```
|
61
|
+
|
62
|
+
### Loading associations
|
63
|
+
|
64
|
+
If you'd like to also use associations on your struct (works only with models):
|
65
|
+
|
66
|
+
```ruby
|
67
|
+
user = User.first
|
68
|
+
Stripper.strip(user, with_associations: %w[cookies])
|
69
|
+
# => #<struct id=2, ..., cookies=[#<struct ...>, #<struct ...>, #<struct ...>]>
|
70
|
+
```
|
71
|
+
|
72
|
+
Beware that this will load _all_ cookies associated with your user! If you want to impose some
|
73
|
+
filtering conditions, you can do that:
|
74
|
+
|
75
|
+
```ruby
|
76
|
+
user = User.first
|
77
|
+
Stripper.strip(
|
78
|
+
user,
|
79
|
+
with_associations: { cookies: -> (ds) { ds.where(active: true).limit(10) } },
|
80
|
+
)
|
81
|
+
```
|
82
|
+
|
83
|
+
This will only load no more than 10 active cookies.
|
84
|
+
|
85
|
+
### Providing extra attributes
|
86
|
+
|
87
|
+
Sometimes it's useful to provide some context beyond associations and model/dataset attributes.
|
88
|
+
|
89
|
+
In the example below, we're providing information about user's payment sum, not only user's fields.
|
90
|
+
|
91
|
+
```ruby
|
92
|
+
# with a model
|
93
|
+
user = User
|
94
|
+
.left_join(:payments)
|
95
|
+
.select_all(:users)
|
96
|
+
.select_append(
|
97
|
+
Sequel.function(:sum, Sequel[:payments][:amount]).as(:payment_sum),
|
98
|
+
)
|
99
|
+
.group(Sequel[:users][:id])
|
100
|
+
.first
|
101
|
+
|
102
|
+
Stripper.strip(user, with_attributes: { payment_sum: user[:payment_sum] })
|
103
|
+
# => #<struct id=2, ..., payment_sum=418.0>
|
104
|
+
|
105
|
+
# with a dataset, it's nearly identical
|
106
|
+
user = DB[:users]
|
107
|
+
.left_join(:payments)
|
108
|
+
.select_all(:users)
|
109
|
+
.select_append(
|
110
|
+
Sequel.function(:sum, Sequel[:payments][:amount]).as(:payment_sum),
|
111
|
+
)
|
112
|
+
.group(Sequel[:users][:id])
|
113
|
+
.first
|
114
|
+
|
115
|
+
Stripper.strip(user, with_attributes: { payment_sum: user[:payment_sum] })
|
116
|
+
# => #<struct id=2, ..., payment_sum=418.0>
|
117
|
+
```
|
118
|
+
|
119
|
+
## Default value conversions
|
120
|
+
|
121
|
+
`Sequel::Postgres::JSONHashBase` (`JSONBHash`, `JSONHash`, ...) => `Hash`
|
122
|
+
|
123
|
+
`Sequel::Postgres::JSONArrayBase` (`JSONBArray`, `JSONArray`, ...) => `Array`
|
124
|
+
|
125
|
+
`Sequel::Postgres::PGArray` => `Array`
|
126
|
+
|
127
|
+
`BigDecimal` => `Float`
|
128
|
+
|
129
|
+
Currently, these are not extensible.
|
130
|
+
|
131
|
+
## Roadmap
|
132
|
+
|
133
|
+
It would be lovely to:
|
134
|
+
- Make value conversions extensible
|
135
|
+
- Support ActiveRecord
|
136
|
+
- Test the gem on other databases
|
137
|
+
|
138
|
+
Your contributions and feedback are very welcome!
|
139
|
+
|
140
|
+
## Development
|
141
|
+
|
142
|
+
To run tests, you need to first create a PostgreSQL database, and then set a `DB_URL` variable.
|
143
|
+
|
144
|
+
Example:
|
145
|
+
```sh
|
146
|
+
DB_URL=postgres://localhost/scripper_test bundle exec rspec
|
147
|
+
```
|
148
|
+
|
149
|
+
If you want to enable Sequel's database access logging during spec runs, use `LOG_DB=1`.
|
150
|
+
|
151
|
+
## Contributing
|
152
|
+
|
153
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/umbrellio/scripper.
|
154
|
+
|
155
|
+
## License
|
156
|
+
|
157
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
158
|
+
|
159
|
+
## Authors
|
160
|
+
Created by [Alexander Komarov](https://github.com/akxcv).
|
161
|
+
|
162
|
+
<a href="https://github.com/umbrellio/">
|
163
|
+
<img style="float: left;" src="https://umbrellio.github.io/Umbrellio/supported_by_umbrellio.svg" alt="Supported by Umbrellio" width="439" height="72">
|
164
|
+
</a>
|
data/Rakefile
CHANGED
@@ -1,8 +1,8 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "bundler/gem_tasks"
|
4
|
-
require "rspec/core/rake_task"
|
5
|
-
|
6
|
-
RSpec::Core::RakeTask.new(:spec)
|
7
|
-
|
8
|
-
task default: :spec
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "bundler/gem_tasks"
|
4
|
+
require "rspec/core/rake_task"
|
5
|
+
|
6
|
+
RSpec::Core::RakeTask.new(:spec)
|
7
|
+
|
8
|
+
task default: :spec
|
data/lib/scripper.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "scripper/sequel"
|
4
|
-
require "scripper/sequel/dataset_stripper"
|
5
|
-
require "scripper/sequel/model_stripper"
|
6
|
-
require "scripper/sequel/value_converter"
|
7
|
-
|
8
|
-
module Scripper; end
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "scripper/sequel"
|
4
|
+
require "scripper/sequel/dataset_stripper"
|
5
|
+
require "scripper/sequel/model_stripper"
|
6
|
+
require "scripper/sequel/value_converter"
|
7
|
+
|
8
|
+
module Scripper; end
|
data/lib/scripper/sequel.rb
CHANGED
@@ -1,25 +1,25 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Scripper
|
4
|
-
module Sequel
|
5
|
-
class << self
|
6
|
-
def strip(object, **args)
|
7
|
-
if object.is_a?(Hash)
|
8
|
-
strip_dataset(object)
|
9
|
-
else
|
10
|
-
strip_model(object, **args)
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
private
|
15
|
-
|
16
|
-
def strip_model(*args)
|
17
|
-
ModelStripper.strip(*args)
|
18
|
-
end
|
19
|
-
|
20
|
-
def strip_dataset(ds)
|
21
|
-
DatasetStripper.strip(ds)
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Scripper
|
4
|
+
module Sequel
|
5
|
+
class << self
|
6
|
+
def strip(object, **args)
|
7
|
+
if object.is_a?(Hash)
|
8
|
+
strip_dataset(object)
|
9
|
+
else
|
10
|
+
strip_model(object, **args)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def strip_model(*args)
|
17
|
+
ModelStripper.strip(*args)
|
18
|
+
end
|
19
|
+
|
20
|
+
def strip_dataset(ds)
|
21
|
+
DatasetStripper.strip(ds)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -1,14 +1,14 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Scripper
|
4
|
-
module Sequel
|
5
|
-
module DatasetStripper
|
6
|
-
class << self
|
7
|
-
def strip(hsh)
|
8
|
-
struct_klass = Struct.new(*hsh.keys)
|
9
|
-
struct_klass.new(*hsh.transform_values { |v| ValueConverter.convert_value(v) }.values)
|
10
|
-
end
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Scripper
|
4
|
+
module Sequel
|
5
|
+
module DatasetStripper
|
6
|
+
class << self
|
7
|
+
def strip(hsh)
|
8
|
+
struct_klass = Struct.new(*hsh.keys)
|
9
|
+
struct_klass.new(*hsh.transform_values { |v| ValueConverter.convert_value(v) }.values)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -1,63 +1,69 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Scripper
|
4
|
-
module Sequel
|
5
|
-
module ModelStripper
|
6
|
-
class << self
|
7
|
-
def strip(object, with_associations: nil, with_attributes: nil)
|
8
|
-
association_fields = build_association_fields(object, with_associations)
|
9
|
-
attribute_fields = build_attribute_fields(object, with_attributes)
|
10
|
-
|
11
|
-
attrs = {
|
12
|
-
**convert_values(object.values),
|
13
|
-
**association_fields,
|
14
|
-
**attribute_fields,
|
15
|
-
}
|
16
|
-
|
17
|
-
struct_klass = Struct.new(*attrs.keys)
|
18
|
-
struct_klass.new(*attrs.values)
|
19
|
-
end
|
20
|
-
|
21
|
-
private
|
22
|
-
|
23
|
-
def build_association_fields(object, associations)
|
24
|
-
return {} if associations.nil?
|
25
|
-
|
26
|
-
if associations.is_a?(Array)
|
27
|
-
build_association_fields_from_array(object, associations)
|
28
|
-
else # consider associations a hash
|
29
|
-
build_association_fields_from_hash(object, associations)
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
def build_attribute_fields(object, attributes)
|
34
|
-
return {} if attributes.nil?
|
35
|
-
|
36
|
-
convert_values(attributes)
|
37
|
-
end
|
38
|
-
|
39
|
-
def convert_values(hsh)
|
40
|
-
hsh.transform_values { |v| ValueConverter.convert_value(v) }
|
41
|
-
end
|
42
|
-
|
43
|
-
def build_association_fields_from_array(object, associations)
|
44
|
-
associations.reduce({}) do |acc, association|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Scripper
|
4
|
+
module Sequel
|
5
|
+
module ModelStripper
|
6
|
+
class << self
|
7
|
+
def strip(object, with_associations: nil, with_attributes: nil)
|
8
|
+
association_fields = build_association_fields(object, with_associations)
|
9
|
+
attribute_fields = build_attribute_fields(object, with_attributes)
|
10
|
+
|
11
|
+
attrs = {
|
12
|
+
**convert_values(object.values),
|
13
|
+
**association_fields,
|
14
|
+
**attribute_fields,
|
15
|
+
}
|
16
|
+
|
17
|
+
struct_klass = Struct.new(*attrs.keys)
|
18
|
+
struct_klass.new(*attrs.values)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def build_association_fields(object, associations)
|
24
|
+
return {} if associations.nil?
|
25
|
+
|
26
|
+
if associations.is_a?(Array)
|
27
|
+
build_association_fields_from_array(object, associations)
|
28
|
+
else # consider associations a hash
|
29
|
+
build_association_fields_from_hash(object, associations)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def build_attribute_fields(object, attributes)
|
34
|
+
return {} if attributes.nil?
|
35
|
+
|
36
|
+
convert_values(attributes)
|
37
|
+
end
|
38
|
+
|
39
|
+
def convert_values(hsh)
|
40
|
+
hsh.transform_values { |v| ValueConverter.convert_value(v) }
|
41
|
+
end
|
42
|
+
|
43
|
+
def build_association_fields_from_array(object, associations)
|
44
|
+
associations.reduce({}) do |acc, association|
|
45
|
+
association_value = object.public_send(association)
|
46
|
+
stripped_association_value =
|
47
|
+
if association_value.is_a?(Array)
|
48
|
+
association_value.map { |obj| strip(obj) }
|
49
|
+
else
|
50
|
+
strip(association_value)
|
51
|
+
end
|
52
|
+
|
53
|
+
acc.merge(association.to_sym => stripped_association_value)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def build_association_fields_from_hash(object, associations)
|
58
|
+
associations.entries.reduce({}) do |acc, (association, condition)|
|
59
|
+
association_ds = object.class.association_reflection(association).associated_dataset
|
60
|
+
|
61
|
+
acc.merge(
|
62
|
+
association.to_sym => condition.call(association_ds).all.map { |obj| strip(obj) },
|
63
|
+
)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -1,23 +1,23 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Scripper
|
4
|
-
module Sequel
|
5
|
-
module ValueConverter
|
6
|
-
class << self
|
7
|
-
def convert_value(value)
|
8
|
-
if value.is_a?(::Sequel::Postgres::JSONHashBase)
|
9
|
-
value.to_h
|
10
|
-
elsif value.is_a?(::Sequel::Postgres::JSONArrayBase)
|
11
|
-
value.to_a
|
12
|
-
elsif value.is_a?(::Sequel::Postgres::PGArray)
|
13
|
-
value.to_a
|
14
|
-
elsif value.is_a?(BigDecimal)
|
15
|
-
value.to_f
|
16
|
-
else
|
17
|
-
value
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Scripper
|
4
|
+
module Sequel
|
5
|
+
module ValueConverter
|
6
|
+
class << self
|
7
|
+
def convert_value(value)
|
8
|
+
if value.is_a?(::Sequel::Postgres::JSONHashBase)
|
9
|
+
value.to_h
|
10
|
+
elsif value.is_a?(::Sequel::Postgres::JSONArrayBase)
|
11
|
+
value.to_a
|
12
|
+
elsif value.is_a?(::Sequel::Postgres::PGArray)
|
13
|
+
value.to_a
|
14
|
+
elsif value.is_a?(BigDecimal)
|
15
|
+
value.to_f
|
16
|
+
else
|
17
|
+
value
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/scripper.gemspec
CHANGED
@@ -1,35 +1,35 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
lib = File.expand_path(
|
4
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
-
|
6
|
-
Gem::Specification.new do |spec|
|
7
|
-
spec.name =
|
8
|
-
spec.version =
|
9
|
-
spec.authors = [
|
10
|
-
spec.email = [
|
11
|
-
|
12
|
-
spec.summary =
|
13
|
-
spec.description =
|
14
|
-
spec.homepage =
|
15
|
-
spec.license =
|
16
|
-
|
17
|
-
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
18
|
-
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
19
|
-
end
|
20
|
-
spec.bindir =
|
21
|
-
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
22
|
-
spec.require_paths = [
|
23
|
-
|
24
|
-
spec.add_development_dependency
|
25
|
-
spec.add_development_dependency
|
26
|
-
spec.add_development_dependency
|
27
|
-
spec.add_development_dependency
|
28
|
-
spec.add_development_dependency
|
29
|
-
spec.add_development_dependency
|
30
|
-
spec.add_development_dependency
|
31
|
-
spec.add_development_dependency
|
32
|
-
|
33
|
-
spec.add_development_dependency
|
34
|
-
spec.add_development_dependency
|
35
|
-
end
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
lib = File.expand_path("lib", __dir__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "scripper"
|
8
|
+
spec.version = "0.1.1"
|
9
|
+
spec.authors = ["Alexander Komarov <ak@akxcv.com>"]
|
10
|
+
spec.email = %w[ak@akxcv.com]
|
11
|
+
|
12
|
+
spec.summary = "A gem that strips Sequel models down to simple value objects"
|
13
|
+
spec.description = "@see summary"
|
14
|
+
spec.homepage = "https://github.com/umbrellio/scripper"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
18
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
19
|
+
end
|
20
|
+
spec.bindir = "exe"
|
21
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
22
|
+
spec.require_paths = %w[lib]
|
23
|
+
|
24
|
+
spec.add_development_dependency "bundler", "~> 2.0"
|
25
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
26
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
27
|
+
spec.add_development_dependency "simplecov"
|
28
|
+
spec.add_development_dependency "coveralls"
|
29
|
+
spec.add_development_dependency "armitage-rubocop"
|
30
|
+
spec.add_development_dependency "rubocop-config-umbrellio"
|
31
|
+
spec.add_development_dependency "pry"
|
32
|
+
|
33
|
+
spec.add_development_dependency "pg"
|
34
|
+
spec.add_development_dependency "sequel"
|
35
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: scripper
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alexander Komarov <ak@akxcv.com>
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-10-
|
11
|
+
date: 2019-10-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -162,7 +162,6 @@ files:
|
|
162
162
|
- ".rubocop.yml"
|
163
163
|
- ".travis.yml"
|
164
164
|
- Gemfile
|
165
|
-
- Gemfile.lock
|
166
165
|
- LICENSE
|
167
166
|
- README.md
|
168
167
|
- Rakefile
|
data/Gemfile.lock
DELETED
@@ -1,104 +0,0 @@
|
|
1
|
-
PATH
|
2
|
-
remote: .
|
3
|
-
specs:
|
4
|
-
scripper (0.1.0)
|
5
|
-
|
6
|
-
GEM
|
7
|
-
remote: https://rubygems.org/
|
8
|
-
specs:
|
9
|
-
armitage-rubocop (0.70.0.110)
|
10
|
-
rubocop (= 0.70.0)
|
11
|
-
rubocop-performance (= 1.3.0)
|
12
|
-
rubocop-rails (= 2.0.0)
|
13
|
-
rubocop-rspec (= 1.33.0)
|
14
|
-
ast (2.4.0)
|
15
|
-
coderay (1.1.2)
|
16
|
-
coveralls (0.7.2)
|
17
|
-
multi_json (~> 1.3)
|
18
|
-
rest-client (= 1.6.7)
|
19
|
-
simplecov (>= 0.7)
|
20
|
-
term-ansicolor (= 1.2.2)
|
21
|
-
thor (= 0.18.1)
|
22
|
-
diff-lcs (1.3)
|
23
|
-
docile (1.3.2)
|
24
|
-
jaro_winkler (1.5.3)
|
25
|
-
json (2.2.0)
|
26
|
-
method_source (0.9.2)
|
27
|
-
mime-types (3.3)
|
28
|
-
mime-types-data (~> 3.2015)
|
29
|
-
mime-types-data (3.2019.1009)
|
30
|
-
multi_json (1.13.1)
|
31
|
-
parallel (1.18.0)
|
32
|
-
parser (2.6.5.0)
|
33
|
-
ast (~> 2.4.0)
|
34
|
-
pg (1.1.4-x64-mingw32)
|
35
|
-
pry (0.12.2)
|
36
|
-
coderay (~> 1.1.0)
|
37
|
-
method_source (~> 0.9.0)
|
38
|
-
rack (2.0.7)
|
39
|
-
rainbow (3.0.0)
|
40
|
-
rake (10.5.0)
|
41
|
-
rest-client (1.6.7)
|
42
|
-
mime-types (>= 1.16)
|
43
|
-
rspec (3.9.0)
|
44
|
-
rspec-core (~> 3.9.0)
|
45
|
-
rspec-expectations (~> 3.9.0)
|
46
|
-
rspec-mocks (~> 3.9.0)
|
47
|
-
rspec-core (3.9.0)
|
48
|
-
rspec-support (~> 3.9.0)
|
49
|
-
rspec-expectations (3.9.0)
|
50
|
-
diff-lcs (>= 1.2.0, < 2.0)
|
51
|
-
rspec-support (~> 3.9.0)
|
52
|
-
rspec-mocks (3.9.0)
|
53
|
-
diff-lcs (>= 1.2.0, < 2.0)
|
54
|
-
rspec-support (~> 3.9.0)
|
55
|
-
rspec-support (3.9.0)
|
56
|
-
rubocop (0.70.0)
|
57
|
-
jaro_winkler (~> 1.5.1)
|
58
|
-
parallel (~> 1.10)
|
59
|
-
parser (>= 2.6)
|
60
|
-
rainbow (>= 2.2.2, < 4.0)
|
61
|
-
ruby-progressbar (~> 1.7)
|
62
|
-
unicode-display_width (>= 1.4.0, < 1.7)
|
63
|
-
rubocop-config-umbrellio (0.70.0.55)
|
64
|
-
rubocop (= 0.70.0)
|
65
|
-
rubocop-performance (= 1.3.0)
|
66
|
-
rubocop-rspec (= 1.33.0)
|
67
|
-
rubocop-performance (1.3.0)
|
68
|
-
rubocop (>= 0.68.0)
|
69
|
-
rubocop-rails (2.0.0)
|
70
|
-
rack (>= 2.0)
|
71
|
-
rubocop (>= 0.70.0)
|
72
|
-
rubocop-rspec (1.33.0)
|
73
|
-
rubocop (>= 0.60.0)
|
74
|
-
ruby-progressbar (1.10.1)
|
75
|
-
sequel (5.25.0)
|
76
|
-
simplecov (0.17.1)
|
77
|
-
docile (~> 1.1)
|
78
|
-
json (>= 1.8, < 3)
|
79
|
-
simplecov-html (~> 0.10.0)
|
80
|
-
simplecov-html (0.10.2)
|
81
|
-
term-ansicolor (1.2.2)
|
82
|
-
tins (~> 0.8)
|
83
|
-
thor (0.18.1)
|
84
|
-
tins (0.13.2)
|
85
|
-
unicode-display_width (1.6.0)
|
86
|
-
|
87
|
-
PLATFORMS
|
88
|
-
x64-mingw32
|
89
|
-
|
90
|
-
DEPENDENCIES
|
91
|
-
armitage-rubocop
|
92
|
-
bundler (~> 2.0)
|
93
|
-
coveralls
|
94
|
-
pg
|
95
|
-
pry
|
96
|
-
rake (~> 10.0)
|
97
|
-
rspec (~> 3.0)
|
98
|
-
rubocop-config-umbrellio
|
99
|
-
scripper!
|
100
|
-
sequel
|
101
|
-
simplecov
|
102
|
-
|
103
|
-
BUNDLED WITH
|
104
|
-
2.0.2
|