table_saw 2.10.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2523b019080dccb116fed85c38ecb0a629cd6f8630abd65a1a30e2214c6a71b3
4
- data.tar.gz: 03e53e17ef16f72e976772bc027ca4698ba8eaa7bf4498a2fae5ab7e7dfd3f7d
3
+ metadata.gz: 5e8e8f428d4b3d28ddec794d07a0d052e6280bfd96ee0b58735b3bcefa5a7bee
4
+ data.tar.gz: 64249bc2ae252e9b361c32b376f1679837ac1fd82bde70b11eda9fad66050a00
5
5
  SHA512:
6
- metadata.gz: ab502f4b52e5e5214cca044d4ca450b9c885e63595423161e4ad130f85da6a5eb28fca008c77e49d70b675448ebe54a63bf6dfba465db6f47572b5d9b7fddbf3
7
- data.tar.gz: 63a25f77f34251c801ba0f21aa5649988e763c1e8516dc4c1d3fc7976c2bd97c5bbc50d4fe6085b24032780ea7c93203d27c856c1304d681f6d2065a9a60f2f3
6
+ metadata.gz: 11b993bf2a9f723783b3f67648af15b794b807678db845b6c57b1ac8e5b57762ac41a3c1530696f5f3f99d6a7649061a22a88267aab23fb4e5cf158533f014e6
7
+ data.tar.gz: 84eb90d309a2dc4a8ddd04c4898c748f81f6c2f62223780b9e5d272913d2e990c840880e49e17dbe8832726b0eb6ea38f7f21970a44fd49ebd85d3b7e12b91e6
@@ -10,11 +10,12 @@ jobs:
10
10
  strategy:
11
11
  matrix:
12
12
  ruby:
13
- - '2.7.2'
14
- - '3.0.0'
13
+ - '3.0.5'
14
+ - '3.1.4'
15
+ - '3.2.2'
15
16
  activerecord:
16
- - '6.0.0'
17
17
  - '6.1.0'
18
+ - '7.0.0'
18
19
 
19
20
  env:
20
21
  BUNDLE_GEMFILE: ${{ github.workspace }}/gemfiles/activerecord_${{ matrix.activerecord }}.gemfile
data/.rubocop.yml CHANGED
@@ -2,7 +2,7 @@ require: rubocop-rspec
2
2
 
3
3
  AllCops:
4
4
  NewCops: enable
5
- TargetRubyVersion: 2.6
5
+ TargetRubyVersion: 3.0
6
6
  Exclude:
7
7
  - gemfiles/*
8
8
 
@@ -34,6 +34,9 @@ Style/EmptyMethod:
34
34
  Style/ExponentialNotation:
35
35
  Enabled: true
36
36
 
37
+ Style/FetchEnvVar:
38
+ Enabled: false
39
+
37
40
  Style/FormatStringToken:
38
41
  EnforcedStyle: template
39
42
 
data/.tool-versions CHANGED
@@ -1 +1 @@
1
- ruby 3.0.2
1
+ ruby 3.2.2
data/Appraisals CHANGED
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- appraise 'activerecord-6.0.0' do
4
- gem 'activerecord', '~> 6.0', '< 6.1'
5
- end
6
-
7
3
  appraise 'activerecord-6.1.0' do
8
4
  gem 'activerecord', '~> 6.1', '< 6.2'
9
5
  end
6
+
7
+ appraise 'activerecord-7.0.0' do
8
+ gem 'activerecord', '~> 7.0', '< 7.1'
9
+ end
data/Gemfile CHANGED
@@ -6,3 +6,12 @@ source 'https://rubygems.org'
6
6
  gemspec
7
7
 
8
8
  gem 'appraisal'
9
+ gem 'bundler', '~> 2.0'
10
+ gem 'combustion', '~> 1.3'
11
+ gem 'database_cleaner', '~> 2'
12
+ gem 'pry'
13
+ gem 'rake', '13.0.3'
14
+ gem 'rspec', '~> 3.0'
15
+ gem 'rubocop-rspec', '~> 2.3'
16
+ gem 'scenic', '~> 1.5'
17
+ gem 'simplecov', '~> 0.16'
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- table_saw (2.10.0)
4
+ table_saw (3.0.0)
5
5
  activerecord (>= 5.2)
6
6
  pg
7
7
  thor
@@ -9,131 +9,138 @@ PATH
9
9
  GEM
10
10
  remote: https://rubygems.org/
11
11
  specs:
12
- actionpack (6.1.4.1)
13
- actionview (= 6.1.4.1)
14
- activesupport (= 6.1.4.1)
15
- rack (~> 2.0, >= 2.0.9)
12
+ actionpack (7.0.5)
13
+ actionview (= 7.0.5)
14
+ activesupport (= 7.0.5)
15
+ rack (~> 2.0, >= 2.2.4)
16
16
  rack-test (>= 0.6.3)
17
17
  rails-dom-testing (~> 2.0)
18
18
  rails-html-sanitizer (~> 1.0, >= 1.2.0)
19
- actionview (6.1.4.1)
20
- activesupport (= 6.1.4.1)
19
+ actionview (7.0.5)
20
+ activesupport (= 7.0.5)
21
21
  builder (~> 3.1)
22
22
  erubi (~> 1.4)
23
23
  rails-dom-testing (~> 2.0)
24
24
  rails-html-sanitizer (~> 1.1, >= 1.2.0)
25
- activemodel (6.1.4.1)
26
- activesupport (= 6.1.4.1)
27
- activerecord (6.1.4.1)
28
- activemodel (= 6.1.4.1)
29
- activesupport (= 6.1.4.1)
30
- activesupport (6.1.4.1)
25
+ activemodel (7.0.5)
26
+ activesupport (= 7.0.5)
27
+ activerecord (7.0.5)
28
+ activemodel (= 7.0.5)
29
+ activesupport (= 7.0.5)
30
+ activesupport (7.0.5)
31
31
  concurrent-ruby (~> 1.0, >= 1.0.2)
32
32
  i18n (>= 1.6, < 2)
33
33
  minitest (>= 5.1)
34
34
  tzinfo (~> 2.0)
35
- zeitwerk (~> 2.3)
36
- appraisal (2.4.0)
35
+ appraisal (2.4.1)
37
36
  bundler
38
37
  rake
39
38
  thor (>= 0.14.0)
40
39
  ast (2.4.2)
41
40
  builder (3.2.4)
42
41
  coderay (1.1.3)
43
- combustion (1.3.1)
42
+ combustion (1.3.7)
44
43
  activesupport (>= 3.0.0)
45
44
  railties (>= 3.0.0)
46
45
  thor (>= 0.14.6)
47
- concurrent-ruby (1.1.9)
46
+ concurrent-ruby (1.2.2)
48
47
  crass (1.0.6)
49
- database_cleaner (2.0.1)
50
- database_cleaner-active_record (~> 2.0.0)
51
- database_cleaner-active_record (2.0.1)
48
+ database_cleaner (2.0.2)
49
+ database_cleaner-active_record (>= 2, < 3)
50
+ database_cleaner-active_record (2.1.0)
52
51
  activerecord (>= 5.a)
53
52
  database_cleaner-core (~> 2.0.0)
54
53
  database_cleaner-core (2.0.1)
55
- diff-lcs (1.4.4)
54
+ diff-lcs (1.5.0)
56
55
  docile (1.4.0)
57
- erubi (1.10.0)
58
- i18n (1.8.10)
56
+ erubi (1.12.0)
57
+ i18n (1.13.0)
59
58
  concurrent-ruby (~> 1.0)
60
- loofah (2.12.0)
59
+ json (2.6.3)
60
+ loofah (2.21.3)
61
61
  crass (~> 1.0.2)
62
- nokogiri (>= 1.5.9)
62
+ nokogiri (>= 1.12.0)
63
63
  method_source (1.0.0)
64
- mini_portile2 (2.6.1)
65
- minitest (5.14.4)
66
- nokogiri (1.12.5)
67
- mini_portile2 (~> 2.6.1)
64
+ mini_portile2 (2.8.2)
65
+ minitest (5.18.0)
66
+ nokogiri (1.15.2)
67
+ mini_portile2 (~> 2.8.2)
68
68
  racc (~> 1.4)
69
- parallel (1.20.1)
70
- parser (3.0.1.1)
69
+ parallel (1.23.0)
70
+ parser (3.2.2.1)
71
71
  ast (~> 2.4.1)
72
- pg (1.2.3)
73
- pry (0.14.1)
72
+ pg (1.5.3)
73
+ pry (0.14.2)
74
74
  coderay (~> 1.1)
75
75
  method_source (~> 1.0)
76
- racc (1.5.2)
77
- rack (2.2.3)
78
- rack-test (1.1.0)
79
- rack (>= 1.0, < 3)
76
+ racc (1.6.2)
77
+ rack (2.2.7)
78
+ rack-test (2.1.0)
79
+ rack (>= 1.3)
80
80
  rails-dom-testing (2.0.3)
81
81
  activesupport (>= 4.2.0)
82
82
  nokogiri (>= 1.6)
83
- rails-html-sanitizer (1.4.2)
84
- loofah (~> 2.3)
85
- railties (6.1.4.1)
86
- actionpack (= 6.1.4.1)
87
- activesupport (= 6.1.4.1)
83
+ rails-html-sanitizer (1.5.0)
84
+ loofah (~> 2.19, >= 2.19.1)
85
+ railties (7.0.5)
86
+ actionpack (= 7.0.5)
87
+ activesupport (= 7.0.5)
88
88
  method_source
89
- rake (>= 0.13)
89
+ rake (>= 12.2)
90
90
  thor (~> 1.0)
91
- rainbow (3.0.0)
91
+ zeitwerk (~> 2.5)
92
+ rainbow (3.1.1)
92
93
  rake (13.0.3)
93
- regexp_parser (2.1.1)
94
+ regexp_parser (2.8.0)
94
95
  rexml (3.2.5)
95
- rspec (3.10.0)
96
- rspec-core (~> 3.10.0)
97
- rspec-expectations (~> 3.10.0)
98
- rspec-mocks (~> 3.10.0)
99
- rspec-core (3.10.1)
100
- rspec-support (~> 3.10.0)
101
- rspec-expectations (3.10.1)
96
+ rspec (3.12.0)
97
+ rspec-core (~> 3.12.0)
98
+ rspec-expectations (~> 3.12.0)
99
+ rspec-mocks (~> 3.12.0)
100
+ rspec-core (3.12.2)
101
+ rspec-support (~> 3.12.0)
102
+ rspec-expectations (3.12.3)
102
103
  diff-lcs (>= 1.2.0, < 2.0)
103
- rspec-support (~> 3.10.0)
104
- rspec-mocks (3.10.2)
104
+ rspec-support (~> 3.12.0)
105
+ rspec-mocks (3.12.5)
105
106
  diff-lcs (>= 1.2.0, < 2.0)
106
- rspec-support (~> 3.10.0)
107
- rspec-support (3.10.2)
108
- rubocop (1.14.0)
107
+ rspec-support (~> 3.12.0)
108
+ rspec-support (3.12.0)
109
+ rubocop (1.51.0)
110
+ json (~> 2.3)
109
111
  parallel (~> 1.10)
110
- parser (>= 3.0.0.0)
112
+ parser (>= 3.2.0.0)
111
113
  rainbow (>= 2.2.2, < 4.0)
112
114
  regexp_parser (>= 1.8, < 3.0)
113
- rexml
114
- rubocop-ast (>= 1.5.0, < 2.0)
115
+ rexml (>= 3.2.5, < 4.0)
116
+ rubocop-ast (>= 1.28.0, < 2.0)
115
117
  ruby-progressbar (~> 1.7)
116
- unicode-display_width (>= 1.4.0, < 3.0)
117
- rubocop-ast (1.5.0)
118
- parser (>= 3.0.1.1)
119
- rubocop-rspec (2.3.0)
120
- rubocop (~> 1.0)
121
- rubocop-ast (>= 1.1.0)
122
- ruby-progressbar (1.11.0)
123
- scenic (1.5.4)
118
+ unicode-display_width (>= 2.4.0, < 3.0)
119
+ rubocop-ast (1.28.1)
120
+ parser (>= 3.2.1.0)
121
+ rubocop-capybara (2.18.0)
122
+ rubocop (~> 1.41)
123
+ rubocop-factory_bot (2.23.1)
124
+ rubocop (~> 1.33)
125
+ rubocop-rspec (2.22.0)
126
+ rubocop (~> 1.33)
127
+ rubocop-capybara (~> 2.17)
128
+ rubocop-factory_bot (~> 2.22)
129
+ ruby-progressbar (1.13.0)
130
+ scenic (1.7.0)
124
131
  activerecord (>= 4.0.0)
125
132
  railties (>= 4.0.0)
126
- simplecov (0.21.2)
133
+ simplecov (0.22.0)
127
134
  docile (~> 1.1)
128
135
  simplecov-html (~> 0.11)
129
136
  simplecov_json_formatter (~> 0.1)
130
137
  simplecov-html (0.12.3)
131
- simplecov_json_formatter (0.1.3)
132
- thor (1.1.0)
133
- tzinfo (2.0.4)
138
+ simplecov_json_formatter (0.1.4)
139
+ thor (1.2.2)
140
+ tzinfo (2.0.6)
134
141
  concurrent-ruby (~> 1.0)
135
- unicode-display_width (2.0.0)
136
- zeitwerk (2.4.2)
142
+ unicode-display_width (2.4.2)
143
+ zeitwerk (2.6.8)
137
144
 
138
145
  PLATFORMS
139
146
  ruby
@@ -152,4 +159,4 @@ DEPENDENCIES
152
159
  table_saw!
153
160
 
154
161
  BUNDLED WITH
155
- 2.2.22
162
+ 2.4.13
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
  [![Maintainability](https://api.codeclimate.com/v1/badges/abd5b5451c764d3249f1/maintainability)](https://codeclimate.com/github/hasghari/table_saw/maintainability)
3
3
  [![Test Coverage](https://api.codeclimate.com/v1/badges/abd5b5451c764d3249f1/test_coverage)](https://codeclimate.com/github/hasghari/table_saw/test_coverage)
4
4
 
5
- # TableSaw
5
+ # table-saw
6
6
 
7
7
  This gem creates a PSQL dump file (data only) from a Postgres database by only dumping a subset of data defined by a
8
8
  manifest file.
@@ -50,52 +50,188 @@ Options:
50
50
  -o, [--output=OUTPUT] # Default value is 'output.dump'
51
51
  ```
52
52
 
53
- The manifest file describes which tables you want to dump from your Postgres database:
53
+ ### Manifest
54
+
55
+ The manifest is a YAML file that describes the dataset to be exported to a dump file. At the top level, the manifest
56
+ file supports 4 nodes:
57
+
58
+ - [variables](#variables)
59
+ - [tables](#tables)
60
+ - [has_many](#has_many)
61
+ - [foreign_keys](#foreign_keys)
62
+
63
+ The examples that follow assume you have a database set up as follows:
64
+ ```sql
65
+ CREATE TABLE authors (
66
+ id bigint NOT NULL,
67
+ name character varying NOT NULL,
68
+ PRIMARY KEY (id)
69
+ );
70
+
71
+ CREATE TABLE books (
72
+ id bigint NOT NULL,
73
+ author_id bigint NOT NULL,
74
+ name character varying NOT NULL,
75
+ votes integer DEFAULT 0 NOT NULL,
76
+ PRIMARY KEY (id),
77
+ CONSTRAINT fk_author FOREIGN KEY (author_id) REFERENCES authors(id)
78
+ );
79
+
80
+ INSERT INTO authors (id, name) VALUES (1, 'Dan Brown');
81
+ INSERT INTO authors (id, name) VALUES (2, 'J. K. Rowling');
82
+
83
+ INSERT INTO books (id, author_id, name, votes) VALUES (1, 1, 'Angels and Demons', 10);
84
+ INSERT INTO books (id, author_id, name, votes) VALUES (2, 1, 'Digital Fortress', 35);
85
+ INSERT INTO books (id, author_id, name, votes) VALUES (3, 1, 'The Da Vinci Code', 50);
86
+ INSERT INTO books (id, author_id, name, votes) VALUES (4, 2, 'Philosopher''s Stone', 55);
87
+ INSERT INTO books (id, author_id, name, votes) VALUES (5, 2, 'Chamber of Secrets', 5);
88
+ INSERT INTO books (id, author_id, name, votes) VALUES (6, 2, 'Prisoner of Azkaban', 25);
89
+ ```
90
+
91
+ #### variables
92
+ Variables allow you parameterize the queries in the manifest. You can use the `%{variable}` substitution pattern in your
93
+ query strings:
54
94
 
55
95
  ```yaml
56
96
  variables:
57
- author_id: 24
97
+ author_id: 2
58
98
  tables:
59
99
  - table: books
60
100
  query: "select * from books where author_id = %{author_id}"
61
101
  ```
62
102
 
63
- This will only fetch records from the `books` table where `author_id = 24` and will also fetch the record from the
64
- `authors` table where `id = 24`.
65
-
66
- Assuming there is a `chapters` table with a foreign key reference of `book_id` to the `books` table, the above manifest
67
- file will **not** automatically retrieve those records. If `chapters` records are also desired, there a couple ways to
68
- accomplish this:
103
+ Additionally, you can now use the `%{variable}` substitution pattern in other variables:
69
104
 
70
105
  ```yaml
71
106
  variables:
72
- author_id: 24
107
+ author_id: '1,3,4',
108
+ book_ids: 'select * from books where author_id in (%{author_id})'
109
+ ```
110
+
111
+ #### tables
112
+ This is where you list the specific tables that you want to export. If you only specify the `table` without providing a
113
+ `query`, then the **entire** table will be exported. However, if you specify a `query`, then only rows matching that
114
+ query will be exported:
115
+
116
+ ```yaml
73
117
  tables:
74
118
  - table: books
75
- query: "select * from books where author_id = %{author_id}"
119
+ query: "select * from books where author_id = 2"
120
+ ```
121
+
122
+ The above manifest will only export rows from the `books` table where `author_id = 2`. In addition, due to the
123
+ foreign key constraint defined from `books(author_id)` to `authors(id)`, table-saw will automatically export the row
124
+ from the `authors` table where `id = 2` in order to preserve referential integrity.
125
+
126
+ The above manifest can alternatively be written as follows where exactly the same rows would be exported:
127
+
128
+ ```yaml
129
+ tables:
130
+ - table: authors
131
+ query: "select * from authors where id = 2"
132
+ has_many:
133
+ - books
134
+ ```
135
+
136
+ Notice we have to explicitly list the `has_many` association to the `books` table. Since `authors` does not have a
137
+ dependency on `books` to preserve referential integrity, table-saw by design will not export the associated `books`
138
+ rows in order to keep the output dump file as small as possible. In other words, if we eliminate the `has_many` node
139
+ from the manifest above, table-saw will only export a single author with `id = 2`.
140
+
141
+ #### has_many
142
+ This is where we define which optional associations we want to export for each table:
143
+
144
+ ```yaml
145
+ tables:
146
+ - table: authors
147
+ query: "select * from authors where id = 1"
148
+ - table: books
149
+ query: "select * from books where id = 6"
76
150
  has_many:
77
- books:
78
- - chapters
151
+ authors:
152
+ - books
79
153
  ```
80
154
 
81
- or
155
+ The above manifest would export the following rows:
156
+
157
+ * authors:
158
+ - `id = 1` because of the explicit query in the manifest
159
+ - `id = 2` because it is the `author_id` for `books` with `id = 6`
160
+
161
+ * books:
162
+ - `id = [1, 2, 3]` due to the `has_many` association for `authors` with `id = 1`
163
+ - `id = [4, 5, 6]` due to the `has_many` association for `authors` with `id = 2`
164
+
165
+ Now, if instead of defining the `has_many` node at the top level, we define it under the `authors` table as follows:
82
166
 
83
167
  ```yaml
84
- variables:
85
- author_id: 24
86
168
  tables:
87
- - table: chapters
88
- query: "select * from chapters inner join books on books.id = chapters.book_id where books.author_id = %{author_id}"
169
+ - table: authors
170
+ query: "select * from authors where id = 1"
171
+ has_many:
172
+ - books
173
+ - table: books
174
+ query: "select * from books where id = 6"
175
+ ```
176
+
177
+ The above manifest would export the following rows:
178
+
179
+ * authors:
180
+ - `id = 1` because of the explicit query in the manifest
181
+ - `id = 2` because it is the `author_id` for `books` with `id = 6`
182
+
183
+ * books:
184
+ - `id = [1, 2, 3]` due to the `has_many` association for `authors` with `id = 1`
185
+ - `id = 6` due to the explicit query in the manifest
186
+
187
+ One potential pitfall with using `has_many` is that you end up pulling in too many associated rows when all you wanted
188
+ was a limited number. table-saw allows you to specify a `scope` and `limit`:
189
+
190
+ ```yaml
191
+ tables:
192
+ - table: authors
193
+ query: "select * from authors where id = 1"
194
+ - table: books
195
+ query: "select * from books where id = 6"
196
+ has_many:
197
+ authors:
198
+ - books:
199
+ scope: "votes > 30"
200
+ limit: 1
201
+ ```
202
+
203
+ The above manifest would export the following rows:
204
+
205
+ * authors:
206
+ - `id = 1` because of the explicit query in the manifest
207
+ - `id = 2` because it is the `author_id` for `books` with `id = 6`
208
+
209
+ * books:
210
+ - Either `id = 2` or `id = 3` for `author_id = 1` since they both have `vote > 30` and the limit of 1 will randomly
211
+ choose one of them
212
+ - Only `id = 4` for `author_id = 2` since it's the only book for that author with `vote > 30`
213
+
214
+ #### foreign_keys
215
+ By default, table-saw will query the Postgres `information_schema` to look up the foreign key constraints and determine
216
+ whether it needs to export associated rows. However, if your database schema does not define foreign key constraints for
217
+ the tables you would like to export, you can manually define them in the manifest. Assuming we had not defined any
218
+ foreign key constraints for the `books` table, we could specify it in the manifest as follows:
219
+
220
+ ```yaml
221
+ foreign_keys:
222
+ - from_table: books
223
+ from_column: author_id
224
+ to_table: authors
225
+ to_column: id
89
226
  ```
90
227
 
91
- The output of the 2 manifest files above are exactly the same. The dump file will contain all the relevant records from
92
- the `authors`, `books` and `chapters` tables.
228
+ ## Dump and Restore
93
229
 
94
230
  Once your dump file has been created, you can import the data using `psql`:
95
231
 
96
232
  ```bash
97
- table-saw dump -m manifest.yml -o chapters.dump
98
- psql -h localhost -U postgres -d library < chapters.dump
233
+ table-saw dump -m manifest.yml -o library.dump
234
+ psql -h localhost -U postgres -d library < library.dump
99
235
  ```
100
236
 
101
237
  ## Development
@@ -3,6 +3,15 @@
3
3
  source "https://rubygems.org"
4
4
 
5
5
  gem "appraisal"
6
+ gem "bundler", "~> 2.0"
7
+ gem "combustion", "~> 1.3"
8
+ gem "database_cleaner", "~> 2"
9
+ gem "pry"
10
+ gem "rake", "13.0.3"
11
+ gem "rspec", "~> 3.0"
12
+ gem "rubocop-rspec", "~> 2.3"
13
+ gem "scenic", "~> 1.5"
14
+ gem "simplecov", "~> 0.16"
6
15
  gem "activerecord", "~> 6.1", "< 6.2"
7
16
 
8
17
  gemspec path: "../"