module NRSER::RSpex::ExampleGroup
Definitions
¶ ↑
Public Instance Methods
Short names (need `_` pre 'cause of `when` Ruby keyword, and suffix fucks up auto-indent in Atom/VSCode)
Old name (used to be different method)
Describe an attribute of the parent subject.
@return [void]
# File lib/nrser/rspex/example_group/describe_attribute.rb, line 10 def describe_attribute symbol, **metadata, &body describe_x \ NRSER::RSpex::Format.md_code_quote( "##{ symbol }" ), type: :attribute, metadata: metadata, subject_block: -> { super().public_send symbol }, &body end
Version
of {#describe_called_with} for when you have no arguments.
@param [#call] body
Block to execute in the context of the example group after refining the subject.
@return [void]
# File lib/nrser/rspex/example_group/describe_called_with.rb, line 49 def describe_called &body describe_x Args(), type: :called_with, subject_block: -> { super().call }, &body end
Create a new {RSpec.describe} section where the subject is set by calling the parent subject with `args` and evaluate `block` in it.
@example
describe "hi sayer" do subject{ ->( name ) { "Hi #{ name }!" } } describe_called_with 'Mom' do it { is_expected.to eq 'Hi Mom!' } end end
@param [Array] args
Arguments to call `subject` with to produce the new subject.
@param [#call] body
Block to execute in the context of the example group after refining the subject.
@return [void]
# File lib/nrser/rspex/example_group/describe_called_with.rb, line 27 def describe_called_with *args, &body describe_x Args(*args), type: :called_with, subject_block: -> { super().call *args }, &body end
@todo Document describe_use_case
method.
@return [void]
# File lib/nrser/rspex/example_group/describe_case.rb, line 9 def describe_case *description, where: {}, **metadata, &body describe_x \ *description, type: :case, bindings: where, metadata: metadata, &body end
@todo Document describe_class
method.
@return [void]
# File lib/nrser/rspex/example_group/describe_class.rb, line 10 def describe_class klass, *description, bind_subject: true, **metadata, &body subject_block = if bind_subject -> { klass } end describe_x \ NRSER::RSpex::Format.md_code_quote( klass.name ), klass.source_location, *description, type: :class, metadata: { **metadata, class: klass, }, subject_block: subject_block, &body end
Describe a “group”. Doesn't really do much. Didn't end up getting used much. Probably not long for this world.
@param *description (see describe_x
)
@param [Hash<Symbol, Object>] metadata
RSpec metadata to set for the example group. See the `metadata` keyword argument to {#describe_x}.
@param &body (see describe_x
)
@return (see describe_x
)
# File lib/nrser/rspex/example_group/describe_group.rb, line 20 def describe_group *description, **metadata, &body # Pass up to {#describe_x} describe_x \ *description, type: :group, metadata: metadata, &body end
Describe an instance of the described class by providing arguments for it's construction.
@param [Array] constructor_args
Arguments to pass to `.new` on {#described_class} to create instances.
@return [void]
# File lib/nrser/rspex/example_group/describe_instance.rb, line 13 def describe_instance *constructor_args, &body describe_x ".new", Args(*constructor_args), type: :instance, metadata: { constructor_args: constructor_args, }, subject_block: -> { described_class.new *described_constructor_args }, &body end
# File lib/nrser/rspex/example_group/describe_instance_method.rb, line 5 def describe_instance_method name, **metadata, &block describe( "##{ name }", type: :instance_method, method_name: name, **metadata ) do if name.is_a? Symbol subject { super().method name } end module_exec &block end end
Describe a {NRSER::Message}. Useful when you have a message that you want to send to many receivers (see {#describe_sent_to}).
@note
Since the block is used for the example group body, if you want to describe a message with a {NRSER::Message#block} your need to create the message yourself and pass it as the only argument.
@see describe_x
@param [Array] args
Passed to {NRSER::Message.from} to get or create the message instance.
@param &body (see describe_x
)
@return (see describe_x
)
# File lib/nrser/rspex/example_group/describe_message.rb, line 32 def describe_message *args, &body message = NRSER::Message.from *args describe_x \ message, type: :message, metadata: { message: message, }, subject_block: -> { message.send_to super() }, &body end
Describe a method of the parent subject.
@param [Method | Symbol
| String] method
The method being described: 1. {Method} instance - used directly. 2. {Symbol}, {String} -
@return [void]
# File lib/nrser/rspex/example_group/describe_method.rb, line 17 def describe_method method, *description, bind_subject: nil, **metadata, &body case method when Method method_name = method.name subject_block = -> { method } bind_subject = true name_string = NRSER::RSpex::Format.md_code_quote \ "#{ method.receiver }.#{ method.name }" when Symbol, String method_name = method # Due to legacy, we only auto-bind if the name is a symbol # # TODO Get rid of this # bind_subject = method_name.is_a?( Symbol ) if bind_subject.nil? subject_block = if bind_subject -> { super().method method_name } end name_prefix = if self.respond_to?( :metadata ) && self.metadata.key?( :constructor_args ) '#' else '.' end method = if self.try( :metadata ) getter = if self.metadata.key?( :constructor_args ) :instance_method else :method end target = self.metadata[:class] || self.metadata[:module] if target begin target.public_send getter, method_name rescue nil end end end name_string = NRSER::RSpex::Format.md_code_quote \ "#{ name_prefix }#{ method_name }" else raise NRSER::TypeError.new \ "Expected Method, Symbol or String for `method_name`, found", method_name end # case method_arg # Create the RSpec example group context describe_x \ name_string, NRSER::Meta::Source::Location.new( method ), *description, type: :method, metadata: { **metadata, method_name: method_name, }, bind_subject: bind_subject, subject_block: subject_block, &body end
# File lib/nrser/rspex/example_group/describe_module.rb, line 6 def describe_module mod, bind_subject: true, **metadata, &body describe_x \ mod, type: :module, metadata: { module: mod, **metadata, }, bind_subject: bind_subject, subject_block: -> { mod }, &body end
Describe the response of the subject to a {NRSER::Message}.
Pretty much a short-cut for nesting {#describe_method} / {#describe_called_with}. Meh.
@param [Array] args
Passed to {NRSER::Message.from} to get or create the message instance.
@param &body (see describe_x
)
@return (see describe_x
)
# File lib/nrser/rspex/example_group/describe_response_to.rb, line 18 def describe_response_to *args, &body msg = NRSER::Message.from *args # Pass up to {#describe_x} describe_x \ msg, type: :response_to, subject_block: -> { msg.send_to super() }, &body end
Describe a “section”. Just like {RSpec.describe} except it:
-
Expects a string title.
-
Prepends a little section squiggle `§` to the title so sections are easier to pick out visually.
-
Adds `type: :section` metadata.
@param *description (see describe_x
)
@param [Hash<Symbol, Object>] metadata
RSpec metadata to set for the example group. See the `metadata` keyword argument to {#describe_x}.
@param &body (see describe_x
)
@return (see describe_x
)
# File lib/nrser/rspex/example_group/describe_section.rb, line 26 def describe_section *description, **metadata, &body # Pass up to {#describe_x} describe_x \ *description, type: :section, metadata: metadata, &body end
For use when `subject` is a {NRSER::Message}. Create a new context for the `receiver` where the subject is the result of sending that message to the receiver.
@param [Object] receiver
Object that will receive the message to create the new subject. If it's a {Wrapper} it will be unwrapped in example contexts of the new example group.
@param [Boolean] publicly
Send message publicly via {Object#public_send} (default) or privately via {Object.send}.
@param bind_subject: (see describe_x
) @param &body (see describe_x
)
@return (see describe_x
)
# File lib/nrser/rspex/example_group/describe_sent_to.rb, line 25 def describe_sent_to receiver, publicly: true, bind_subject: true, &body mode = if publicly "publicly" else "privately" end describe_x \ receiver, "(#{ mode })", type: :sent_to, bind_subject: bind_subject, subject_block: -> { super().send_to \ unwrap( receiver, context: self ), publicly: publicly }, &body end
Setup describes what's going to be done in all child examples.
It's where you setup your `subject`, usually depending on `let` bindings that are provided in the children.
@return [void]
# File lib/nrser/rspex/example_group/describe_setup.rb, line 12 def describe_setup *description, **metadata, &body describe_x \ *description, type: :setup, metadata: metadata, &body end
Create an example group covering a source file.
Useful for when method implementations are spread out across multiple files but you want to group examples by the source file they're in.
@note
Honestly, now that modules, classes and methods described through RSpex add their source locations, this is not all that useful. But it was there from before that, which is why for the moment it's still here.
@see describe_x
@param [String | Pathname] path
File path.
@param *description (see describe_x
)
@param [Hash<Symbol, Object>] metadata
RSpec metadata to set for the example group. See the `metadata` keyword argument to {#describe_x}. A `file` key is added pointed to the {Pathname} for `path` before passing up to {#describe_x}.
@param &body (see describe_x
)
@return (see describe_x
)
# File lib/nrser/rspex/example_group/describe_source_file.rb, line 35 def describe_source_file path, *description, **metadata, &body path = path.to_pn describe_x \ path, *description, type: :source_file, metadata: { source_file: path, **metadata, }, &body end
EXPERIMENTAL
Example
group helper for use at the top level of each spec file to set a bunch of stuff up and build a helpful description.
@todo
This is totally just a one-off right now... would need to be generalized quite a bit... 1. Extraction of module, class, etc from metadata should be flexible 2. Built description would need to be conditional on what metadata was found.
@param [String] description
A description of the spec file to add to the RSpec description.
@param [String] spec_path
The path to the spec file (just feed it `__FILE__`). Probably possible to extract this somehow without having to provide it?
@return (see describe_x
)
# File lib/nrser/rspex/example_group/describe_spec_file.rb, line 51 def describe_spec_file description: nil, spec_path:, bind_subject: true, **metadata, &body chain = [] [ :source_file, :module, :class, :instance, :method, :instance_method, :called_with, :attribute, ].each do |type| if data = metadata.delete( type ) chain << [type, data] end end describe_x_body = if chain.empty? body else -> { dive_x *chain, bind_subject: bind_subject, &body } end describe_x \ NRSER::RSpex.dot_rel_path( spec_path ), *description, type: :spec_file, metadata: metadata, &describe_x_body end
Define a example group binding a subject.
@note Experimental - only used in Rash at the moment. I've wanted something
like this to make binding subject less noisy, but I'm not sure this is exactly it yet...
@see describe_x
@param [Object] subject
The value to bind as the subject. May be wrapped.
@param [Hash<Symbol, Object>] metadata
Optional metadata for the example group.
@param &body (see describe_x
)
@return (see describe_x
)
# File lib/nrser/rspex/example_group/describe_subject.rb, line 34 def describe_subject subject, **metadata, &body describe_x \ subject, type: :subject, metadata: metadata, subject_block: -> { unwrap subject, context: self }, &body end
Define a example group with the keyword args as bindings.
@see describe_x
@param *description (see describe_x
)
@param [Hash<Symbol, Object>] bindings
See the `bindings` keyword arg in {#describe_x}.
@param &body (see describe_x
)
@return (see describe_x
)
# File lib/nrser/rspex/example_group/describe_when.rb, line 19 def describe_when *description, **bindings, &body describe_x \ *description, type: :when, bindings: bindings, &body end
The core, mostly internal method that all RSpex's description methods lead back too (or should / will when refactoring is done).
Keyword options are explicitly broken out in this method, versus the sugary ones that call it, so `metadata` can be set without restriction (save the `type` key, which is also it's own keyword here). You can use this method if you want the RSpex
functionality but absolutely have to set some metadata key that we use for something else.
@param [Array] description
Optional list of elements that compose the custom description. Will be passed to {NRSER::RSpex::Format.description} to produce the string value that is in turn passed to {RSpec.describe}.
@param [Symbol] type
The RSpex "type" of the example group, which is used to determine the prefix of the final description and is assigned to the `:type` metadata key.
@param [Hash<Symbol, Object>] metadata
[RSpec metadata][] to add to the new example group. In addition to the keys RSpec will reject, we prohibit `:type` *unless* it is the same as the `type` keyword argument or `nil`. In either of these cases, the `type` keyword arg will be used for the new example group's `:type` metadata value. [RSpec metadata]: https://relishapp.com/rspec/rspec-core/docs/metadata/user-defined-metadata
@param [Hash<Symbol, Object>] bindings
Name to value pairs to bind in the new example group. All values will be bound at the example group and example levels - though if they are {Wrapper}, that wrapper will be available at the group level, while they will be automatically unwrapped at the example level (as the requisite context is available there).
@param [Boolean] bind_subject
When `true` (and there is a `subject_block`) bind the `subject` inside the new example group.
@return [void]
# File lib/nrser/rspex/example_group/describe_x.rb, line 52 def describe_x *description, type:, metadata: {}, bindings: {}, add_binding_desc: true, bind_subject: true, subject_block: nil, &body # Check that `metadata` doesn't have a `:type` value too... although we # allow it if's equal to `type` or `nil` 'cause why not I guess? # if metadata.key?( :type ) && metadata[:type] != nil && metadata[:type] != type raise ArgumentError.new binding.erb <<-END `metadata:` keyword argument may not have a `:type` key that conflicts with the `type:` keyword argument. Received: `type`: <%= type.inspect %> `metadata[:type]`: <%= metadata[:type].pretty_inspect %> END end # Add description of the bindings, if we have any and were told to unless bindings.empty? || add_binding_desc == false bindings_desc = NRSER::RSpex::Format.md_code_quote \ bindings.map { |name, value| "#{ name } = #{ value.inspect }" }.join( '; ' ) if description.empty? description = bindings_desc else description << "(" + bindings_desc + ")" end end # Call up to RSpec's `#describe` method describe( NRSER::RSpex::Format.description( *description, type: type ), **metadata, type: type, ) do if subject_block && bind_subject subject &subject_block end # Bind bindings unless bindings.empty? bindings.each { |name, value| # Example-level binding let( name ) { unwrap value, context: self } # Example group-level binding (which may return a {Wrapper} that # of course can not be unwrapped at the group level) define_singleton_method( name ) { value } } end module_exec &body end # describe end
# File lib/nrser/rspex/example_group/describe_spec_file.rb, line 7 def dive_x current, *rest, **kwds, &body type, data = current method_name = "describe_#{ type }" block = if rest.empty? body else -> { dive_x *rest, &body } end begin public_send method_name, data, **kwds, &block rescue NoMethodError => error pp self.methods raise error end end
Aliases to other names I was using at first… not preferring their use at the moment.