oselvar-var 0.3.2

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