quickdraw 0.0.5 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +5 -5
  2. data/.rubocop.yml +18 -0
  3. data/.ruby-version +1 -1
  4. data/CHANGELOG.md +5 -0
  5. data/CODE_OF_CONDUCT.md +84 -0
  6. data/Gemfile +6 -1
  7. data/Gemfile.lock +49 -0
  8. data/LICENSE.txt +17 -18
  9. data/README.md +100 -29
  10. data/config/quickdraw.rb +16 -0
  11. data/exe/qt +12 -0
  12. data/lib/quickdraw/cluster.rb +27 -0
  13. data/lib/quickdraw/configuration.rb +13 -0
  14. data/lib/quickdraw/context.rb +138 -0
  15. data/lib/quickdraw/expectation.rb +62 -0
  16. data/lib/quickdraw/map.rb +30 -0
  17. data/lib/quickdraw/matchers/boolean.rb +11 -0
  18. data/lib/quickdraw/matchers/case_equality.rb +9 -0
  19. data/lib/quickdraw/matchers/change.rb +33 -0
  20. data/lib/quickdraw/matchers/equality.rb +39 -0
  21. data/lib/quickdraw/matchers/include.rb +15 -0
  22. data/lib/quickdraw/matchers/predicate.rb +14 -0
  23. data/lib/quickdraw/matchers/respond_to.rb +15 -0
  24. data/lib/quickdraw/matchers/to_be_a.rb +18 -0
  25. data/lib/quickdraw/matchers/to_have_attributes.rb +13 -0
  26. data/lib/quickdraw/matchers/to_raise.rb +26 -0
  27. data/lib/quickdraw/matchers/to_receive.rb +30 -0
  28. data/lib/quickdraw/matchers.rb +13 -0
  29. data/lib/quickdraw/pipe.rb +27 -0
  30. data/lib/quickdraw/queue.rb +32 -0
  31. data/lib/quickdraw/registry.rb +57 -0
  32. data/lib/quickdraw/run.rb +53 -0
  33. data/lib/quickdraw/runner.rb +48 -0
  34. data/lib/quickdraw/timer.rb +30 -0
  35. data/lib/quickdraw/version.rb +3 -1
  36. data/lib/quickdraw/worker.rb +28 -0
  37. data/lib/quickdraw.rb +29 -22
  38. metadata +51 -123
  39. data/.gitignore +0 -17
  40. data/Rakefile +0 -1
  41. data/bin/quickdraw +0 -25
  42. data/lib/quickdraw/cli.rb +0 -251
  43. data/lib/quickdraw/shopify_connector.rb +0 -105
  44. data/lib/quickdraw/shopify_connector_pool.rb +0 -54
  45. data/quickdraw.gemspec +0 -29
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 51f90e243e29a4b80d81e1d21012cae4fa8e02dc
4
- data.tar.gz: 970effdf3f87cfe12b67618bac1b9d5dd02fcc03
2
+ SHA256:
3
+ metadata.gz: 5d4d16d42b91edb328fb30254cd55b985f8b6fb946eca6d95c12b6bb06edeea3
4
+ data.tar.gz: c3f4efb7868749f81a4b33c733297344a229871ece2e4cb9b420cb66a1c6cc5e
5
5
  SHA512:
6
- metadata.gz: 9fc8e5530408ad1cc5d7bde5933b64309d26afb6b9c8097574f33a206d7a3a6a10db8da3dbe6f5acff293095e0b6bfcecd533c9be6eab4f4a4b29181ffb1d6bd
7
- data.tar.gz: 410c09bb7122a33a7ee99be3146bafac7b561143a03b0b26d2663e6172a3b2d7107231e33f2415439caaa31fe0709dd27c534ba1098e3996e9d7fbde031581bf
6
+ metadata.gz: a7df8e690a53fe6a6ce32b718e89f897ecacb6b9506ff723589042e3535a51f6d8075f0341f57b641bb88bf8a1b9b5f45622cc61870b5f142643d504bdf3259f
7
+ data.tar.gz: 05a13bace7aed487bbf8ff0b3d32a1303b86c142867e1c675ea80dad8d6f05846feafeeb44f6318092e0e9a860f2a597ee523a54f5a4f47d29c6a5b8d2b6257b
data/.rubocop.yml ADDED
@@ -0,0 +1,18 @@
1
+ inherit_from:
2
+ - "https://www.goodcop.style/rubocop.yml"
3
+ - "https://www.goodcop.style/tabs.yml"
4
+
5
+ AllCops:
6
+ TargetRubyVersion: 2.7
7
+
8
+ Lint/RescueException:
9
+ Enabled: false
10
+
11
+ Lint/Void:
12
+ Enabled: false
13
+
14
+ Style/MultilineBlockChain:
15
+ Enabled: false
16
+
17
+ Style/YodaCondition:
18
+ Enabled: false
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- ruby-2.1
1
+ 3.3.0
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2023-02-19
4
+
5
+ - Initial release
@@ -0,0 +1,84 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.
6
+
7
+ We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.
8
+
9
+ ## Our Standards
10
+
11
+ Examples of behavior that contributes to a positive environment for our community include:
12
+
13
+ * Demonstrating empathy and kindness toward other people
14
+ * Being respectful of differing opinions, viewpoints, and experiences
15
+ * Giving and gracefully accepting constructive feedback
16
+ * Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience
17
+ * Focusing on what is best not just for us as individuals, but for the overall community
18
+
19
+ Examples of unacceptable behavior include:
20
+
21
+ * The use of sexualized language or imagery, and sexual attention or
22
+ advances of any kind
23
+ * Trolling, insulting or derogatory comments, and personal or political attacks
24
+ * Public or private harassment
25
+ * Publishing others' private information, such as a physical or email
26
+ address, without their explicit permission
27
+ * Other conduct which could reasonably be considered inappropriate in a
28
+ professional setting
29
+
30
+ ## Enforcement Responsibilities
31
+
32
+ Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful.
33
+
34
+ Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate.
35
+
36
+ ## Scope
37
+
38
+ This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event.
39
+
40
+ ## Enforcement
41
+
42
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at joel@drapper.me. All complaints will be reviewed and investigated promptly and fairly.
43
+
44
+ All community leaders are obligated to respect the privacy and security of the reporter of any incident.
45
+
46
+ ## Enforcement Guidelines
47
+
48
+ Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct:
49
+
50
+ ### 1. Correction
51
+
52
+ **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community.
53
+
54
+ **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested.
55
+
56
+ ### 2. Warning
57
+
58
+ **Community Impact**: A violation through a single incident or series of actions.
59
+
60
+ **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban.
61
+
62
+ ### 3. Temporary Ban
63
+
64
+ **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior.
65
+
66
+ **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban.
67
+
68
+ ### 4. Permanent Ban
69
+
70
+ **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals.
71
+
72
+ **Consequence**: A permanent ban from any sort of public interaction within the community.
73
+
74
+ ## Attribution
75
+
76
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0,
77
+ available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
78
+
79
+ Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity).
80
+
81
+ [homepage]: https://www.contributor-covenant.org
82
+
83
+ For answers to common questions about this code of conduct, see the FAQ at
84
+ https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations.
data/Gemfile CHANGED
@@ -1,4 +1,9 @@
1
- source 'https://rubygems.org'
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
2
4
 
3
5
  # Specify your gem's dependencies in quickdraw.gemspec
4
6
  gemspec
7
+
8
+ gem "rubocop"
9
+ gem "simplecov"
data/Gemfile.lock ADDED
@@ -0,0 +1,49 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ quickdraw (0.1.0)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ ast (2.4.2)
10
+ docile (1.4.0)
11
+ json (2.6.3)
12
+ parallel (1.22.1)
13
+ parser (3.2.1.0)
14
+ ast (~> 2.4.1)
15
+ rainbow (3.1.1)
16
+ regexp_parser (2.7.0)
17
+ rexml (3.2.5)
18
+ rubocop (1.45.1)
19
+ json (~> 2.3)
20
+ parallel (~> 1.10)
21
+ parser (>= 3.2.0.0)
22
+ rainbow (>= 2.2.2, < 4.0)
23
+ regexp_parser (>= 1.8, < 3.0)
24
+ rexml (>= 3.2.5, < 4.0)
25
+ rubocop-ast (>= 1.24.1, < 2.0)
26
+ ruby-progressbar (~> 1.7)
27
+ unicode-display_width (>= 2.4.0, < 3.0)
28
+ rubocop-ast (1.26.0)
29
+ parser (>= 3.2.1.0)
30
+ ruby-progressbar (1.11.0)
31
+ simplecov (0.22.0)
32
+ docile (~> 1.1)
33
+ simplecov-html (~> 0.11)
34
+ simplecov_json_formatter (~> 0.1)
35
+ simplecov-html (0.12.3)
36
+ simplecov_json_formatter (0.1.4)
37
+ unicode-display_width (2.4.2)
38
+
39
+ PLATFORMS
40
+ arm64-darwin-22
41
+ arm64-darwin-23
42
+
43
+ DEPENDENCIES
44
+ quickdraw!
45
+ rubocop
46
+ simplecov
47
+
48
+ BUNDLED WITH
49
+ 2.4.6
data/LICENSE.txt CHANGED
@@ -1,22 +1,21 @@
1
- Copyright (c) 2013 Bryan Morris
1
+ The MIT License (MIT)
2
2
 
3
- MIT License
3
+ Copyright (c) 2023 Joel Drapper
4
4
 
5
- Permission is hereby granted, free of charge, to any person obtaining
6
- a copy of this software and associated documentation files (the
7
- "Software"), to deal in the Software without restriction, including
8
- without limitation the rights to use, copy, modify, merge, publish,
9
- distribute, sublicense, and/or sell copies of the Software, and to
10
- permit persons to whom the Software is furnished to do so, subject to
11
- the following conditions:
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:
12
11
 
13
- The above copyright notice and this permission notice shall be
14
- included in all copies or substantial portions of the Software.
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
15
14
 
16
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
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,50 +1,121 @@
1
1
  # Quickdraw
2
2
 
3
- The idea for Quickdraw comes from the 'shopify_theme' gem. I use a lot of code from them.
3
+ Quickdraw is currently in development. You should almost definitely not use it in a project until `1.0` is released.
4
4
 
5
- ### Features
5
+ > [!TIP]
6
+ > Your test files are executed in an anonymous class, so you can define methods and constants at the top level without worrying about collisions. If you’re testing something that references `Class.name`, you may have to define those classes as fixtures somewhere else.
6
7
 
7
- - Compatible with Ruby 2.0 and 2.1
8
- - MUCH faster downloads and uploads. Unfortunately, Shopify API call limits will slow you down. But in short bursts (10-20 files), Quickdraw is as much as 10x faster or more!
9
- - Quickdraw supports using ERB templates which are compiled then uploaded to shopify. This can save you time when making templates with redundant code.
10
-
11
- ## Installation
8
+ ## Getting Started
12
9
 
13
10
  Add this line to your application's Gemfile:
14
11
 
15
- gem 'quickdraw'
12
+ ```ruby
13
+ gem 'quickdraw'
14
+ ```
16
15
 
17
16
  And then execute:
18
17
 
19
- $ bundle
18
+ ```bash
19
+ bundle install
20
+ ```
21
+
22
+ Now create a file called `config/quickdraw.rb`.
20
23
 
21
- Or install it yourself as:
24
+ To run tests, execute:
25
+ ```bash
26
+ bundle exec qt
27
+ ```
22
28
 
23
- $ gem install quickdraw
29
+ It stands for "quickdraw tests".
24
30
 
25
31
  ## Usage
26
32
 
27
- 1. Create a project folder and open a terminal
28
- 2. run ````quickdraw configure````
29
- 3. run ````quickdraw download````
30
- 4. run ````quickdraw watch````
33
+ Quickdraw searches for files that end with `.test.rb`. You can put these anywhere. Some people like to put them under `/tests` or `/spec`. Others like to put them next to the code they're testing.
34
+
35
+ ### `.test`
36
+ Use the `test` method to define a test. The description is optional — sometimes you don’t need it.
37
+
38
+ ```ruby
39
+ test { assert true }
40
+ ```
41
+
42
+ You can pass `skip: true` to skip the test. Skipped tests are still run; they pass if they fail and fail they pass.
43
+
44
+ ```ruby
45
+ test(skip: true) { assert false }
46
+ ```
47
+
48
+ ### `.describe`
49
+ You can optionally wrap tests in any number of `describe` blocks, which can take a description as a string or module/class.
50
+
51
+ ```ruby
52
+ describe Thing do
53
+ # your Thing tests here
54
+ end
55
+ ```
56
+
57
+ ### `#assert`
58
+ `assert` takes a value and passes if it’s truthy.
59
+
60
+ ```ruby
61
+ test "something" do
62
+ assert true
63
+ end
64
+ ```
65
+
66
+ You can pass a custom failure message as a block. Using blocks for the failure messages means we don’t waste time constructing them unless the test fails. You don’t need to worry about expensive failure messages slowing down your tests.
67
+
68
+ ```ruby
69
+ test "something" do
70
+ assert(false) { "This is a custom failure message" }
71
+ end
72
+ ```
73
+
74
+ ### `#refute`
75
+ `refute` is just like `assert`, but it passes if the value is falsy.
76
+
77
+ ```ruby
78
+ test "something" do
79
+ refute false
80
+ end
81
+ ```
82
+
83
+ ### `expect` matchers
84
+ `expect` takes either a value or a block and returns an expectation object, which you can call matchers on.
85
+
86
+ #### `==` and `!=`
87
+
88
+ ```ruby
89
+ test "equality" do
90
+ expect(Thing.foo) == "foo"
91
+ expect(Thing.bar) != "foo"
92
+ end
93
+ ```
31
94
 
32
- ### Workflow
95
+ #### `to_raise`
33
96
 
34
- Quickdraw creates two folders in your project, ````theme```` and ````src````.
35
- When ````quickdraw watch```` is running, files added/changed/deleted in the ````theme```` folder will be mirrored to Shopify automatically.
36
- To use ERB create a copy of the ````.liquid```` file you want to use, add an ````.erb```` extension to the end, and move it to the ````src```` folder.
97
+ ```ruby
98
+ test "raises" do
99
+ expect { Thing.bar! }.to_raise(ArgumentError) do |error|
100
+ expect(error.message) == "Foo bar"
101
+ end
102
+ end
103
+ ```
37
104
 
38
- #####Example
39
- project/theme/templates/index.liquid
40
- project/src/templates/index.liquid.erb
105
+ #### `to_receive`
41
106
 
42
- When the ````project/src/templates/index.liquid.erb```` is modified, Quickdraw will render it, overwriting ````project/theme/templates/index.liquid````, and uploading the result to Shopify.
107
+ ```ruby
108
+ test "mocks and spies" do
109
+ expect(Thing).to_receive(:foo) do |a, b, c|
110
+ # The block receives arguments and can make assertions about them.
111
+ expect(a) == 1
112
+ expect(b) != 1
113
+ assert(c)
43
114
 
44
- ## Contributing
115
+ # Either return a mock response or call the original via `@super`
116
+ @super.call
117
+ end
45
118
 
46
- 1. Fork it
47
- 2. Create your feature branch (`git checkout -b my-new-feature`)
48
- 3. Commit your changes (`git commit -am 'Add some feature'`)
49
- 4. Push to the branch (`git push origin my-new-feature`)
50
- 5. Create new Pull Request
119
+ Thing.foo(1, 2, 3)
120
+ end
121
+ ```
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../test/support/matchers/pass_fail"
4
+
5
+ module BazMatcher
6
+ def baz?
7
+ success!
8
+ end
9
+ end
10
+
11
+ Quickdraw.configure do |config|
12
+ config.matcher Matchers::PassFail, Proc
13
+ config.matcher BazMatcher, String
14
+ end
15
+
16
+ # SimpleCov.start
data/exe/qt ADDED
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "etc"
5
+ require_relative "../lib/quickdraw"
6
+ require File.expand_path("config/quickdraw")
7
+
8
+ Quickdraw::Run.new(
9
+ number_of_processes: Etc.nprocessors,
10
+ number_of_threads: 2,
11
+ test_files: Dir.glob("./**/*.test.rb")
12
+ ).call
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "etc"
4
+
5
+ class Quickdraw::Cluster
6
+ def self.call(n = Etc.nprocessors, &)
7
+ spawn(n, &).wait
8
+ end
9
+
10
+ def self.spawn(n = Etc.nprocessors, &block)
11
+ new.tap do |cluster|
12
+ n.times { cluster.fork(&block) }
13
+ end
14
+ end
15
+
16
+ def initialize
17
+ @workers = []
18
+ end
19
+
20
+ def fork(&)
21
+ @workers << Quickdraw::Worker.fork(&)
22
+ end
23
+
24
+ def wait
25
+ @workers.map(&:wait)
26
+ end
27
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Quickdraw::Configuration
4
+ def initialize
5
+ @registry = Quickdraw::Registry.new
6
+ end
7
+
8
+ attr_reader :registry
9
+
10
+ def matcher(matcher, *types)
11
+ @registry.register(matcher, *types)
12
+ end
13
+ end
@@ -0,0 +1,138 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Quickdraw::Context
4
+ DEFAULT_MATCHERS = [
5
+ Quickdraw::Matchers::Boolean,
6
+ Quickdraw::Matchers::CaseEquality,
7
+ Quickdraw::Matchers::Change,
8
+ Quickdraw::Matchers::Equality,
9
+ Quickdraw::Matchers::Include,
10
+ Quickdraw::Matchers::Predicate,
11
+ Quickdraw::Matchers::RespondTo,
12
+ Quickdraw::Matchers::ToBeA,
13
+ Quickdraw::Matchers::ToHaveAttributes,
14
+ Quickdraw::Matchers::ToRaise,
15
+ Quickdraw::Matchers::ToReceive,
16
+ ].freeze
17
+
18
+ class << self
19
+ def run(result = Quickdraw::Runner.new, path = [])
20
+ new(result, path).run(@tests) if @tests
21
+
22
+ if defined?(@sub_contexts)
23
+ @sub_contexts.each do |(context, desc)|
24
+ context.run(result, [*path, desc])
25
+ end
26
+ end
27
+ end
28
+
29
+ def use(*new_matchers)
30
+ new_matchers.each { |m| matchers << m }
31
+ end
32
+
33
+ def matchers
34
+ @matchers ||= if superclass < Quickdraw::Context
35
+ superclass.matchers.dup
36
+ else
37
+ Set.new(DEFAULT_MATCHERS)
38
+ end
39
+ end
40
+
41
+ def describe(description, &block)
42
+ unless defined?(@sub_contexts)
43
+ @sub_contexts = []
44
+ end
45
+
46
+ @sub_contexts << [Class.new(self, &block), description]
47
+ end
48
+
49
+ alias_method :context, :describe
50
+
51
+ def test(name = nil, skip: false, &block)
52
+ unless defined?(@tests)
53
+ @tests = []
54
+ end
55
+
56
+ @tests << [name, skip, block]
57
+ end
58
+ end
59
+
60
+ def initialize(run, path)
61
+ @run = run
62
+ @path = path
63
+ @expectations = []
64
+ @matchers = self.class.matchers
65
+
66
+ @name = nil
67
+ @skip = false
68
+ end
69
+
70
+ def run(tests)
71
+ tests.each do |(name, skip, block)|
72
+ @name = name
73
+ @skip = skip
74
+
75
+ instance_exec(&block)
76
+
77
+ resolve
78
+ end
79
+ end
80
+
81
+ def expect(value = Quickdraw::Null, &block)
82
+ type = Quickdraw::Null == value ? block : value
83
+
84
+ expectation_class = Quickdraw::Config.registry.expectation_for(
85
+ type, matchers: @matchers
86
+ )
87
+
88
+ expectation = expectation_class.new(self, value, &block)
89
+ @expectations << expectation
90
+ expectation
91
+ end
92
+
93
+ def resolve
94
+ @expectations.each(&:resolve)
95
+ ensure
96
+ @expectations.clear
97
+ end
98
+
99
+ def assert(value)
100
+ if value
101
+ success!
102
+ elsif block_given?
103
+ failure! { yield(value) }
104
+ else
105
+ failure! { "expected #{value.inspect} to be truthy" }
106
+ end
107
+ end
108
+
109
+ def refute(value)
110
+ if !value
111
+ success!
112
+ elsif block_given?
113
+ failure! { yield(value) }
114
+ else
115
+ failure! { "expected #{value.inspect} to be falsy" }
116
+ end
117
+ end
118
+
119
+ def success!
120
+ if @skip
121
+ @run.failure!(full_path) { "The skipped test `#{@name}` started passing." }
122
+ else
123
+ @run.success!(@name)
124
+ end
125
+ end
126
+
127
+ def failure!(&)
128
+ if @skip
129
+ @run.success!(@name)
130
+ else
131
+ @run.failure!(full_path, &)
132
+ end
133
+ end
134
+
135
+ def full_path
136
+ [*@path, @name]
137
+ end
138
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Quickdraw::Expectation
4
+ def initialize(context, value = Quickdraw::Null, &block)
5
+ if block && Quickdraw::Null != value
6
+ raise Quickdraw::ArgumentError.new(
7
+ "You must only provide a value or a block to `expect`."
8
+ )
9
+ end
10
+
11
+ @context = context
12
+ @value = value
13
+ @block = block
14
+ @made_expectations = false
15
+ end
16
+
17
+ def success!
18
+ @context.success!
19
+ @made_expectations = true
20
+ end
21
+
22
+ def failure!(&)
23
+ @context.failure!(&)
24
+ @made_expectations = true
25
+ end
26
+
27
+ def resolve
28
+ if !@made_expectations
29
+ failure! { "You didn't make any expectations." }
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def assert(value, &)
36
+ value ? success! : failure!(&)
37
+ end
38
+
39
+ def refute(value, &)
40
+ value ? failure!(&) : success!
41
+ end
42
+
43
+ def value
44
+ if @block
45
+ raise Quickdraw::ArgumentError.new(
46
+ "You must pass a value rather than a block when using the #{caller_locations.first.label} matcher."
47
+ )
48
+ else
49
+ @value
50
+ end
51
+ end
52
+
53
+ def block
54
+ if @block
55
+ @block
56
+ else
57
+ raise Quickdraw::ArgumentError.new(
58
+ "You must pass a block rather than a value when using the #{caller_locations.first.label} matcher."
59
+ )
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Quickdraw::Map
4
+ include Enumerable
5
+
6
+ def initialize
7
+ @hash = {}
8
+ @mutex = Mutex.new
9
+ end
10
+
11
+ def [](value)
12
+ @hash[value]
13
+ end
14
+
15
+ def []=(key, value)
16
+ @mutex.synchronize do
17
+ @hash[key] = value
18
+ end
19
+ end
20
+
21
+ def each(&)
22
+ @hash.each(&)
23
+ end
24
+
25
+ def clear
26
+ @mutex.synchronize do
27
+ @hash.clear
28
+ end
29
+ end
30
+ end