inquirex 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 5a1021c83f11e037c69736dd5fa9228d95af53c9f7967173dd21bf9b096d4f31
4
+ data.tar.gz: 5b3cec5558da5aa1f9c3850e77ff6021916b9652531862ba621b8a7ea65fd5a7
5
+ SHA512:
6
+ metadata.gz: aba4ff0487e52177d96fdf385c3268f4ea3c888c7e0e711547ec7b78700c7b5b377d573235823a5d8fe2861553006bed2b7573cdc9c8c38c2fc480ae0af81aa9
7
+ data.tar.gz: d1f7ccf5705755923d240414abb3e688534a618a36f9607fd60d6a526b72886c5d7fd86d8b1b93589edf16f7627c0ac2c26be4d7d42f1a4109f9f0bf1376ee2e
@@ -0,0 +1,153 @@
1
+ # Relaxed.Ruby.Style
2
+ ## Version 2.5
3
+
4
+ Style/Alias:
5
+ Enabled: false
6
+ StyleGuide: https://relaxed.ruby.style/#stylealias
7
+
8
+ Style/AsciiComments:
9
+ Enabled: false
10
+ StyleGuide: https://relaxed.ruby.style/#styleasciicomments
11
+
12
+ Style/BeginBlock:
13
+ Enabled: false
14
+ StyleGuide: https://relaxed.ruby.style/#stylebeginblock
15
+
16
+ Style/BlockDelimiters:
17
+ Enabled: false
18
+ StyleGuide: https://relaxed.ruby.style/#styleblockdelimiters
19
+
20
+ Style/CommentAnnotation:
21
+ Enabled: false
22
+ StyleGuide: https://relaxed.ruby.style/#stylecommentannotation
23
+
24
+ Style/Documentation:
25
+ Enabled: false
26
+ StyleGuide: https://relaxed.ruby.style/#styledocumentation
27
+
28
+ Layout/DotPosition:
29
+ Enabled: false
30
+ StyleGuide: https://relaxed.ruby.style/#layoutdotposition
31
+
32
+ Style/DoubleNegation:
33
+ Enabled: false
34
+ StyleGuide: https://relaxed.ruby.style/#styledoublenegation
35
+
36
+ Style/EndBlock:
37
+ Enabled: false
38
+ StyleGuide: https://relaxed.ruby.style/#styleendblock
39
+
40
+ Style/FormatString:
41
+ Enabled: false
42
+ StyleGuide: https://relaxed.ruby.style/#styleformatstring
43
+
44
+ Style/IfUnlessModifier:
45
+ Enabled: false
46
+ StyleGuide: https://relaxed.ruby.style/#styleifunlessmodifier
47
+
48
+ Style/Lambda:
49
+ Enabled: false
50
+ StyleGuide: https://relaxed.ruby.style/#stylelambda
51
+
52
+ Style/ModuleFunction:
53
+ Enabled: false
54
+ StyleGuide: https://relaxed.ruby.style/#stylemodulefunction
55
+
56
+ Style/MultilineBlockChain:
57
+ Enabled: false
58
+ StyleGuide: https://relaxed.ruby.style/#stylemultilineblockchain
59
+
60
+ Style/NegatedIf:
61
+ Enabled: false
62
+ StyleGuide: https://relaxed.ruby.style/#stylenegatedif
63
+
64
+ Style/NegatedWhile:
65
+ Enabled: false
66
+ StyleGuide: https://relaxed.ruby.style/#stylenegatedwhile
67
+
68
+ Style/NumericPredicate:
69
+ Enabled: false
70
+ StyleGuide: https://relaxed.ruby.style/#stylenumericpredicate
71
+
72
+ Style/ParallelAssignment:
73
+ Enabled: false
74
+ StyleGuide: https://relaxed.ruby.style/#styleparallelassignment
75
+
76
+ Style/PercentLiteralDelimiters:
77
+ Enabled: false
78
+ StyleGuide: https://relaxed.ruby.style/#stylepercentliteraldelimiters
79
+
80
+ Style/PerlBackrefs:
81
+ Enabled: false
82
+ StyleGuide: https://relaxed.ruby.style/#styleperlbackrefs
83
+
84
+ Style/Semicolon:
85
+ Enabled: false
86
+ StyleGuide: https://relaxed.ruby.style/#stylesemicolon
87
+
88
+ Style/SignalException:
89
+ Enabled: false
90
+ StyleGuide: https://relaxed.ruby.style/#stylesignalexception
91
+
92
+ Style/SingleLineBlockParams:
93
+ Enabled: false
94
+ StyleGuide: https://relaxed.ruby.style/#stylesinglelineblockparams
95
+
96
+ Style/SingleLineMethods:
97
+ Enabled: false
98
+ StyleGuide: https://relaxed.ruby.style/#stylesinglelinemethods
99
+
100
+ Layout/SpaceBeforeBlockBraces:
101
+ Enabled: false
102
+ StyleGuide: https://relaxed.ruby.style/#layoutspacebeforeblockbraces
103
+
104
+ Layout/SpaceInsideParens:
105
+ Enabled: false
106
+ StyleGuide: https://relaxed.ruby.style/#layoutspaceinsideparens
107
+
108
+ Style/SpecialGlobalVars:
109
+ Enabled: false
110
+ StyleGuide: https://relaxed.ruby.style/#stylespecialglobalvars
111
+
112
+ Style/StringLiterals:
113
+ Enabled: false
114
+ StyleGuide: https://relaxed.ruby.style/#stylestringliterals
115
+
116
+ Style/TrailingCommaInArguments:
117
+ Enabled: false
118
+ StyleGuide: https://relaxed.ruby.style/#styletrailingcommainarguments
119
+
120
+ Style/TrailingCommaInArrayLiteral:
121
+ Enabled: false
122
+ StyleGuide: https://relaxed.ruby.style/#styletrailingcommainarrayliteral
123
+
124
+ Style/TrailingCommaInHashLiteral:
125
+ Enabled: false
126
+ StyleGuide: https://relaxed.ruby.style/#styletrailingcommainhashliteral
127
+
128
+ Style/SymbolArray:
129
+ Enabled: false
130
+ StyleGuide: http://relaxed.ruby.style/#stylesymbolarray
131
+
132
+ Style/WhileUntilModifier:
133
+ Enabled: false
134
+ StyleGuide: https://relaxed.ruby.style/#stylewhileuntilmodifier
135
+
136
+ Style/WordArray:
137
+ Enabled: false
138
+ StyleGuide: https://relaxed.ruby.style/#stylewordarray
139
+
140
+ Lint/AmbiguousRegexpLiteral:
141
+ Enabled: false
142
+ StyleGuide: https://relaxed.ruby.style/#lintambiguousregexpliteral
143
+
144
+ Lint/AssignmentInCondition:
145
+ Enabled: false
146
+ StyleGuide: https://relaxed.ruby.style/#lintassignmentincondition
147
+
148
+ Layout/LineLength:
149
+ Enabled: false
150
+
151
+ Metrics:
152
+ Enabled: false
153
+
data/.rub ADDED
File without changes
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 4.0.2
data/.secrets.baseline ADDED
@@ -0,0 +1,127 @@
1
+ {
2
+ "version": "1.5.0",
3
+ "plugins_used": [
4
+ {
5
+ "name": "ArtifactoryDetector"
6
+ },
7
+ {
8
+ "name": "AWSKeyDetector"
9
+ },
10
+ {
11
+ "name": "AzureStorageKeyDetector"
12
+ },
13
+ {
14
+ "name": "Base64HighEntropyString",
15
+ "limit": 4.5
16
+ },
17
+ {
18
+ "name": "BasicAuthDetector"
19
+ },
20
+ {
21
+ "name": "CloudantDetector"
22
+ },
23
+ {
24
+ "name": "DiscordBotTokenDetector"
25
+ },
26
+ {
27
+ "name": "GitHubTokenDetector"
28
+ },
29
+ {
30
+ "name": "GitLabTokenDetector"
31
+ },
32
+ {
33
+ "name": "HexHighEntropyString",
34
+ "limit": 3.0
35
+ },
36
+ {
37
+ "name": "IbmCloudIamDetector"
38
+ },
39
+ {
40
+ "name": "IbmCosHmacDetector"
41
+ },
42
+ {
43
+ "name": "IPPublicDetector"
44
+ },
45
+ {
46
+ "name": "JwtTokenDetector"
47
+ },
48
+ {
49
+ "name": "KeywordDetector",
50
+ "keyword_exclude": ""
51
+ },
52
+ {
53
+ "name": "MailchimpDetector"
54
+ },
55
+ {
56
+ "name": "NpmDetector"
57
+ },
58
+ {
59
+ "name": "OpenAIDetector"
60
+ },
61
+ {
62
+ "name": "PrivateKeyDetector"
63
+ },
64
+ {
65
+ "name": "PypiTokenDetector"
66
+ },
67
+ {
68
+ "name": "SendGridDetector"
69
+ },
70
+ {
71
+ "name": "SlackDetector"
72
+ },
73
+ {
74
+ "name": "SoftlayerDetector"
75
+ },
76
+ {
77
+ "name": "SquareOAuthDetector"
78
+ },
79
+ {
80
+ "name": "StripeDetector"
81
+ },
82
+ {
83
+ "name": "TelegramBotTokenDetector"
84
+ },
85
+ {
86
+ "name": "TwilioKeyDetector"
87
+ }
88
+ ],
89
+ "filters_used": [
90
+ {
91
+ "path": "detect_secrets.filters.allowlist.is_line_allowlisted"
92
+ },
93
+ {
94
+ "path": "detect_secrets.filters.common.is_ignored_due_to_verification_policies",
95
+ "min_level": 2
96
+ },
97
+ {
98
+ "path": "detect_secrets.filters.heuristic.is_indirect_reference"
99
+ },
100
+ {
101
+ "path": "detect_secrets.filters.heuristic.is_likely_id_string"
102
+ },
103
+ {
104
+ "path": "detect_secrets.filters.heuristic.is_lock_file"
105
+ },
106
+ {
107
+ "path": "detect_secrets.filters.heuristic.is_not_alphanumeric_string"
108
+ },
109
+ {
110
+ "path": "detect_secrets.filters.heuristic.is_potential_uuid"
111
+ },
112
+ {
113
+ "path": "detect_secrets.filters.heuristic.is_prefixed_with_dollar_sign"
114
+ },
115
+ {
116
+ "path": "detect_secrets.filters.heuristic.is_sequential_string"
117
+ },
118
+ {
119
+ "path": "detect_secrets.filters.heuristic.is_swagger_file"
120
+ },
121
+ {
122
+ "path": "detect_secrets.filters.heuristic.is_templated_secret"
123
+ }
124
+ ],
125
+ "results": {},
126
+ "generated_at": "2026-04-13T21:29:03Z"
127
+ }
data/Brewfile ADDED
@@ -0,0 +1,7 @@
1
+ # Please run `brew bundle` before running `lefthook install`
2
+
3
+ brew 'lefthook'
4
+ brew 'codespell'
5
+ brew 'mdformat'
6
+ brew 'detect-secrets'
7
+
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2026-04-13
4
+
5
+ - Initial release
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2026 Konstantin Gredeskoul
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 ADDED
@@ -0,0 +1,289 @@
1
+ # Inquirex
2
+
3
+ `inquirex` is a pure Ruby, declarative, rules-driven questionnaire engine for building conditional intake forms, qualification wizards, and branching surveys.
4
+
5
+ It is the core gem in the Inquirex ecosystem and focuses on:
6
+
7
+ - A conversational DSL (`ask`, `say`, `header`, `btw`, `warning`, `confirm`)
8
+ - A serializable AST rule system (`contains`, `equals`, `greater_than`, `less_than`, `not_empty`, `all`, `any`)
9
+ - An immutable flow definition graph
10
+ - A runtime engine for stateful step traversal
11
+ - JSON round-trip serialization for cross-platform clients
12
+ - A structured `Answers` wrapper and Mermaid graph export
13
+
14
+ ## Status
15
+
16
+ - Version: `0.1.0`
17
+ - Ruby: `>= 4.0.0` (project currently uses `4.0.2`)
18
+ - Test suite: `109 examples, 0 failures`
19
+ - Coverage: ~`92.5%` line coverage
20
+
21
+ ## Why Inquirex
22
+
23
+ Many form builders hard-code branching in controllers, React components, or database callbacks. Inquirex keeps flow logic in one portable graph:
24
+
25
+ - Define once in Ruby
26
+ - Serialize to JSON
27
+ - Evaluate transitions consistently using rule AST objects
28
+ - Run the same flow in different frontends (web widget, terminal, etc.)
29
+
30
+ This design is the foundation for the broader ecosystem (`inquirex-ui`, `inquirex-tty`, `inquirex-js`, `inquirex-llm`, `inquirex-rails`).
31
+
32
+ ## Installation
33
+
34
+ Add to your Gemfile:
35
+
36
+ ```ruby
37
+ gem "inquirex"
38
+ ```
39
+
40
+ Then install:
41
+
42
+ ```bash
43
+ bundle install
44
+ ```
45
+
46
+ ## Quick Start
47
+
48
+ ```ruby
49
+ require "inquirex"
50
+
51
+ definition = Inquirex.define id: "tax-intake-2025", version: "1.0.0" do
52
+ meta title: "Tax Preparation Intake", subtitle: "Let's understand your situation"
53
+ start :filing_status
54
+
55
+ ask :filing_status do
56
+ type :enum
57
+ question "What is your filing status?"
58
+ options single: "Single", married_jointly: "Married Filing Jointly"
59
+ transition to: :dependents
60
+ end
61
+
62
+ ask :dependents do
63
+ type :integer
64
+ question "How many dependents?"
65
+ default 0
66
+ transition to: :business_income
67
+ end
68
+
69
+ confirm :business_income do
70
+ question "Do you have business income?"
71
+ transition to: :business_count, if_rule: equals(:business_income, true)
72
+ transition to: :done
73
+ end
74
+
75
+ ask :business_count do
76
+ type :integer
77
+ question "How many businesses?"
78
+ transition to: :done
79
+ end
80
+
81
+ say :done do
82
+ text "Thanks for completing the intake."
83
+ end
84
+ end
85
+
86
+ engine = Inquirex::Engine.new(definition)
87
+
88
+ engine.answer("single") # filing_status
89
+ engine.answer(2) # dependents
90
+ engine.answer(false) # business_income
91
+ engine.advance # done (display step)
92
+
93
+ engine.finished? # => true
94
+ ```
95
+
96
+ ## DSL Overview
97
+
98
+ ### Flow-level methods
99
+
100
+ - `start :step_id` sets the entry step
101
+ - `meta title:, subtitle:, brand:` adds optional frontend metadata
102
+
103
+ ### Step verbs
104
+
105
+ - Collecting verbs: `ask`, `confirm`
106
+ - Display verbs: `say`, `header`, `btw`, `warning`
107
+
108
+ ### Supported input types
109
+
110
+ - `:string`
111
+ - `:text`
112
+ - `:integer`
113
+ - `:decimal`
114
+ - `:currency`
115
+ - `:boolean`
116
+ - `:enum`
117
+ - `:multi_enum`
118
+ - `:date`
119
+ - `:email`
120
+ - `:phone`
121
+
122
+ ### Step options
123
+
124
+ - `question "..."` for collecting steps
125
+ - `text "..."` for display steps
126
+ - `options [...]` or `options key: "Label"` for enum-style inputs
127
+ - `default value` or `default { |answers| ... }`
128
+ - `skip_if rule`
129
+ - `transition to: :next_step, if_rule: rule, requires_server: false`
130
+ - `compute { |answers| ... }` (accepted by the DSL as a server-side hook; currently omitted from runtime JSON)
131
+
132
+ ## Rule System (AST, JSON-serializable)
133
+
134
+ Rule helpers available in DSL blocks:
135
+
136
+ - `contains(:income_types, "Business")`
137
+ - `equals(:status, "single")`
138
+ - `greater_than(:dependents, 0)`
139
+ - `less_than(:age, 18)`
140
+ - `not_empty(:email)`
141
+ - `all(rule1, rule2, ...)`
142
+ - `any(rule1, rule2, ...)`
143
+
144
+ Example:
145
+
146
+ ```ruby
147
+ transition to: :complex_path,
148
+ if_rule: all(
149
+ contains(:income_types, "Business"),
150
+ greater_than(:business_count, 2)
151
+ )
152
+ ```
153
+
154
+ ## Runtime Engine
155
+
156
+ `Inquirex::Engine` holds runtime state:
157
+
158
+ - `current_step_id`
159
+ - `current_step`
160
+ - `answers` (raw hash)
161
+ - `history` (visited step IDs)
162
+
163
+ Behavior:
164
+
165
+ - Use `answer(value)` on collecting steps
166
+ - Use `advance` on display steps
167
+ - Use `finished?` to detect completion
168
+ - Use `to_state` / `.from_state` for persistence/resume
169
+
170
+ ### Validation Adapter
171
+
172
+ Validation is pluggable:
173
+
174
+ - `Inquirex::Validation::Adapter` (abstract)
175
+ - `Inquirex::Validation::NullAdapter` (default, accepts everything)
176
+
177
+ Pass a custom adapter to the engine:
178
+
179
+ ```ruby
180
+ engine = Inquirex::Engine.new(definition, validator: my_validator)
181
+ ```
182
+
183
+ ## Serialization
184
+
185
+ Definitions support round-trip serialization:
186
+
187
+ ```ruby
188
+ json = definition.to_json
189
+ restored = Inquirex::Definition.from_json(json)
190
+ ```
191
+
192
+ Serialized structure includes:
193
+
194
+ - Flow metadata (`id`, `version`, `meta`, `start`)
195
+ - Steps and transitions
196
+ - Rule AST payloads
197
+
198
+ Important serialization details:
199
+
200
+ - Rule objects serialize and deserialize cleanly
201
+ - Proc/lambda defaults are stripped from JSON
202
+ - `requires_server: true` transition flag is preserved
203
+
204
+ ## Answers Wrapper
205
+
206
+ `Inquirex::Answers` provides structured answer access:
207
+
208
+ ```ruby
209
+ answers = Inquirex::Answers.new(
210
+ filing_status: "single",
211
+ business: { count: 3, type: "llc" }
212
+ )
213
+
214
+ answers.filing_status # => "single"
215
+ answers[:filing_status] # => "single"
216
+ answers.business.count # => 3
217
+ answers.dig("business.count") # => 3
218
+ answers.to_flat_h # => {"filing_status"=>"single", "business.count"=>3, ...}
219
+ ```
220
+
221
+ ## Mermaid Export
222
+
223
+ Visualize flow graphs with Mermaid:
224
+
225
+ ```ruby
226
+ exporter = Inquirex::Graph::MermaidExporter.new(definition)
227
+ puts exporter.export
228
+ ```
229
+
230
+ Output is `flowchart TD` syntax with:
231
+
232
+ - One node per step
233
+ - Conditional edge labels from rule `to_s`
234
+ - Truncated node content for readability
235
+
236
+ ## Error Types
237
+
238
+ Common exceptions under `Inquirex::Errors`:
239
+
240
+ - `DefinitionError`
241
+ - `UnknownStepError`
242
+ - `SerializationError`
243
+ - `AlreadyFinishedError`
244
+ - `ValidationError`
245
+ - `NonCollectingStepError`
246
+
247
+ ## Development
248
+
249
+ ### Setup
250
+
251
+ ```bash
252
+ bin/setup
253
+ ```
254
+
255
+ ### Run tests
256
+
257
+ ```bash
258
+ bundle exec rspec
259
+ ```
260
+
261
+ ### Lint
262
+
263
+ ```bash
264
+ bundle exec rubocop
265
+ ```
266
+
267
+ ### Useful `just` tasks
268
+
269
+ ```bash
270
+ just install
271
+ just test
272
+ just lint
273
+ just lint-fix
274
+ just ci
275
+ ```
276
+
277
+ ## Roadmap Context
278
+
279
+ This repository is the core runtime (`inquirex`). The full ecosystem roadmap includes:
280
+
281
+ - `inquirex-ui` for rendering metadata
282
+ - `inquirex-tty` for terminal interaction
283
+ - `inquirex-js` for embeddable web widget runtime
284
+ - `inquirex-llm` for server-side LLM verbs
285
+ - `inquirex-rails` for persistence/API integration
286
+
287
+ ## License
288
+
289
+ MIT. See `LICENSE.txt`.
data/Rakefile ADDED
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+ require "timeout"
6
+ require "yard"
7
+
8
+ def shell(*args)
9
+ puts "running: #{args.join(" ")}"
10
+ system(args.join(" "))
11
+ end
12
+
13
+ task :clean do
14
+ shell("rm -rf pkg/ tmp/ coverage/ doc/ ")
15
+ end
16
+
17
+ task gem: [:build] do
18
+ shell("gem install pkg/*")
19
+ end
20
+
21
+ task permissions: [:clean] do
22
+ shell("chmod -v o+r,g+r * */* */*/* */*/*/* */*/*/*/* */*/*/*/*/*")
23
+ shell("find . -type d -exec chmod o+x,g+x {} \\;")
24
+ end
25
+
26
+ task build: :permissions
27
+
28
+ YARD::Rake::YardocTask.new(:doc) do |t|
29
+ t.files = %w[lib/**/*.rb exe/*.rb - README.md LICENSE.txt CHANGELOG.md]
30
+ t.options.unshift("--title", '"FlowEngine — DSL + AST for buildiong complex flows in Ruby."')
31
+ t.after = -> { exec("open doc/index.html") } if RUBY_PLATFORM =~ /darwin/
32
+ end
33
+
34
+ RSpec::Core::RakeTask.new(:spec)
35
+
36
+ task default: :spec
data/exe/inquirex ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "inquirex"
data/justfile ADDED
@@ -0,0 +1,38 @@
1
+ set shell := ["bash", "-lc"]
2
+
3
+ rbenv := 'eval "$(rbenv init -)"'
4
+
5
+ [no-exit-message]
6
+ recipes:
7
+ @just --choose
8
+
9
+ # Sync all dependencies
10
+ install:
11
+ {{rbenv}} && bin/setup
12
+
13
+ # Lint and reformat files
14
+ lint-fix *args:
15
+ {{rbenv}} && bundle exec rubocop -a
16
+
17
+ alias format := lint-fix
18
+
19
+ # Lint and reformat files
20
+ lint:
21
+ {{rbenv}} && bundle exec rubocop
22
+
23
+ # Run all the tests
24
+ test *args:
25
+ {{rbenv}} && ENVIRONMENT=test bundle exec rspec {{args}}
26
+
27
+ # Run tests with coverage
28
+ test-coverage *args:
29
+ ENVIRONMENT=test COVERAGE=true bundle exec rspec
30
+
31
+ clean:
32
+ #!/usr/bin/env bash
33
+ find . -name .DS_Store -delete -print || true
34
+ rm -rf tmp/*
35
+
36
+ # Run all lefthook pre-commit hooks
37
+ ci:
38
+ {{rbenv}} && lefthook run pre-commit --all-files