rspec-sleeping_king_studios 2.2.4 → 2.3.0.rc.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (23) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +23 -1
  3. data/DEVELOPMENT.md +20 -30
  4. data/README.md +201 -10
  5. data/lib/rspec/sleeping_king_studios/concerns/example_constants.rb +94 -0
  6. data/lib/rspec/sleeping_king_studios/concerns/shared_example_group.rb +3 -1
  7. data/lib/rspec/sleeping_king_studios/concerns/toolbelt.rb +25 -0
  8. data/lib/rspec/sleeping_king_studios/concerns/wrap_env.rb +46 -0
  9. data/lib/rspec/sleeping_king_studios/examples/property_examples/class_properties.rb +66 -0
  10. data/lib/rspec/sleeping_king_studios/examples/property_examples/constants.rb +51 -0
  11. data/lib/rspec/sleeping_king_studios/examples/property_examples/predicates.rb +28 -0
  12. data/lib/rspec/sleeping_king_studios/examples/property_examples/private_properties.rb +64 -0
  13. data/lib/rspec/sleeping_king_studios/examples/property_examples/properties.rb +78 -0
  14. data/lib/rspec/sleeping_king_studios/examples/property_examples.rb +12 -117
  15. data/lib/rspec/sleeping_king_studios/matchers/core/have_property.rb +5 -2
  16. data/lib/rspec/sleeping_king_studios/matchers/core/have_property_matcher.rb +19 -10
  17. data/lib/rspec/sleeping_king_studios/matchers/core/have_reader.rb +5 -2
  18. data/lib/rspec/sleeping_king_studios/matchers/core/have_reader_matcher.rb +17 -8
  19. data/lib/rspec/sleeping_king_studios/matchers/core/have_writer.rb +5 -2
  20. data/lib/rspec/sleeping_king_studios/matchers/core/have_writer_matcher.rb +14 -7
  21. data/lib/rspec/sleeping_king_studios/matchers/shared/match_property.rb +6 -6
  22. data/lib/rspec/sleeping_king_studios/version.rb +4 -4
  23. metadata +44 -8
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2cc8203c15e94a3b3eb1b02593024abd5b9d831d
4
- data.tar.gz: 3b5f2c2d68e0999136707eed7e1a3eece9e1e429
3
+ metadata.gz: 5a9d2f85f2b455f1996f711b0d606f5b1ca1c299
4
+ data.tar.gz: 21a4f0209ed50bc16090b2de208540a10e28dfd4
5
5
  SHA512:
6
- metadata.gz: e05d8ef7c6e0d9c665e89ef25daec30d7326a7ab5a3f13e4986e3ee9484de2083dfe62e45b0f5b862baa93bf274f3be4250d01a4eb0c8591ab79ff626cf80f4a
7
- data.tar.gz: 4aab6dca88d46c22a1f8fbb2efef286eed61ebc654ab5e233decfadc6a277fc61a2283592a5fb42df35cc7040b016f0d1e9f198b5b5cbf3c126ea22be535140d
6
+ metadata.gz: fefccdff728588bd83fca3941faffc70330531edcfd8a9c9507c5b2cec59340d0492feeeb17d857729974d132e3d2687283f5957da799577226b5979c091e19b
7
+ data.tar.gz: 47d1c296a0acc07d2b8a24fd5e3f1cceea18fa8ce5f7e7da51e9c510b2f3b06f36199ed3b89628ce8a1f53cb0f8e62137c0ded9f1b79d51ced492779d12662eb
data/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
1
1
  # Changelog
2
2
 
3
+ ## 2.3.0
4
+
5
+ ### Concerns
6
+
7
+ Added ExampleConstants concern to define named, example-scoped constants and classes.
8
+
9
+ Added Toolbelt concern to automatically expose SleepingKingStudios::Tools methods in examples and example groups.
10
+
11
+ Added WrapEnv concern to cleanly control environment variables per example or within example blocks.
12
+
13
+ ### Examples
14
+
15
+ Added shared examples for expecting class readers, writers, and properties.
16
+
17
+ Added shared examples for expecting private readers, writers, and properties.
18
+
19
+ Added aliased examples for "has constant", "has immutable constant".
20
+
21
+ ### Matchers
22
+
23
+ Added :allow_private option to HaveReaderMatcher, HaveWriterMatcher, and HavePropertyMatcher.
24
+
3
25
  ## 2.2.4
4
26
 
5
27
  Reverted support for RSpec 3.0 to 3.2.
@@ -173,7 +195,7 @@ Removed the functionality checking the value of `:property` after `:property=` h
173
195
 
174
196
  Now supports composable matchers, as follows:
175
197
 
176
- expect(object).to have_property(:my_method).with(an_instance_of(Fixnum))
198
+ expect(object).to have_property(:my_method).with(an_instance_of(Integer))
177
199
 
178
200
  Also added `#with_value` as an alias for `with`, and the error messages have been edited for clarity.
179
201
 
data/DEVELOPMENT.md CHANGED
@@ -2,65 +2,55 @@
2
2
 
3
3
  ## Version 2.3
4
4
 
5
- ### Features
5
+ ## Version 2.3.1
6
+
7
+ ### Features - Syntactic Sugar
6
8
 
7
9
  - Alias `have_reader`, etc as `define_reader`.
8
10
  - Also alias shared examples.
9
11
  - Alias `have_constant` as `define_constant`.
10
12
  - Alias #immutable as #frozen.
11
13
  - Also alias shared examples.
12
- - Add 'should have class reader/writer/property' shared examples.
13
- - Add 'should have private reader/writer/property' shared examples.
14
- - Implement RespondToMatcher#with_at_least(N).arguments, equivalent to with(N).arguments.and_unlimited_arguments.
15
14
 
16
15
  ## Future Tasks
17
16
 
18
17
  - Resolve Aruba deprecation warnings.
19
- - Run each file individually as CI step.
20
18
 
21
19
  ### Bug Fixes
22
20
 
23
21
  - false negative on #alias_method?
22
+ - need reproduction steps!
24
23
  - compare via Method#source_location equality and Method#original_name is expected?
25
24
 
26
- ### Features
27
-
28
- - Implement ::stub_env, #stub_env: |
29
-
30
- describe 'something' do
31
- # Changes the value using an around(:example) block.
32
- stub_env('FIRST_KEY', 'value')
33
-
34
- # Block syntax.
35
- stub_env('SECOND_KEY') { calculated_value }
36
-
37
- it 'should something' do
38
- # Temporarily changes the value, calls the block, and resets the value.
39
- stub_env('THIRD_KEY', 'value') do
40
-
41
- end # stub_env
42
- end # it
43
- end # describe
25
+ ### Features - Functionality
44
26
 
45
- - Implement example-scoped test constants: |
46
-
47
- example_class 'Example::Class::Name' do |klass| ... end
48
-
49
- example_module 'Example::Module::Name' do |mod| ... end
27
+ - Add spy+matcher for expect(my_object, :my_method).to have_changed ?
50
28
 
51
- example_const 'Example::Constant::Name' do ... end
29
+ ### Features - Quality of Life
52
30
 
31
+ - Implement RespondToMatcher#with_optional_keywords, #with_required_keywords.
53
32
  - Implement be_immutable matcher.
54
33
  - Enhance RSpec matcher examples to display the #failure_message on a failed "should pass/fail with" example.
55
34
  - let?(:name) { } # Defines a memoized helper, but only if one is not already defined.
56
- - Add spy+matcher for expect(my_object, :my_method).to have_changed ?
35
+
36
+ ### Features - Syntactic Sugar
37
+
38
+ - Implement RespondToMatcher#with_at_least(N).arguments, equivalent to with(N).arguments.and_unlimited_arguments.
57
39
 
58
40
  ### Maintenance
59
41
 
42
+ - Update all Concerns to be #include-d, not #extend-ed.
43
+ - Update SharedExampleGroup concern to use Ruby paradigms:
44
+ - overload #include_examples
45
+ - use proper method-based lookup, inheritance, etc
46
+ - less fragility than hooking into existing RSpec internals
60
47
  - Revisit failure messages for #respond_to, #be_constructible - see #received/#have_received for example?
61
48
  - Revisit how matchers are documented, particularly in README.md
62
49
  - Use matcher class name instead of macro names?
63
50
  - Clarify documentation of parameters - YARD-like?
51
+ - Integration specs for shared example groups
52
+ - Run in external process, parse output for expected values (similar to Aruba)
53
+ - Allows testing of failing example groups
64
54
  - Pare down Cucumber features for matchers - repurpose as documentation/examples only.
65
55
  - Break down into smaller (bite-sized?) individual examples.
66
56
  - RuboCop - use RSpec rule file as starting point?
data/README.md CHANGED
@@ -4,7 +4,7 @@ A collection of matchers and extensions to ease TDD/BDD using RSpec. Extends bui
4
4
 
5
5
  ## Support
6
6
 
7
- RSpec::SleepingKingStudios is tested against RSpec 3.0 through 3.6, but support for RSpec 3.2 and below will be removed in version 2.3.0.
7
+ RSpec::SleepingKingStudios is tested against RSpec 3.3 through 3.6.
8
8
 
9
9
  Currently, the following versions of Ruby are officially supported:
10
10
 
@@ -14,7 +14,7 @@ Currently, the following versions of Ruby are officially supported:
14
14
 
15
15
  For Ruby 2.0 support, use version 2.1 or earlier: `gem "rspec-sleeping_king_studios", "~> 2.1.1"`.
16
16
 
17
- For RSpec 3.0 to 3.2 support, use version 2.2 or earlier: `gem "rspec-sleeping_king_studios", "~> 2.2.4"`.
17
+ For RSpec 3.0 to 3.2 support, use version 2.2 or earlier: `gem "rspec-sleeping_king_studios", "~> 2.2.2"`.
18
18
 
19
19
  If you require a previous version of Ruby or RSpec, the 1.0 branch supports Ruby 1.9.3 and RSpec 2: `gem "rspec-sleeping_king_studios", "~> 1.0.1"`. However, changes from 2.0 and higher will not be backported.
20
20
 
@@ -98,12 +98,55 @@ This option is used with the HavePredicateMatcher (see `#have_predicate`, below)
98
98
 
99
99
  RSpec::SleepingKingStudios defines a few concerns that can be included in or extended into modules or example groups for additional functionality.
100
100
 
101
+ ### Example Constants
102
+
103
+ require 'rspec/sleeping_king_studios/concerns/example_constants'
104
+
105
+ RSpec.describe 'constants' do
106
+ extend RSpec::SleepingKingStudios::Concerns::ExampleConstants
107
+
108
+ example_constant 'THE_ANSWER', 42
109
+
110
+ example_class 'Spec::Examples::Question' do |klass|
111
+ klass.send :define_method, :answer, THE_ANSWER
112
+ end # example_class
113
+
114
+ let(:described_class) { Spec::Examples::Question }
115
+ let(:instance) { described_class.new }
116
+
117
+ it { expect(described_class.name).to be == 'Spec::Examples::Question' }
118
+
119
+ it { expect(instance.answer).to be THE_ANSWER }
120
+ end # describe
121
+
122
+ Provides a programmatic way to define temporary constants and classes scoped to the current example.
123
+
124
+ #### `::example_constant`
125
+
126
+ `param constant_name [String, Symbol]` The name of the constant. Can be a qualified name separated by :: (e.g. `'Spec::Examples::Question'`), in which case any missing modules will be temporarily set as well.
127
+
128
+ `param constant_value` Defaults to nil. If the constant value is not set and a block is given, the block will be executed in the context of the example (so previously-set constants will be available, as well as example features such as the values of `let` blocks) and the value of the constant will be set to the result of the block call.
129
+
130
+ `option force [Boolean]` Defaults to false. If the constant is already defined, trying to set the constant value will raise an error unless the force option is set to true.
131
+
132
+ Sets the value of the named constant to the specified value within the context of the current example.
133
+
134
+ #### `::example_class`
135
+
136
+ `param constant_name [String, Symbol]` The name of the constant. Can be a qualified name separated by :: (e.g. `'Spec::Examples::Question'`), in which case any missing modules will be temporarily set as well.
137
+
138
+ `option base_class [Class]` Defaults to Object. The base class of the generated class.
139
+
140
+ `yield klass [Class]` If a block is given, it is executed in the context of the generated class and yielded the class.
141
+
142
+ Creates a new class with the specified base class and sets the value of the named constant to the created class within the context of the current example.
143
+
101
144
  ### Focus Examples
102
145
 
103
146
  require 'rspec/sleeping_king_studios/concerns/focus_examples'
104
147
 
105
148
  RSpec.describe String do
106
- extend RSpec::SleepingKingStudios::Concerns::WrapExamples
149
+ extend RSpec::SleepingKingStudios::Concerns::FocusExamples
107
150
 
108
151
  shared_examples 'should be a greeting' do
109
152
  it { expect(salutation).to be =~ /greeting/i }
@@ -160,6 +203,74 @@ Utility functions for defining shared examples. If included in a module, any sha
160
203
 
161
204
  (also `::shared_context`) Defines a shared example group within the context of the current module. Unlike a top-level example group defined using RSpec#shared_examples, these examples are not globally available, and must be mixed into an example group by including the module. The shared examples must be defined before including the module, or they will not be available in the example group.
162
205
 
206
+ ### Toolbelt
207
+
208
+ require 'rspec/sleeping_king_studios/concerns/toolbelt'
209
+
210
+ RSpec.describe "a String" do
211
+ include RSpec::SleepingKingStudios::Concerns::Toolbelt
212
+
213
+ shared_examples 'should process' do |string|
214
+ singular = tools.string.singularize(string)
215
+ plural = tools.string.pluralize(string)
216
+
217
+ it "should singularize #{string} to #{singular}" do
218
+ expect(tools.singularize string).to be_a String
219
+ end # it
220
+
221
+ it "should pluralize #{string} to #{plural}" do
222
+ expect(tools.pluralize string).to be_a String
223
+ end # it
224
+ end # shared_examples
225
+
226
+ include_examples 'should pluralize', 'light'
227
+ end # describe
228
+
229
+ A helper module for exposing SleepingKingStudios::Tools methods in examples and example groups.
230
+
231
+ ### Wrap Environment
232
+
233
+ require 'rspec/sleeping_king_studios/concerns/wrap_env'
234
+
235
+ RSpec.describe 'environment' do
236
+ include RSpec::SleepingKingStudios::Concerns::WrapEnv
237
+
238
+ it { expect(ENV['VAR_NAME']).to be nil }
239
+
240
+ context 'when the variable is set in the example group' do
241
+ wrap_env 'VAR_NAME', 'custom_value'
242
+
243
+ it { expect(ENV['VAR_NAME']).to be == 'custom_value' }
244
+ end # context
245
+
246
+ context 'when the variable is set in the example group with a block' do
247
+ let(:calculated_value) { 'calculated_value' }
248
+
249
+ wrap_env('VAR_NAME') { calculated_value }
250
+
251
+ it { expect(ENV['VAR_NAME']).to be == calculated_value }
252
+ end # context
253
+
254
+ context 'when the variable is set inside an example' do
255
+ it 'should set the variable' do
256
+ expect(ENV['VAR_NAME']).to be nil
257
+
258
+ begin
259
+ wrap_env('VAR_NAME', 'new_value') do
260
+ expect(ENV['VAR_NAME']).to be == 'new_value'
261
+
262
+ raise RuntimeError, 'must handle errors and reset the var'
263
+ end # wrap_env
264
+ rescue RuntimeError
265
+ end # begin-rescue
266
+
267
+ expect(ENV['VAR_NAME']).to be nil
268
+ end # it
269
+ end # context
270
+ end # describe
271
+
272
+ Provides helper methods for temporarily overwriting values in the environment, which are safely and automatically reset after the example or block.
273
+
163
274
  ### Wrap Examples
164
275
 
165
276
  require 'rspec/sleeping_king_studios/concerns/wrap_examples'
@@ -446,7 +557,13 @@ Checks if the actual object responds to `#property` and `#property=`, and option
446
557
 
447
558
  expect(instance).to have_property(:foo).with("foo")
448
559
 
449
- **Parameters:** Property. Expects a string or symbol that is a valid identifier.
560
+ expect(instance).to have_property(:foo, :allow_private => true).with("foo")
561
+
562
+ **Parameters:**
563
+
564
+ `param property [String, Symbol]` The name of the property.
565
+
566
+ `option allow_private [Boolean]` Defaults to false. If true, the matcher will also match a private or protected reader or writer method.
450
567
 
451
568
  **Chaining:**
452
569
 
@@ -458,19 +575,23 @@ Checks if the actual object responds to `#property` and `#property=`, and option
458
575
 
459
576
  require 'rspec/sleeping_king_studios/matchers/core/have_reader'
460
577
 
461
- Checks if the actual object responds to `#property`, and optionally if the current value of `actual.property()` is equal to a specified value.
578
+ Checks if the actual object responds to `#property` with 0 arguments, and optionally if the current value of `actual.property()` is equal to a specified value.
462
579
 
463
580
  **How To Use:**
464
581
 
465
582
  expect(instance).to have_reader(:foo).with("foo")
466
583
 
467
- **Parameters:** Property. Expects a string or symbol that is a valid identifier.
584
+ expect(instance).to have_reader(:foo, :allow_private => true).with("foo")
468
585
 
469
- **Chaining:**
586
+ **Parameters:**
470
587
 
471
- * **`#with`:** (also `#with_value`) Expects one object, which is checked against the current value of `actual.property()` if actual responds to `#property`. Can also be used with an RSpec matcher:
588
+ `param property [String, Symbol]` The name of the reader method.
589
+
590
+ `option allow_private [Boolean]` Defaults to false. If true, the matcher will also match a private or protected method.
591
+
592
+ **Chaining:**
472
593
 
473
- expect(instance).to have_reader(:bar).with(an_instance_of(String))
594
+ * **`#with`:** (also `#with_value`) Expects one object, which is checked against the current value of `actual.property()` if actual responds to `#property`. Can also be used with an RSpec matcher: `expect(instance).to have_reader(:bar).with(an_instance_of(String))`
474
595
 
475
596
  #### `#have_writer` Matcher
476
597
 
@@ -482,7 +603,13 @@ Checks if the actual object responds to `#property=`.
482
603
 
483
604
  expect(instance).to have_writer(:foo=)
484
605
 
485
- **Parameters:** Property. Expects a string or symbol that is a valid identifier. An equals sign '=' is automatically added if the identifier does not already terminate in '='.
606
+ expect(instance).to have_writer(:foo=, :allow_private => true)
607
+
608
+ **Parameters:**
609
+
610
+ `param property [String, Symbol]` The name of the writer method. An equals sign '=' is automatically added if the identifier does not already terminate in '='.
611
+
612
+ `option allow_private [Boolean]` Defaults to false. If true, the matcher will also match a private or protected method.
486
613
 
487
614
  ## Shared Examples
488
615
 
@@ -512,6 +639,32 @@ These examples are shorthand for defining a property expectation.
512
639
  # You can use the custom shared examples here.
513
640
  end # describe
514
641
 
642
+ #### Should Have Class Property
643
+
644
+ include_examples 'should have class property', :foo, 42
645
+
646
+ Delegates to the `#have_property` matcher (see Core/#have\_property, above) and passes if `described_class` responds to the specified reader and writer methods. If a value is specified, the described class must respond to the property and return the specified value. Alternatively, you can set a proc as the expected value, which can contain a comparison, an RSpec expectation, or a more complex expression:
647
+
648
+ include_examples 'should have class property', :bar, ->() { an_instance_of(String) }
649
+
650
+ include_examples 'should have class property', :baz, ->(value) { value.count = 3 }
651
+
652
+ #### Should Have Class Reader
653
+
654
+ include_examples 'should have class reader', :foo, 42
655
+
656
+ Delegates to the `#have_reader` matcher (see Core/#have_reader, above) and passes if `described_class` responds to the specified property reader. If a value is specified, the described class must respond to the property and return the specified value. Alternatively, you can set a proc as the expected value, which can contain a comparison, an RSpec expectation, or a more complex expression:
657
+
658
+ include_examples 'should have class reader', :bar, ->() { an_instance_of(String) }
659
+
660
+ include_examples 'should have class reader', :baz, ->(value) { value.count = 3 }
661
+
662
+ #### Should Have Class Writer
663
+
664
+ include_examples 'should have class writer', :foo=
665
+
666
+ Delegates to the `#have_writer` matcher (see Core/#have_writer, above) and passes if `described_class` responds to the specified property writer.
667
+
515
668
  #### Should Have Constant
516
669
 
517
670
  include_examples 'should have constant', :FOO, 42
@@ -550,6 +703,20 @@ Delegates to the `#have_property` matcher (see Core/#have\_property, above) and
550
703
 
551
704
  include_examples 'should have property', :baz, ->(value) { value.count = 3 }
552
705
 
706
+ You can also set the :allow_private option to allow the examples to match a private reader and/or writer method:
707
+
708
+ include_examples 'should have property', :foo, :allow_private => true
709
+
710
+ include_examples 'should have property', :foo, 42, :allow_private => true
711
+
712
+ #### Should Have Private Property
713
+
714
+ include_examples 'should have private property', :foo
715
+
716
+ include_examples 'should have private property', :foo, 42
717
+
718
+ Passes if the actual object has the specified private or protected property reader and writer, and fails if the actual object does not have the specified reader and writer or if the specified reader or writer is a public method. If a value is specified, the value of the private reader must match the specified value.
719
+
553
720
  #### Should Have Reader
554
721
 
555
722
  include_examples 'should have reader', :foo, 42
@@ -560,24 +727,48 @@ Delegates to the `#have_reader` matcher (see Core/#have_reader, above) and passe
560
727
 
561
728
  include_examples 'should have reader', :baz, ->(value) { value.count = 3 }
562
729
 
730
+ You can also set the :allow_private option to allow the examples to match a private reader method:
731
+
732
+ include_examples 'should have reader', :foo, :allow_private => true
733
+
734
+ include_examples 'should have reader', :foo, 42, :allow_private => true
735
+
563
736
  #### Should Not Have Reader
564
737
 
565
738
  include_examples 'should not have reader', :foo
566
739
 
567
740
  Delegates to the `#have_reader` matcher (see Core/#have_reader, above) and passes if the actual object does not respond to to the specified property reader.
568
741
 
742
+ #### Should Have Private Reader
743
+
744
+ include_examples 'should have private reader', :foo
745
+
746
+ include_examples 'should have private reader', :foo, 42
747
+
748
+ Passes if the actual object has the specified private or protected property reader, and fails if the actual object does not have the specified reader or if the specified reader is a public method. If a value is specified, the value of the private reader must match the specified value.
749
+
569
750
  #### Should Have Writer
570
751
 
571
752
  include_examples 'should have writer', :foo=
572
753
 
573
754
  Delegates to the `#have_writer` matcher (see Core/#have_writer, above) and passes if the actual object responds to the specified property writer.
574
755
 
756
+ You can also set the :allow_private option to allow the examples to match a private writer method:
757
+
758
+ include_examples 'should have writer', :foo=, :allow_private => true
759
+
575
760
  #### Should Not Have Writer
576
761
 
577
762
  include_examples 'should not have writer', :foo=
578
763
 
579
764
  Delegates to the `#have_writer` matcher (see Core/#have_writer, above) and passes if the actual object does not respond to to the specified property writer.
580
765
 
766
+ #### Should Have Private Writer
767
+
768
+ include_examples 'should have private writer', :foo=
769
+
770
+ Passes if the actual object has the specified private or protected property writer, and fails if the actual object does not have the specified writer or if the specified writer is a public method.
771
+
581
772
  ### RSpec Matcher Examples
582
773
 
583
774
  These examples are used for validating custom RSpec matchers. They are used
@@ -0,0 +1,94 @@
1
+ # lib/rspec/sleeping_king_studios/concerns/example_constants.rb
2
+
3
+ require 'rspec/sleeping_king_studios/concerns'
4
+
5
+ module RSpec::SleepingKingStudios::Concerns
6
+ module ExampleConstants
7
+ DEFAULT_VALUE = Object.new.freeze
8
+ private_constant :DEFAULT_VALUE
9
+
10
+ def self.assign_constant namespace, constant_name, constant_value
11
+ prior_value = DEFAULT_VALUE
12
+
13
+ if namespace.const_defined?(constant_name)
14
+ prior_value = namespace.const_get(constant_name)
15
+ end # if
16
+
17
+ namespace.const_set(constant_name, constant_value)
18
+
19
+ yield
20
+ ensure
21
+ if prior_value == DEFAULT_VALUE
22
+ namespace.send :remove_const, constant_name
23
+ else
24
+ namespace.const_set(constant_name, prior_value)
25
+ end # if-else
26
+ end # class method assign_constant
27
+
28
+ def self.guard_existing_constant! namespace, constant_name
29
+ return unless namespace.const_defined?(constant_name)
30
+
31
+ message =
32
+ "constant #{constant_name} is already defined with value "\
33
+ "#{namespace.const_get(constant_name).inspect}"
34
+
35
+ raise NameError, message
36
+ end # class method guard_existing_constant!
37
+
38
+ def self.resolve_namespace module_names
39
+ last_defined = nil
40
+
41
+ resolved =
42
+ module_names.reduce(Object) do |ns, module_name|
43
+ next ns.const_get(module_name) if ns.const_defined?(module_name)
44
+
45
+ last_defined ||= { :namespace => ns, :module_name => module_name }
46
+
47
+ ns.const_set(module_name, Module.new)
48
+ end # reduce
49
+
50
+ yield resolved
51
+ ensure
52
+ if last_defined
53
+ last_defined[:namespace].send(:remove_const, last_defined[:module_name])
54
+ end # if
55
+ end # class method resolve_namespace
56
+
57
+ def example_class class_name, base_class: Object, &block
58
+ example_constant(class_name) do
59
+ base_class = Object.const_get(base_class) if base_class.is_a?(String)
60
+ klass = Class.new(base_class)
61
+
62
+ klass.define_singleton_method(:name) { class_name }
63
+ klass.singleton_class.send(:alias_method, :inspect, :name)
64
+ klass.singleton_class.send(:alias_method, :to_s, :name)
65
+
66
+ klass.instance_exec(klass, &block) if block_given?
67
+
68
+ klass
69
+ end # example_constant
70
+ end # method example_class
71
+
72
+ def example_constant qualified_name, constant_value = DEFAULT_VALUE, force: false, &block
73
+ around(:example) do |example|
74
+ resolved_value =
75
+ if constant_value == DEFAULT_VALUE
76
+ block ? example.instance_exec(&block) : nil
77
+ else
78
+ constant_value
79
+ end # if
80
+
81
+ module_names = qualified_name.to_s.split('::')
82
+ constant_name = module_names.pop
83
+
84
+ ExampleConstants.resolve_namespace(module_names) do |namespace|
85
+ ExampleConstants.guard_existing_constant!(namespace, constant_name) unless force
86
+
87
+ ExampleConstants.assign_constant(namespace, constant_name, resolved_value) do
88
+ example.call
89
+ end # assign_constant
90
+ end # resolve_namespace
91
+ end # before example
92
+ end # method example_constant
93
+ end # module
94
+ end # module
@@ -8,7 +8,7 @@ module RSpec::SleepingKingStudios::Concerns
8
8
  #
9
9
  # @example
10
10
  # module MySharedExamples
11
- # extend Rspec::SleepingKingStudios::Concerns::SharedExampleGroup
11
+ # extend RSpec::SleepingKingStudios::Concerns::SharedExampleGroup
12
12
  #
13
13
  # shared_examples 'my examples' do
14
14
  # # Define shared examples here.
@@ -48,6 +48,8 @@ module RSpec::SleepingKingStudios::Concerns
48
48
  #
49
49
  # Hook to merge defined example groups when included in another module.
50
50
  def included other
51
+ super
52
+
51
53
  merge_shared_example_groups other
52
54
  end # method included
53
55
 
@@ -0,0 +1,25 @@
1
+ # lib/rspec/sleeping_king_studios/concerns/toolbelt.rb
2
+
3
+ require 'sleeping_king_studios/tools/toolbox/mixin'
4
+
5
+ require 'rspec/sleeping_king_studios/concerns'
6
+
7
+ module RSpec::SleepingKingStudios::Concerns
8
+ # Defines ::tools and #tools methods for example groups and examples, exposing
9
+ # an instance of SleepingKingStudios::Tools::Toolbelt.
10
+ module Toolbelt
11
+ extend SleepingKingStudios::Tools::Toolbox::Mixin
12
+
13
+ # Class methods to define when including
14
+ # RSpec::SleepingKingStudios::Concerns::Toolbelt in a class.
15
+ module ClassMethods
16
+ def tools
17
+ @tools ||= SleepingKingStudios::Tools::Toolbelt.instance
18
+ end # class method tools
19
+ end # module
20
+
21
+ def tools
22
+ @tools ||= self.class.tools
23
+ end # method tools
24
+ end # module
25
+ end # module
@@ -0,0 +1,46 @@
1
+ # lib/rspec/sleeping_king_studios/concerns/wrap_env.rb
2
+
3
+ require 'sleeping_king_studios/tools/toolbox/mixin'
4
+
5
+ require 'rspec/sleeping_king_studios/concerns'
6
+
7
+ module RSpec::SleepingKingStudios::Concerns
8
+ # Methods for temporarily overwriting values in the environment, which are
9
+ # safely and automatically reset after the block or example.
10
+ module WrapEnv
11
+ extend SleepingKingStudios::Tools::Toolbox::Mixin
12
+
13
+ # Class methods to define when including
14
+ # RSpec::SleepingKingStudios::Concerns::WrapEnv in a class.
15
+ module ClassMethods
16
+ def wrap_env key, value = nil, &block
17
+ around(:example) do |wrapped_example|
18
+ begin
19
+ if block_given?
20
+ example = wrapped_example.example
21
+ value = example.instance_exec(&block)
22
+ end # if
23
+
24
+ prior_value = ENV[key]
25
+ ENV[key] = value
26
+
27
+ wrapped_example.call
28
+ ensure
29
+ ENV[key] = prior_value
30
+ end # begin-ensure
31
+ end # around example
32
+ end # class method wrap_env
33
+ alias_method :stub_env, :wrap_env
34
+ end # module
35
+
36
+ def wrap_env key, value
37
+ prior_value = ENV[key]
38
+ ENV[key] = value
39
+
40
+ yield
41
+ ensure
42
+ ENV[key] = prior_value
43
+ end # method wrap_env
44
+ alias_method :stub_env, :wrap_env
45
+ end # module
46
+ end # module