varar 0.5.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: b755d326581e7e691a70063e5fb1905193d49d10aa0330e3f6060e1edf35a99d
4
+ data.tar.gz: 9ca4b86b148592bf6ec59b91a56dd57c4ba23d1ebf31126f52a56bddbb552431
5
+ SHA512:
6
+ metadata.gz: cafcb8f2d06411195cf648320ed6715df36108cfa18f8cf4694c0a0e466bc0ddf84f2d9dcbf0e60b242548ead3edc24ead4db74ee3d89db225fbf03f7c704bd1
7
+ data.tar.gz: 013c8f2eb13ac12fdf79ea7ee60aacfde0329920e22801c97befe7673bcf9d6f9cc98a7a4b773bdaf21aa8404abe55fca7335c2edddc2953d80dfbe650758b46
data/lib/varar/dsl.rb ADDED
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'varar/internal'
4
+
5
+ # `steps` as a top-level method, available in any step file after
6
+ # `require "varar"` (idiomatic for BDD step DSLs, like Cucumber-Ruby's
7
+ # Given/When/Then). It takes a block in which bare `stimulus`, `sensor` and
8
+ # `param` register the file's steps:
9
+ #
10
+ # steps(-> { { greeting: '' } }) do
11
+ # stimulus('I greet {string}') { |state, name| state.merge(greeting: "Hello, #{name}!") }
12
+ # sensor('the greeting is {string}') { |state, _expected| state[:greeting] }
13
+ # end
14
+ #
15
+ # The initial state is optional — omit it entirely for stateless step files —
16
+ # but when given it MUST be a Proc/lambda, called fresh for every example. A
17
+ # Hash was previously accepted and closed over a single object shared by every
18
+ # example in the file; nested values `deep_freeze` does not recurse into (it
19
+ # only descends Hash and Array) were then mutable and shared. Requiring a
20
+ # factory makes freshness structural rather than something the deep-freeze has
21
+ # to defend. The state is keyed by the calling file so contexts never bleed
22
+ # across step files.
23
+ module Kernel
24
+ private
25
+
26
+ def steps(factory = nil, &block)
27
+ unless factory.nil? || factory.is_a?(Proc)
28
+ raise ArgumentError,
29
+ 'steps expects a Proc/lambda state factory, called fresh per example — ' \
30
+ "got #{factory.class}. Wrap it: steps(-> { { count: 0 } })"
31
+ end
32
+
33
+ builder = Varar::Internal.register(factory || -> { {} }, caller_locations(1, 1).first.path)
34
+ builder.instance_eval(&block) if block
35
+ nil
36
+ end
37
+ end
@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'varar/core'
4
+
5
+ module Varar
6
+ # The module-scope step-registration accumulator behind the block DSL
7
+ # `steps(...) do stimulus(...); sensor(...) end`. Mirrors @varar/varar's
8
+ # internal.ts. A step file, when loaded, calls steps() once; the Builder its
9
+ # block registers into these accumulators. The runner/harness then reads
10
+ # them via build_registry / context_factory.
11
+ module Internal
12
+ @steps = []
13
+ @context_factories_by_file = {}
14
+ @custom_types = []
15
+
16
+ class << self
17
+ # Register a file's state factory and return a Builder whose
18
+ # stimulus/sensor/param methods accumulate that file's steps. +factory+
19
+ # is a callable (or nil for empty state); +source_file+ keys the
20
+ # per-file context factory. Raises if called twice for one file.
21
+ def register(factory, source_file)
22
+ raise "steps() called more than once in #{source_file}" if @context_factories_by_file.key?(source_file)
23
+
24
+ @context_factories_by_file[source_file] = factory || -> { {} }
25
+ Builder.new
26
+ end
27
+
28
+ # Accumulate one step. The handler's source_location anchors it to the
29
+ # line the block is written on.
30
+ def add_step(expression, handler, kind)
31
+ file, line = handler.source_location
32
+ @steps << {
33
+ expression: expression, source_file: file, source_line: line,
34
+ handler: handler, kind: kind
35
+ }
36
+ nil
37
+ end
38
+
39
+ # Accumulate one custom parameter type.
40
+ def add_custom_type(name, regexp, parse, format)
41
+ @custom_types << { name: name, regexp: regexp, parse: parse, format: format }
42
+ nil
43
+ end
44
+
45
+ # (step_file) -> state: invoke the file's factory, or {} if none.
46
+ def context_factory
47
+ factories = @context_factories_by_file.dup
48
+ lambda do |step_file|
49
+ factory = factories[step_file]
50
+ factory ? factory.call : {}
51
+ end
52
+ end
53
+
54
+ # Build a Core::Registry: custom parameter types first (so expressions
55
+ # can reference them), then steps in registration order.
56
+ def build_registry
57
+ registry = Core::Registries.create_registry
58
+ @custom_types.each do |type|
59
+ registry = Core::Registries.define_parameter_type(
60
+ registry, name: type[:name], regexp: type[:regexp], parse: type[:parse], format: type[:format]
61
+ )
62
+ end
63
+ @steps.each do |step|
64
+ registry = Core::Registries.add_step(
65
+ registry,
66
+ expression: step[:expression],
67
+ expression_source_file: step[:source_file],
68
+ expression_source_line: step[:source_line],
69
+ handler: step[:handler],
70
+ kind: step[:kind]
71
+ )
72
+ end
73
+ registry
74
+ end
75
+
76
+ # Clear all accumulated state (between isolated runs / harness bundles).
77
+ def reset_builder
78
+ @steps = []
79
+ @context_factories_by_file = {}
80
+ @custom_types = []
81
+ end
82
+
83
+ # Conformance-harness accessor: custom parameter types projected to the
84
+ # {"name","regexp"} wire shape. `regexp` is the bare source (Regexp#source
85
+ # or the string as authored) — the cross-port convention.
86
+ def custom_parameter_types
87
+ @custom_types.map do |type|
88
+ regexp = type[:regexp]
89
+ regexp = regexp.source if regexp.is_a?(Regexp)
90
+ unless regexp.is_a?(String)
91
+ raise "parameter type #{type[:name].inspect}: regexp arrays are not supported " \
92
+ 'by the conformance projection yet'
93
+ end
94
+ { 'name' => type[:name], 'regexp' => regexp }
95
+ end
96
+ end
97
+ end
98
+
99
+ # The block-scoped authoring DSL. A Builder is `instance_eval`-ed with the
100
+ # `steps` block, so authors write bare `stimulus`/`sensor`/`param` calls;
101
+ # each delegates to the accumulator above.
102
+ class Builder
103
+ def stimulus(expression, &handler)
104
+ Internal.add_step(expression, handler, 'stimulus')
105
+ end
106
+
107
+ def sensor(expression, &handler)
108
+ Internal.add_step(expression, handler, 'sensor')
109
+ end
110
+
111
+ def param(name, regexp, parse: nil, format: nil)
112
+ Internal.add_custom_type(name, regexp, parse, format)
113
+ end
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'varar/internal'
4
+
5
+ module Varar
6
+ # Adapter/harness glue — mirrors the `@varar/varar/registry` subpath.
7
+ # Authors import only `steps` (via varar); runners and the conformance
8
+ # harness reach the accumulator through here.
9
+ module RegistryGlue
10
+ module_function
11
+
12
+ def reset_builder
13
+ Internal.reset_builder
14
+ end
15
+
16
+ def build_registry
17
+ Internal.build_registry
18
+ end
19
+
20
+ def context_factory
21
+ Internal.context_factory
22
+ end
23
+
24
+ def custom_parameter_types
25
+ Internal.custom_parameter_types
26
+ end
27
+ end
28
+ end
data/lib/varar.rb ADDED
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'varar/core'
4
+ require 'varar/internal'
5
+ require 'varar/dsl'
6
+
7
+ module Varar
8
+ # The author facade: `steps` (top-level DSL) → [param, stimulus, sensor],
9
+ # backed by the module-scope accumulator in Internal.
10
+ VERSION = '0.5.0'
11
+ end
metadata ADDED
@@ -0,0 +1,73 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: varar
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.5.0
5
+ platform: ruby
6
+ authors:
7
+ - Aslak Hellesøy
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: cucumber-cucumber-expressions
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - '='
17
+ - !ruby/object:Gem::Version
18
+ version: 20.0.0
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - '='
24
+ - !ruby/object:Gem::Version
25
+ version: 20.0.0
26
+ - !ruby/object:Gem::Dependency
27
+ name: varar-core
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - '='
31
+ - !ruby/object:Gem::Version
32
+ version: 0.5.0
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - '='
38
+ - !ruby/object:Gem::Version
39
+ version: 0.5.0
40
+ description: 'The Vár author facade: define_state and the step-registration accumulator.'
41
+ email:
42
+ - aslak@oselvar.com
43
+ executables: []
44
+ extensions: []
45
+ extra_rdoc_files: []
46
+ files:
47
+ - lib/varar.rb
48
+ - lib/varar/dsl.rb
49
+ - lib/varar/internal.rb
50
+ - lib/varar/registry.rb
51
+ homepage: https://varar.dev
52
+ licenses:
53
+ - MIT
54
+ metadata:
55
+ rubygems_mfa_required: 'true'
56
+ rdoc_options: []
57
+ require_paths:
58
+ - lib
59
+ required_ruby_version: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: '3.2'
64
+ required_rubygems_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ requirements: []
70
+ rubygems_version: 4.0.16
71
+ specification_version: 4
72
+ summary: Markdown-native BDD — author API (define_state)
73
+ test_files: []