activerecord-duckdb 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,131 @@
1
+ # Rails Query Execution Architecture
2
+
3
+ This document summarizes how query execution works across supported Rails versions (7.2, 8.0, 8.1) and how the DuckDB adapter integrates with it.
4
+
5
+ ## Query Execution Call Structure
6
+
7
+ ### Rails 7.2
8
+
9
+ ```
10
+ Model.find_by(...) / Model.create(...)
11
+ └── select_all / insert
12
+ └── internal_exec_query(sql, name, binds, prepare:, async:, allow_retry:)
13
+ └── [Adapter must implement - base class raises NotImplementedError]
14
+ ```
15
+
16
+ **Key point:** Adapters MUST implement `internal_exec_query` in Rails 7.2.
17
+
18
+ ### Rails 8.0 / 8.1
19
+
20
+ ```
21
+ Model.find_by(...) / Model.create(...)
22
+ └── select_all / insert
23
+ └── internal_exec_query(...) # Default implementation provided
24
+ └── cast_result(internal_execute(...))
25
+ └── raw_execute(...)
26
+ └── perform_query(raw_connection, sql, binds, ...)
27
+ └── [Adapter should implement]
28
+ ```
29
+
30
+ **Key point:** Rails 8.0+ provides a default `internal_exec_query` that delegates to `perform_query`. However, adapters can still override `internal_exec_query` directly.
31
+
32
+ ## DuckDB Adapter Implementation
33
+
34
+ The adapter uses **version-specific modules** to integrate optimally with each Rails version:
35
+
36
+ | Rails Version | Query Execution | Schema Statements |
37
+ |--------------|-----------------|-------------------|
38
+ | 7.2 | `DatabaseStatementsRails72` | `SchemaStatementsRails80` |
39
+ | 8.0 | `DatabaseStatementsRails8` | `SchemaStatementsRails80` |
40
+ | 8.1 | `DatabaseStatementsRails8` | `SchemaStatementsRails81` |
41
+
42
+ ### Query Execution Strategy
43
+
44
+ - **Rails 7.2**: Implements `internal_exec_query` (required - base class raises `NotImplementedError`)
45
+ - **Rails 8.0+**: Implements `raw_execute`, lets base class handle `internal_exec_query`
46
+
47
+ This allows Rails 8.x to use its native query infrastructure (logging, retries, async support).
48
+
49
+ ### Module Loading
50
+
51
+ ```ruby
52
+ # In duckdb_adapter.rb
53
+ if ActiveRecord::VERSION::MAJOR >= 8
54
+ require 'active_record/connection_adapters/duckdb/database_statements_rails8'
55
+ include Duckdb::DatabaseStatementsRails8
56
+ else
57
+ require 'active_record/connection_adapters/duckdb/database_statements_rails72'
58
+ include Duckdb::DatabaseStatementsRails72
59
+ end
60
+ ```
61
+
62
+ ## Column Class Signature Change (Rails 8.1)
63
+
64
+ Rails 8.1 introduced a breaking change to the `Column` class:
65
+
66
+ ```ruby
67
+ # Rails 7.2 / 8.0
68
+ def initialize(name, default, sql_type_metadata, null, default_function, ...)
69
+
70
+ # Rails 8.1+
71
+ def initialize(name, cast_type, default, sql_type_metadata, null, default_function, ...)
72
+ ```
73
+
74
+ The adapter handles this with version-specific schema statement modules:
75
+
76
+ ```ruby
77
+ # In duckdb_adapter.rb
78
+ if ActiveRecord::VERSION::MAJOR > 8 ||
79
+ (ActiveRecord::VERSION::MAJOR == 8 && ActiveRecord::VERSION::MINOR >= 1)
80
+ require 'active_record/connection_adapters/duckdb/schema_statements_rails81'
81
+ include Duckdb::SchemaStatementsRails81
82
+ else
83
+ require 'active_record/connection_adapters/duckdb/schema_statements_rails80'
84
+ include Duckdb::SchemaStatementsRails80
85
+ end
86
+ ```
87
+
88
+ Each module implements `new_column_from_field` with the correct Column constructor signature.
89
+
90
+ ## Testing with Appraisal
91
+
92
+ Run tests against all Rails versions:
93
+
94
+ ```bash
95
+ # All versions
96
+ bundle exec appraisal rspec
97
+
98
+ # Specific version
99
+ bundle exec appraisal rails-7.2 rspec
100
+ bundle exec appraisal rails-8.0 rspec
101
+ bundle exec appraisal rails-8.1 rspec
102
+ ```
103
+
104
+ ## Related Methods
105
+
106
+ | Method | Purpose | Module |
107
+ |--------|---------|--------|
108
+ | `internal_exec_query` | Execute query, return ActiveRecord::Result | Rails 7.2 only |
109
+ | `raw_execute` | Low-level query execution | Rails 8.0+ only |
110
+ | `execute` | Direct SQL execution, returns raw DuckDB result | Shared |
111
+ | `cast_result` | Convert DuckDB result to ActiveRecord::Result | Shared |
112
+ | `affected_rows` | Get row count from raw result | Shared |
113
+ | `new_column_from_field` | Create Column object from DB field info | Rails 8.0 / 8.1 |
114
+
115
+ ## File Structure
116
+
117
+ ```
118
+ lib/active_record/connection_adapters/duckdb/
119
+ ├── database_statements.rb # Shared: execute, cast_result, affected_rows
120
+ ├── database_statements_rails72.rb # Rails 7.2: internal_exec_query, exec_delete
121
+ ├── database_statements_rails8.rb # Rails 8.0+: raw_execute
122
+ ├── schema_statements.rb # Shared schema operations
123
+ ├── schema_statements_rails80.rb # Rails 7.2/8.0: new_column_from_field
124
+ └── schema_statements_rails81.rb # Rails 8.1+: new_column_from_field (with cast_type)
125
+ ```
126
+
127
+ ## Future Considerations
128
+
129
+ 1. **`write_query?` implementation** - Currently returns `false`. Should probably return `true` for INSERT/UPDATE/DELETE to properly invalidate caches.
130
+
131
+ 2. **SQLite3 adapter reference** - The SQLite3 adapter in Rails is a good reference implementation for in-process database adapters.
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file was generated by Appraisal
4
+
5
+ source 'https://rubygems.org'
6
+
7
+ gem 'activerecord', '~> 7.2.0'
8
+ gem 'irb'
9
+ gem 'rake', '~> 13.0'
10
+
11
+ group :development do
12
+ gem 'appraisal'
13
+ gem 'debug'
14
+ gem 'fasterer'
15
+ gem 'rubocop', '~> 1.76', require: false
16
+ gem 'rubocop-performance', '~> 1.25', require: false
17
+ gem 'rubocop-rake', '~> 0.7', require: false
18
+ gem 'rubocop-rspec', '~> 3.6', require: false
19
+ gem 'rubocop-thread_safety', require: false
20
+ gem 'sord'
21
+ end
22
+
23
+ group :test do
24
+ gem 'fuubar'
25
+ gem 'rspec', '~> 3.13'
26
+ gem 'simplecov', require: false
27
+ gem 'simplecov-tailwindcss', require: false
28
+ end
29
+
30
+ gemspec path: '../'
@@ -0,0 +1,196 @@
1
+ PATH
2
+ remote: ..
3
+ specs:
4
+ activerecord-duckdb (0.1.0)
5
+ activerecord (>= 7.0.0)
6
+ duckdb (>= 1.3, < 2.0)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ activemodel (7.2.3)
12
+ activesupport (= 7.2.3)
13
+ activerecord (7.2.3)
14
+ activemodel (= 7.2.3)
15
+ activesupport (= 7.2.3)
16
+ timeout (>= 0.4.0)
17
+ activesupport (7.2.3)
18
+ base64
19
+ benchmark (>= 0.3)
20
+ bigdecimal
21
+ concurrent-ruby (~> 1.0, >= 1.3.1)
22
+ connection_pool (>= 2.2.5)
23
+ drb
24
+ i18n (>= 1.6, < 2)
25
+ logger (>= 1.4.2)
26
+ minitest (>= 5.1)
27
+ securerandom (>= 0.3)
28
+ tzinfo (~> 2.0, >= 2.0.5)
29
+ appraisal (2.5.0)
30
+ bundler
31
+ rake
32
+ thor (>= 0.14.0)
33
+ ast (2.4.3)
34
+ base64 (0.3.0)
35
+ benchmark (0.5.0)
36
+ bigdecimal (4.0.1)
37
+ commander (5.0.0)
38
+ highline (~> 3.0.0)
39
+ concurrent-ruby (1.3.6)
40
+ connection_pool (3.0.2)
41
+ date (3.5.1)
42
+ debug (1.11.1)
43
+ irb (~> 1.10)
44
+ reline (>= 0.3.8)
45
+ diff-lcs (1.6.2)
46
+ docile (1.4.1)
47
+ drb (2.2.3)
48
+ duckdb (1.4.3.0)
49
+ bigdecimal (>= 3.1.4)
50
+ erb (6.0.1)
51
+ fasterer (0.11.0)
52
+ ruby_parser (>= 3.19.1)
53
+ fuubar (2.5.1)
54
+ rspec-core (~> 3.0)
55
+ ruby-progressbar (~> 1.4)
56
+ highline (3.0.1)
57
+ i18n (1.14.8)
58
+ concurrent-ruby (~> 1.0)
59
+ io-console (0.8.2)
60
+ irb (1.16.0)
61
+ pp (>= 0.6.0)
62
+ rdoc (>= 4.0.0)
63
+ reline (>= 0.4.2)
64
+ json (2.18.0)
65
+ language_server-protocol (3.17.0.5)
66
+ lint_roller (1.1.0)
67
+ logger (1.7.0)
68
+ minitest (6.0.1)
69
+ prism (~> 1.5)
70
+ parallel (1.27.0)
71
+ parlour (9.1.2)
72
+ commander (~> 5.0)
73
+ parser
74
+ rainbow (~> 3.0)
75
+ sorbet-runtime (>= 0.5)
76
+ parser (3.3.10.0)
77
+ ast (~> 2.4.1)
78
+ racc
79
+ pp (0.6.3)
80
+ prettyprint
81
+ prettyprint (0.2.0)
82
+ prism (1.8.0)
83
+ psych (5.3.1)
84
+ date
85
+ stringio
86
+ racc (1.8.1)
87
+ rainbow (3.1.1)
88
+ rake (13.3.1)
89
+ rbs (3.10.2)
90
+ logger
91
+ rdoc (7.1.0)
92
+ erb
93
+ psych (>= 4.0.0)
94
+ tsort
95
+ regexp_parser (2.11.3)
96
+ reline (0.6.3)
97
+ io-console (~> 0.5)
98
+ rspec (3.13.2)
99
+ rspec-core (~> 3.13.0)
100
+ rspec-expectations (~> 3.13.0)
101
+ rspec-mocks (~> 3.13.0)
102
+ rspec-core (3.13.6)
103
+ rspec-support (~> 3.13.0)
104
+ rspec-expectations (3.13.5)
105
+ diff-lcs (>= 1.2.0, < 2.0)
106
+ rspec-support (~> 3.13.0)
107
+ rspec-mocks (3.13.7)
108
+ diff-lcs (>= 1.2.0, < 2.0)
109
+ rspec-support (~> 3.13.0)
110
+ rspec-support (3.13.6)
111
+ rubocop (1.82.1)
112
+ json (~> 2.3)
113
+ language_server-protocol (~> 3.17.0.2)
114
+ lint_roller (~> 1.1.0)
115
+ parallel (~> 1.10)
116
+ parser (>= 3.3.0.2)
117
+ rainbow (>= 2.2.2, < 4.0)
118
+ regexp_parser (>= 2.9.3, < 3.0)
119
+ rubocop-ast (>= 1.48.0, < 2.0)
120
+ ruby-progressbar (~> 1.7)
121
+ unicode-display_width (>= 2.4.0, < 4.0)
122
+ rubocop-ast (1.49.0)
123
+ parser (>= 3.3.7.2)
124
+ prism (~> 1.7)
125
+ rubocop-performance (1.26.1)
126
+ lint_roller (~> 1.1)
127
+ rubocop (>= 1.75.0, < 2.0)
128
+ rubocop-ast (>= 1.47.1, < 2.0)
129
+ rubocop-rake (0.7.1)
130
+ lint_roller (~> 1.1)
131
+ rubocop (>= 1.72.1)
132
+ rubocop-rspec (3.9.0)
133
+ lint_roller (~> 1.1)
134
+ rubocop (~> 1.81)
135
+ rubocop-thread_safety (0.7.3)
136
+ lint_roller (~> 1.1)
137
+ rubocop (~> 1.72, >= 1.72.1)
138
+ rubocop-ast (>= 1.44.0, < 2.0)
139
+ ruby-progressbar (1.13.0)
140
+ ruby_parser (3.22.0)
141
+ racc (~> 1.5)
142
+ sexp_processor (~> 4.16)
143
+ securerandom (0.4.1)
144
+ sexp_processor (4.17.5)
145
+ simplecov (0.22.0)
146
+ docile (~> 1.1)
147
+ simplecov-html (~> 0.11)
148
+ simplecov_json_formatter (~> 0.1)
149
+ simplecov-html (0.13.2)
150
+ simplecov-tailwindcss (2.3.0)
151
+ simplecov (~> 0.16)
152
+ simplecov_json_formatter (0.1.4)
153
+ sorbet-runtime (0.6.12885)
154
+ sord (7.1.0)
155
+ commander (~> 5.0)
156
+ parlour (~> 9.1)
157
+ parser
158
+ rbs (>= 3.0, < 5)
159
+ sorbet-runtime
160
+ yard
161
+ stringio (3.2.0)
162
+ thor (1.5.0)
163
+ timeout (0.6.0)
164
+ tsort (0.2.0)
165
+ tzinfo (2.0.6)
166
+ concurrent-ruby (~> 1.0)
167
+ unicode-display_width (3.2.0)
168
+ unicode-emoji (~> 4.1)
169
+ unicode-emoji (4.2.0)
170
+ yard (0.9.38)
171
+
172
+ PLATFORMS
173
+ arm64-darwin-23
174
+ ruby
175
+
176
+ DEPENDENCIES
177
+ activerecord (~> 7.2.0)
178
+ activerecord-duckdb!
179
+ appraisal
180
+ debug
181
+ fasterer
182
+ fuubar
183
+ irb
184
+ rake (~> 13.0)
185
+ rspec (~> 3.13)
186
+ rubocop (~> 1.76)
187
+ rubocop-performance (~> 1.25)
188
+ rubocop-rake (~> 0.7)
189
+ rubocop-rspec (~> 3.6)
190
+ rubocop-thread_safety
191
+ simplecov
192
+ simplecov-tailwindcss
193
+ sord
194
+
195
+ BUNDLED WITH
196
+ 2.6.9
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file was generated by Appraisal
4
+
5
+ source 'https://rubygems.org'
6
+
7
+ gem 'activerecord', '~> 8.0.0'
8
+ gem 'irb'
9
+ gem 'rake', '~> 13.0'
10
+
11
+ group :development do
12
+ gem 'appraisal'
13
+ gem 'debug'
14
+ gem 'fasterer'
15
+ gem 'rubocop', '~> 1.76', require: false
16
+ gem 'rubocop-performance', '~> 1.25', require: false
17
+ gem 'rubocop-rake', '~> 0.7', require: false
18
+ gem 'rubocop-rspec', '~> 3.6', require: false
19
+ gem 'rubocop-thread_safety', require: false
20
+ gem 'sord'
21
+ end
22
+
23
+ group :test do
24
+ gem 'fuubar'
25
+ gem 'rspec', '~> 3.13'
26
+ gem 'simplecov', require: false
27
+ gem 'simplecov-tailwindcss', require: false
28
+ end
29
+
30
+ gemspec path: '../'
@@ -0,0 +1,198 @@
1
+ PATH
2
+ remote: ..
3
+ specs:
4
+ activerecord-duckdb (0.1.0)
5
+ activerecord (>= 7.0.0)
6
+ duckdb (>= 1.3, < 2.0)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ activemodel (8.0.4)
12
+ activesupport (= 8.0.4)
13
+ activerecord (8.0.4)
14
+ activemodel (= 8.0.4)
15
+ activesupport (= 8.0.4)
16
+ timeout (>= 0.4.0)
17
+ activesupport (8.0.4)
18
+ base64
19
+ benchmark (>= 0.3)
20
+ bigdecimal
21
+ concurrent-ruby (~> 1.0, >= 1.3.1)
22
+ connection_pool (>= 2.2.5)
23
+ drb
24
+ i18n (>= 1.6, < 2)
25
+ logger (>= 1.4.2)
26
+ minitest (>= 5.1)
27
+ securerandom (>= 0.3)
28
+ tzinfo (~> 2.0, >= 2.0.5)
29
+ uri (>= 0.13.1)
30
+ appraisal (2.5.0)
31
+ bundler
32
+ rake
33
+ thor (>= 0.14.0)
34
+ ast (2.4.3)
35
+ base64 (0.3.0)
36
+ benchmark (0.5.0)
37
+ bigdecimal (4.0.1)
38
+ commander (5.0.0)
39
+ highline (~> 3.0.0)
40
+ concurrent-ruby (1.3.6)
41
+ connection_pool (3.0.2)
42
+ date (3.5.1)
43
+ debug (1.11.1)
44
+ irb (~> 1.10)
45
+ reline (>= 0.3.8)
46
+ diff-lcs (1.6.2)
47
+ docile (1.4.1)
48
+ drb (2.2.3)
49
+ duckdb (1.4.3.0)
50
+ bigdecimal (>= 3.1.4)
51
+ erb (6.0.1)
52
+ fasterer (0.11.0)
53
+ ruby_parser (>= 3.19.1)
54
+ fuubar (2.5.1)
55
+ rspec-core (~> 3.0)
56
+ ruby-progressbar (~> 1.4)
57
+ highline (3.0.1)
58
+ i18n (1.14.8)
59
+ concurrent-ruby (~> 1.0)
60
+ io-console (0.8.2)
61
+ irb (1.16.0)
62
+ pp (>= 0.6.0)
63
+ rdoc (>= 4.0.0)
64
+ reline (>= 0.4.2)
65
+ json (2.18.0)
66
+ language_server-protocol (3.17.0.5)
67
+ lint_roller (1.1.0)
68
+ logger (1.7.0)
69
+ minitest (6.0.1)
70
+ prism (~> 1.5)
71
+ parallel (1.27.0)
72
+ parlour (9.1.2)
73
+ commander (~> 5.0)
74
+ parser
75
+ rainbow (~> 3.0)
76
+ sorbet-runtime (>= 0.5)
77
+ parser (3.3.10.0)
78
+ ast (~> 2.4.1)
79
+ racc
80
+ pp (0.6.3)
81
+ prettyprint
82
+ prettyprint (0.2.0)
83
+ prism (1.8.0)
84
+ psych (5.3.1)
85
+ date
86
+ stringio
87
+ racc (1.8.1)
88
+ rainbow (3.1.1)
89
+ rake (13.3.1)
90
+ rbs (3.10.2)
91
+ logger
92
+ rdoc (7.1.0)
93
+ erb
94
+ psych (>= 4.0.0)
95
+ tsort
96
+ regexp_parser (2.11.3)
97
+ reline (0.6.3)
98
+ io-console (~> 0.5)
99
+ rspec (3.13.2)
100
+ rspec-core (~> 3.13.0)
101
+ rspec-expectations (~> 3.13.0)
102
+ rspec-mocks (~> 3.13.0)
103
+ rspec-core (3.13.6)
104
+ rspec-support (~> 3.13.0)
105
+ rspec-expectations (3.13.5)
106
+ diff-lcs (>= 1.2.0, < 2.0)
107
+ rspec-support (~> 3.13.0)
108
+ rspec-mocks (3.13.7)
109
+ diff-lcs (>= 1.2.0, < 2.0)
110
+ rspec-support (~> 3.13.0)
111
+ rspec-support (3.13.6)
112
+ rubocop (1.82.1)
113
+ json (~> 2.3)
114
+ language_server-protocol (~> 3.17.0.2)
115
+ lint_roller (~> 1.1.0)
116
+ parallel (~> 1.10)
117
+ parser (>= 3.3.0.2)
118
+ rainbow (>= 2.2.2, < 4.0)
119
+ regexp_parser (>= 2.9.3, < 3.0)
120
+ rubocop-ast (>= 1.48.0, < 2.0)
121
+ ruby-progressbar (~> 1.7)
122
+ unicode-display_width (>= 2.4.0, < 4.0)
123
+ rubocop-ast (1.49.0)
124
+ parser (>= 3.3.7.2)
125
+ prism (~> 1.7)
126
+ rubocop-performance (1.26.1)
127
+ lint_roller (~> 1.1)
128
+ rubocop (>= 1.75.0, < 2.0)
129
+ rubocop-ast (>= 1.47.1, < 2.0)
130
+ rubocop-rake (0.7.1)
131
+ lint_roller (~> 1.1)
132
+ rubocop (>= 1.72.1)
133
+ rubocop-rspec (3.9.0)
134
+ lint_roller (~> 1.1)
135
+ rubocop (~> 1.81)
136
+ rubocop-thread_safety (0.7.3)
137
+ lint_roller (~> 1.1)
138
+ rubocop (~> 1.72, >= 1.72.1)
139
+ rubocop-ast (>= 1.44.0, < 2.0)
140
+ ruby-progressbar (1.13.0)
141
+ ruby_parser (3.22.0)
142
+ racc (~> 1.5)
143
+ sexp_processor (~> 4.16)
144
+ securerandom (0.4.1)
145
+ sexp_processor (4.17.5)
146
+ simplecov (0.22.0)
147
+ docile (~> 1.1)
148
+ simplecov-html (~> 0.11)
149
+ simplecov_json_formatter (~> 0.1)
150
+ simplecov-html (0.13.2)
151
+ simplecov-tailwindcss (2.3.0)
152
+ simplecov (~> 0.16)
153
+ simplecov_json_formatter (0.1.4)
154
+ sorbet-runtime (0.6.12885)
155
+ sord (7.1.0)
156
+ commander (~> 5.0)
157
+ parlour (~> 9.1)
158
+ parser
159
+ rbs (>= 3.0, < 5)
160
+ sorbet-runtime
161
+ yard
162
+ stringio (3.2.0)
163
+ thor (1.5.0)
164
+ timeout (0.6.0)
165
+ tsort (0.2.0)
166
+ tzinfo (2.0.6)
167
+ concurrent-ruby (~> 1.0)
168
+ unicode-display_width (3.2.0)
169
+ unicode-emoji (~> 4.1)
170
+ unicode-emoji (4.2.0)
171
+ uri (1.1.1)
172
+ yard (0.9.38)
173
+
174
+ PLATFORMS
175
+ arm64-darwin-23
176
+ ruby
177
+
178
+ DEPENDENCIES
179
+ activerecord (~> 8.0.0)
180
+ activerecord-duckdb!
181
+ appraisal
182
+ debug
183
+ fasterer
184
+ fuubar
185
+ irb
186
+ rake (~> 13.0)
187
+ rspec (~> 3.13)
188
+ rubocop (~> 1.76)
189
+ rubocop-performance (~> 1.25)
190
+ rubocop-rake (~> 0.7)
191
+ rubocop-rspec (~> 3.6)
192
+ rubocop-thread_safety
193
+ simplecov
194
+ simplecov-tailwindcss
195
+ sord
196
+
197
+ BUNDLED WITH
198
+ 2.6.9
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file was generated by Appraisal
4
+
5
+ source 'https://rubygems.org'
6
+
7
+ gem 'activerecord', '~> 8.1.0'
8
+ gem 'irb'
9
+ gem 'rake', '~> 13.0'
10
+
11
+ group :development do
12
+ gem 'appraisal'
13
+ gem 'debug'
14
+ gem 'fasterer'
15
+ gem 'rubocop', '~> 1.76', require: false
16
+ gem 'rubocop-performance', '~> 1.25', require: false
17
+ gem 'rubocop-rake', '~> 0.7', require: false
18
+ gem 'rubocop-rspec', '~> 3.6', require: false
19
+ gem 'rubocop-thread_safety', require: false
20
+ gem 'sord'
21
+ end
22
+
23
+ group :test do
24
+ gem 'fuubar'
25
+ gem 'rspec', '~> 3.13'
26
+ gem 'simplecov', require: false
27
+ gem 'simplecov-tailwindcss', require: false
28
+ end
29
+
30
+ gemspec path: '../'