markdown_exec 3.0.5 → 3.0.7

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.
@@ -0,0 +1,100 @@
1
+ # UX Block Init and Act Keys
2
+
3
+ The `init` and `act` keys determine which other key is read for processing during initialization and activation respectively.
4
+
5
+ ## Algorithm
6
+
7
+ 1. **Init Phase** (when document is loaded) per FCB.init_source:
8
+ - If `init` is `false`: No initialization occurs
9
+ - If `init` is a string: That string becomes the initial value
10
+ - If `init` is `:allow`: First value from `allow` list is used
11
+ - If `init` is `:echo`: Value from `echo` key is evaluated and returned
12
+ - If `init` is `:exec`: Command from `exec` key is executed and stdout is returned
13
+ - If `init` is not present: Defaults to first available in order:
14
+ - `:allow` if `allow` exists
15
+ - `:default` if `default` exists
16
+ - `:echo` if `echo` exists
17
+ - `:exec` if `exec` exists
18
+ - `false` if none of the above exist
19
+
20
+ 2. **Act Phase** (when block is activated) per FCB.act_source:
21
+ - If `act` is `false`: Block cannot be activated
22
+ - If `act` is `:allow`: User selects from `allow` list
23
+ - If `act` is `:echo`: Value from `echo` key is evaluated and returned
24
+ - If `act` is `:edit`: User is prompted for input
25
+ - If `act` is `:exec`: Command from `exec` key is executed and stdout is returned
26
+ - If `act` is not present: Defaults to:
27
+ - If `init` is `false`:
28
+ - First available in order: `:allow`, `:echo`, `:edit`, `:exec`
29
+ - Otherwise:
30
+ - `:allow` if `allow` exists
31
+ - `:edit` if `allow` does not exist
32
+
33
+ ## Examples
34
+
35
+ ### Echo on Init, Exec on Act
36
+ ```ux
37
+ name: DEPLOY_CONFIG
38
+ init: :echo
39
+ echo: "Deploying ${VERSION} to ${ENVIRONMENT}"
40
+ act: :exec
41
+ exec: "deploy.sh ${ENVIRONMENT} ${VERSION}"
42
+ ```
43
+ Behavior:
44
+ - On init: Evaluates echo string "Deploying ${VERSION} to ${ENVIRONMENT}"
45
+ - On act: Executes deploy.sh with environment and version parameters
46
+
47
+ ### Allow on Init, Edit on Act
48
+ ```ux
49
+ name: ENVIRONMENT
50
+ init: :allow
51
+ allow:
52
+ - development
53
+ - staging
54
+ - production
55
+ act: :edit
56
+ prompt: Select environment
57
+ ```
58
+ Behavior:
59
+ - On init: Uses first allowed value (development)
60
+ - On act: Prompts user to select from allowed values
61
+
62
+ ### Exec on Init, Echo on Act
63
+ ```ux
64
+ name: CURRENT_DIR
65
+ init: :exec
66
+ exec: basename $(pwd)
67
+ act: :echo
68
+ echo: "Current directory: ${CURRENT_DIR}"
69
+ ```
70
+ Behavior:
71
+ - On init: Executes basename command on current directory
72
+ - On act: Evaluates echo string with current directory value
73
+
74
+ ### Allow on Both
75
+ ```ux
76
+ name: API_KEY
77
+ init: :allow
78
+ allow:
79
+ - ${PROD_API_KEY}
80
+ - ${STAGING_API_KEY}
81
+ - ${DEV_API_KEY}
82
+ act: :allow
83
+ require:
84
+ - ENVIRONMENT
85
+ ```
86
+ Behavior:
87
+ - On init: Uses first allowed API key
88
+ - On act: Shows menu of allowed API keys for selection
89
+
90
+ ### Echo on Both
91
+ ```ux
92
+ name: SHELL_VERSION
93
+ init: :echo
94
+ echo: $SHELL
95
+ act: :echo
96
+ echo: "Using shell: ${SHELL_VERSION}"
97
+ ```
98
+ Behavior:
99
+ - On init: Gets shell value from environment
100
+ - On act: Evaluates echo string with current shell value
@@ -32,6 +32,18 @@ class CommandResult
32
32
  exit_status.zero?
33
33
  end
34
34
 
35
+ # def new_lines
36
+ # value = @attributes[:new_lines]
37
+ # ww caller.deref[0..4], value
38
+ # value
39
+ # end
40
+
41
+ # # trap assignment to new_lines
42
+ # def new_lines=(value)
43
+ # ww caller.deref[0..4], value
44
+ # @attributes[:new_lines] = value
45
+ # end
46
+
35
47
  def method_missing(name, *args)
36
48
  key = name.to_s.chomp('=').to_sym
37
49
 
@@ -0,0 +1,233 @@
1
+ #!/usr/bin/env -S bundle exec ruby
2
+ # frozen_string_literal: true
3
+
4
+ # encoding=utf-8
5
+
6
+ # Alternative implementations of CommandResult that provide the same specs
7
+
8
+ # Option 1: Using OpenStruct as base with method overrides
9
+ require 'ostruct'
10
+
11
+ class CommandResultOpenStruct < OpenStruct
12
+ def initialize(**attributes)
13
+ # Set defaults
14
+ defaults = { exit_status: 0, stdout: '' }
15
+ super(defaults.merge(attributes))
16
+ end
17
+
18
+ def failure?
19
+ !success?
20
+ end
21
+
22
+ def success?
23
+ exit_status.zero?
24
+ end
25
+
26
+ # Intercept specific setter
27
+ def new_lines=(value)
28
+ warn caller.deref[0..4], value
29
+ super(value)
30
+ end
31
+ end
32
+
33
+ # Option 2: Using instance variables with define_method
34
+ class CommandResultInstanceVars
35
+ def initialize(**attributes)
36
+ @exit_status = 0
37
+ @stdout = ''
38
+
39
+ attributes.each do |name, value|
40
+ instance_variable_set("@#{name}", value)
41
+
42
+ # Define getter and setter methods dynamically
43
+ self.class.define_method(name) do
44
+ instance_variable_get("@#{name}")
45
+ end
46
+
47
+ self.class.define_method("#{name}=") do |val|
48
+ instance_variable_set("@#{name}", val)
49
+ end
50
+ end
51
+ end
52
+
53
+ def failure?
54
+ !success?
55
+ end
56
+
57
+ def success?
58
+ exit_status.zero?
59
+ end
60
+
61
+ # Intercept specific setter
62
+ def new_lines=(value)
63
+ warn caller.deref[0..4], value
64
+ @new_lines = value
65
+ end
66
+
67
+ def method_missing(name, *args)
68
+ if name.to_s.end_with?('=')
69
+ # Setter
70
+ attr_name = name.to_s.chomp('=')
71
+ instance_variable_set("@#{attr_name}", args.first)
72
+
73
+ # Define methods for future use
74
+ self.class.define_method(attr_name) do
75
+ instance_variable_get("@#{attr_name}")
76
+ end
77
+
78
+ self.class.define_method(name) do |val|
79
+ instance_variable_set("@#{attr_name}", val)
80
+ end
81
+ else
82
+ # Getter
83
+ if instance_variable_defined?("@#{name}")
84
+ instance_variable_get("@#{name}")
85
+ else
86
+ super
87
+ end
88
+ end
89
+ end
90
+
91
+ def respond_to_missing?(name, include_private = false)
92
+ attr_name = name.to_s.chomp('=').to_sym
93
+ instance_variable_defined?("@#{attr_name}") || super
94
+ end
95
+ end
96
+
97
+ # Option 3: Using Struct with dynamic extension
98
+ class CommandResultStruct
99
+ def self.new(**attributes)
100
+ # Create a Struct class with the given attributes plus defaults
101
+ defaults = { exit_status: 0, stdout: '' }
102
+ all_attrs = defaults.merge(attributes)
103
+
104
+ struct_class = Struct.new(*all_attrs.keys, keyword_init: true) do
105
+ def failure?
106
+ !success?
107
+ end
108
+
109
+ def success?
110
+ exit_status.zero?
111
+ end
112
+
113
+ # Intercept specific setter
114
+ def new_lines=(value)
115
+ warn caller.deref[0..4], value
116
+ super(value)
117
+ end
118
+
119
+ # Add dynamic attribute support
120
+ def method_missing(name, *args)
121
+ if name.to_s.end_with?('=')
122
+ # Add new attribute dynamically by recreating struct
123
+ attr_name = name.to_s.chomp('=').to_sym
124
+ current_attrs = to_h
125
+ current_attrs[attr_name] = args.first
126
+
127
+ # This is a limitation - we can't easily add new fields to existing Struct
128
+ # So this approach has constraints
129
+ super
130
+ else
131
+ super
132
+ end
133
+ end
134
+ end
135
+
136
+ struct_class.new(**all_attrs)
137
+ end
138
+ end
139
+
140
+ # Option 4: Using SimpleDelegator with Hash
141
+ require 'delegate'
142
+
143
+ class CommandResultDelegator < SimpleDelegator
144
+ def initialize(**attributes)
145
+ defaults = { exit_status: 0, stdout: '' }
146
+ @hash = defaults.merge(attributes)
147
+ super(@hash)
148
+ end
149
+
150
+ def failure?
151
+ !success?
152
+ end
153
+
154
+ def success?
155
+ self[:exit_status].zero?
156
+ end
157
+
158
+ # Intercept specific setter
159
+ def new_lines=(value)
160
+ warn caller.deref[0..4], value
161
+ self[:new_lines] = value
162
+ end
163
+
164
+ def method_missing(name, *args)
165
+ key = name.to_s.chomp('=').to_sym
166
+
167
+ if name.to_s.end_with?('=') # setter
168
+ self[key] = args.first
169
+ elsif key?(key) # getter
170
+ self[key]
171
+ else
172
+ super
173
+ end
174
+ end
175
+
176
+ def respond_to_missing?(name, include_private = false)
177
+ key = name.to_s.chomp('=').to_sym
178
+ key?(key) || super
179
+ end
180
+ end
181
+
182
+ # Option 5: Using refinements for cleaner method_missing
183
+ module AttributeAccess
184
+ refine Hash do
185
+ def method_missing(name, *args)
186
+ key = name.to_s.chomp('=').to_sym
187
+
188
+ if name.to_s.end_with?('=') # setter
189
+ self[key] = args.first
190
+ elsif key?(key) # getter
191
+ self[key]
192
+ else
193
+ super
194
+ end
195
+ end
196
+
197
+ def respond_to_missing?(name, include_private = false)
198
+ key = name.to_s.chomp('=').to_sym
199
+ key?(key) || super
200
+ end
201
+ end
202
+ end
203
+
204
+ class CommandResultRefined
205
+ using AttributeAccess
206
+
207
+ def initialize(**attributes)
208
+ @attributes = { exit_status: 0, stdout: '' }.merge(attributes)
209
+ @attributes.extend(AttributeAccess::Hash)
210
+ end
211
+
212
+ def failure?
213
+ !success?
214
+ end
215
+
216
+ def success?
217
+ @attributes.exit_status.zero?
218
+ end
219
+
220
+ # Intercept specific setter
221
+ def new_lines=(value)
222
+ warn caller.deref[0..4], value
223
+ @attributes.new_lines = value
224
+ end
225
+
226
+ def method_missing(name, *args)
227
+ @attributes.send(name, *args)
228
+ end
229
+
230
+ def respond_to_missing?(name, include_private = false)
231
+ @attributes.respond_to?(name, include_private)
232
+ end
233
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+ require_relative 'ww'
3
+
4
+ # A class that provides an interface to ENV variables with customizable getters and setters
5
+ class EnvInterface
6
+ class << self
7
+ # Get an environment variable with optional transformation
8
+ # @param key [String] The environment variable name
9
+ # @param default [Object] Default value if the variable is not set
10
+ # @param transform [Proc] Optional transformation to apply to the value
11
+ # @return [Object] The environment variable value
12
+ def get(key, default: nil, transform: nil)
13
+ ww key, \
14
+ value = ENV[key]
15
+ return default if value.nil?
16
+
17
+ transform ? transform.call(value) : value
18
+ end
19
+
20
+ # Set an environment variable with optional transformation
21
+ # @param key [String] The environment variable name
22
+ # @param value [Object] The value to set
23
+ # @param transform [Proc] Optional transformation to apply before setting
24
+ # @return [String] The set value
25
+ def set(key, value, transform: nil)
26
+ ww key, caller.deref[0..3], value
27
+ transformed_value = transform ? transform.call(value) : value
28
+ ENV[key] = transformed_value.to_s
29
+ end
30
+
31
+ # Check if an environment variable exists
32
+ # @param key [String] The environment variable name
33
+ # @return [Boolean] true if the variable exists
34
+ def exists?(key)
35
+ ENV.key?(key)
36
+ end
37
+
38
+ # Delete an environment variable
39
+ # @param key [String] The environment variable name
40
+ # @return [String] The deleted value
41
+ def delete(key)
42
+ ENV.delete(key)
43
+ end
44
+
45
+ # Get all environment variables
46
+ # @return [Hash] All environment variables
47
+ def all
48
+ ENV.to_h
49
+ end
50
+
51
+ # Clear all environment variables
52
+ # @return [void]
53
+ def clear
54
+ ENV.clear
55
+ end
56
+ end
57
+ end