circulator 2.1.3 → 2.1.4
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 +4 -4
 - data/CHANGELOG.md +17 -5
 - data/README.md +49 -1
 - data/lib/circulator/dot.rb +18 -4
 - data/lib/circulator/flow.rb +13 -6
 - data/lib/circulator/version.rb +1 -1
 - data/lib/circulator.rb +55 -0
 - metadata +1 -1
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA256:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: 9126192a7ce18956bc8c27842e52765dac25e9436e322ad259100e5138a5b97f
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: f1e15524d5806a3f0b0455bd006e8ba5e3914b13e046ab0d2acd1dbcb897ea27
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 03ad68c5d43c45fddf1b7d5c3f84dd845363b2bf8cc1a881463efbd1b1e43d233e196e965dae157726b2ac0b90066d9ec05087f12339667ffd1e9effe0213e54
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: 7590c58b27a608f4d49f6041c9fed8a4fd5a05526cd5c36eba40a12346ce5a63cd6f9f0a6253c48ec6aa7405e9e4b5ed8121fae276404a1a32f4587118453c2f
         
     | 
    
        data/CHANGELOG.md
    CHANGED
    
    | 
         @@ -5,12 +5,24 @@ All notable changes to this project will be documented in this file. 
     | 
|
| 
       5 
5 
     | 
    
         
             
            The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
         
     | 
| 
       6 
6 
     | 
    
         
             
            and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
         
     | 
| 
       7 
7 
     | 
    
         | 
| 
       8 
     | 
    
         
            -
            ## [2.1. 
     | 
| 
      
 8 
     | 
    
         
            +
            ## [2.1.4] - 2025-11-03
         
     | 
| 
       9 
9 
     | 
    
         | 
| 
       10 
     | 
    
         
            -
            ###  
     | 
| 
      
 10 
     | 
    
         
            +
            ### Added
         
     | 
| 
       11 
11 
     | 
    
         | 
| 
       12 
     | 
    
         
            -
            -  
     | 
| 
      
 12 
     | 
    
         
            +
            - Symbol-based allow_if support (a96b794)
         
     | 
| 
      
 13 
     | 
    
         
            +
            - Documentation for symbol-based allow_if (d3befc1)
         
     | 
| 
      
 14 
     | 
    
         
            +
            - available_flows method with guard and argument support (122e2ab)
         
     | 
| 
      
 15 
     | 
    
         
            +
            - available_flow? predicate method (122e2ab)
         
     | 
| 
      
 16 
     | 
    
         
            +
            - Documentation for query methods (a75507e)
         
     | 
| 
       13 
17 
     | 
    
         | 
| 
       14 
     | 
    
         
            -
            ###  
     | 
| 
      
 18 
     | 
    
         
            +
            ### Changed
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
            - Validate symbol allow_if at definition time (392eac5)
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
            ### Removed
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
            - Redundant respond_to? check (40ff669)
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
            ### Fixed
         
     | 
| 
       15 
27 
     | 
    
         | 
| 
       16 
     | 
    
         
            -
            -  
     | 
| 
      
 28 
     | 
    
         
            +
            - DOT syntax error for states ending with ? (65b503e)
         
     | 
    
        data/README.md
    CHANGED
    
    | 
         @@ -103,10 +103,36 @@ order.status_pending?     # => false 
     | 
|
| 
       103 
103 
     | 
    
         | 
| 
       104 
104 
     | 
    
         
             
            These predicate methods work with both symbol and string values, automatically converting strings to symbols for comparison.
         
     | 
| 
       105 
105 
     | 
    
         | 
| 
      
 106 
     | 
    
         
            +
            #### Query Available Actions
         
     | 
| 
      
 107 
     | 
    
         
            +
             
     | 
| 
      
 108 
     | 
    
         
            +
            Circulator provides methods to query which actions are available from the current state:
         
     | 
| 
      
 109 
     | 
    
         
            +
             
     | 
| 
      
 110 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 111 
     | 
    
         
            +
            order.status = :pending
         
     | 
| 
      
 112 
     | 
    
         
            +
             
     | 
| 
      
 113 
     | 
    
         
            +
            # Get all available actions
         
     | 
| 
      
 114 
     | 
    
         
            +
            order.available_flows(:status)  # => [:approve, :reject]
         
     | 
| 
      
 115 
     | 
    
         
            +
             
     | 
| 
      
 116 
     | 
    
         
            +
            # Check if a specific action is available
         
     | 
| 
      
 117 
     | 
    
         
            +
            order.available_flow?(:status, :approve)  # => true
         
     | 
| 
      
 118 
     | 
    
         
            +
            order.available_flow?(:status, :ship)     # => false
         
     | 
| 
      
 119 
     | 
    
         
            +
            ```
         
     | 
| 
      
 120 
     | 
    
         
            +
             
     | 
| 
      
 121 
     | 
    
         
            +
            These methods respect all `allow_if` conditions and can accept arguments to pass through to guard conditions:
         
     | 
| 
      
 122 
     | 
    
         
            +
             
     | 
| 
      
 123 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 124 
     | 
    
         
            +
            # With conditional guards
         
     | 
| 
      
 125 
     | 
    
         
            +
            order.available_flow?(:status, :approve, level: 5)  # => true
         
     | 
| 
      
 126 
     | 
    
         
            +
            ```
         
     | 
| 
      
 127 
     | 
    
         
            +
             
     | 
| 
       106 
128 
     | 
    
         
             
            ### Advanced Features
         
     | 
| 
       107 
129 
     | 
    
         | 
| 
       108 
130 
     | 
    
         
             
            #### Conditional Transitions with Guards
         
     | 
| 
       109 
131 
     | 
    
         | 
| 
      
 132 
     | 
    
         
            +
            You can control when transitions are allowed using the `allow_if` option. Circulator supports three types of guards:
         
     | 
| 
      
 133 
     | 
    
         
            +
             
     | 
| 
      
 134 
     | 
    
         
            +
            **Proc-based guards** evaluate a block of code:
         
     | 
| 
      
 135 
     | 
    
         
            +
             
     | 
| 
       110 
136 
     | 
    
         
             
            ```ruby
         
     | 
| 
       111 
137 
     | 
    
         
             
            class Document
         
     | 
| 
       112 
138 
     | 
    
         
             
              extend Circulator
         
     | 
| 
         @@ -125,7 +151,29 @@ class Document 
     | 
|
| 
       125 
151 
     | 
    
         
             
            end
         
     | 
| 
       126 
152 
     | 
    
         
             
            ```
         
     | 
| 
       127 
153 
     | 
    
         | 
| 
       128 
     | 
    
         
            -
             
     | 
| 
      
 154 
     | 
    
         
            +
            **Symbol-based guards** call a method on the object:
         
     | 
| 
      
 155 
     | 
    
         
            +
             
     | 
| 
      
 156 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 157 
     | 
    
         
            +
            class Document
         
     | 
| 
      
 158 
     | 
    
         
            +
              extend Circulator
         
     | 
| 
      
 159 
     | 
    
         
            +
             
     | 
| 
      
 160 
     | 
    
         
            +
              attr_accessor :state, :reviewed_by
         
     | 
| 
      
 161 
     | 
    
         
            +
             
     | 
| 
      
 162 
     | 
    
         
            +
              circulator :state do
         
     | 
| 
      
 163 
     | 
    
         
            +
                state :draft do
         
     | 
| 
      
 164 
     | 
    
         
            +
                  action :publish, to: :published, allow_if: :ready_to_publish?
         
     | 
| 
      
 165 
     | 
    
         
            +
                end
         
     | 
| 
      
 166 
     | 
    
         
            +
              end
         
     | 
| 
      
 167 
     | 
    
         
            +
             
     | 
| 
      
 168 
     | 
    
         
            +
              def ready_to_publish?
         
     | 
| 
      
 169 
     | 
    
         
            +
                reviewed_by.present?
         
     | 
| 
      
 170 
     | 
    
         
            +
              end
         
     | 
| 
      
 171 
     | 
    
         
            +
            end
         
     | 
| 
      
 172 
     | 
    
         
            +
            ```
         
     | 
| 
      
 173 
     | 
    
         
            +
             
     | 
| 
      
 174 
     | 
    
         
            +
            This is equivalent to the proc-based approach but cleaner when you have a dedicated method for the condition.
         
     | 
| 
      
 175 
     | 
    
         
            +
             
     | 
| 
      
 176 
     | 
    
         
            +
            **Hash-based guards** check the state of another attribute:
         
     | 
| 
       129 
177 
     | 
    
         | 
| 
       130 
178 
     | 
    
         
             
            You can make one state machine depend on another using hash-based `allow_if`:
         
     | 
| 
       131 
179 
     | 
    
         | 
    
        data/lib/circulator/dot.rb
    CHANGED
    
    | 
         @@ -6,6 +6,14 @@ module Circulator 
     | 
|
| 
       6 
6 
     | 
    
         
             
              class Dot < Diagram
         
     | 
| 
       7 
7 
     | 
    
         
             
                private
         
     | 
| 
       8 
8 
     | 
    
         | 
| 
      
 9 
     | 
    
         
            +
                def quote_node_name(name)
         
     | 
| 
      
 10 
     | 
    
         
            +
                  if name.end_with?("?")
         
     | 
| 
      
 11 
     | 
    
         
            +
                    "\"#{name}\""
         
     | 
| 
      
 12 
     | 
    
         
            +
                  else
         
     | 
| 
      
 13 
     | 
    
         
            +
                    name
         
     | 
| 
      
 14 
     | 
    
         
            +
                  end
         
     | 
| 
      
 15 
     | 
    
         
            +
                end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
       9 
17 
     | 
    
         
             
                def flows_output(flows_data, output)
         
     | 
| 
       10 
18 
     | 
    
         
             
                  if flows_data.size == 1
         
     | 
| 
       11 
19 
     | 
    
         
             
                    # Single flow: no grouping needed
         
     | 
| 
         @@ -27,7 +35,8 @@ module Circulator 
     | 
|
| 
       27 
35 
     | 
    
         
             
                        state_label = state.nil? ? "nil" : state.to_s
         
     | 
| 
       28 
36 
     | 
    
         
             
                        # Prefix state names with attribute to avoid conflicts
         
     | 
| 
       29 
37 
     | 
    
         
             
                        prefixed_name = "#{flow[:attribute_name]}_#{state_label}"
         
     | 
| 
       30 
     | 
    
         
            -
                         
     | 
| 
      
 38 
     | 
    
         
            +
                        quoted_name = quote_node_name(prefixed_name)
         
     | 
| 
      
 39 
     | 
    
         
            +
                        output << "    #{quoted_name} [label=\"#{state_label}\", shape=circle];"
         
     | 
| 
       31 
40 
     | 
    
         
             
                      end
         
     | 
| 
       32 
41 
     | 
    
         | 
| 
       33 
42 
     | 
    
         
             
                      output << "  }"
         
     | 
| 
         @@ -43,7 +52,9 @@ module Circulator 
     | 
|
| 
       43 
52 
     | 
    
         
             
                        # Use prefixed names
         
     | 
| 
       44 
53 
     | 
    
         
             
                        prefixed_from = "#{flow[:attribute_name]}_#{from_label}"
         
     | 
| 
       45 
54 
     | 
    
         
             
                        prefixed_to = "#{flow[:attribute_name]}_#{to_label}"
         
     | 
| 
       46 
     | 
    
         
            -
                         
     | 
| 
      
 55 
     | 
    
         
            +
                        quoted_from = quote_node_name(prefixed_from)
         
     | 
| 
      
 56 
     | 
    
         
            +
                        quoted_to = quote_node_name(prefixed_to)
         
     | 
| 
      
 57 
     | 
    
         
            +
                        output << "  #{quoted_from} -> #{quoted_to} [label=\"#{transition[:label]}\"];"
         
     | 
| 
       47 
58 
     | 
    
         
             
                      end
         
     | 
| 
       48 
59 
     | 
    
         
             
                    end
         
     | 
| 
       49 
60 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -67,7 +78,8 @@ module Circulator 
     | 
|
| 
       67 
78 
     | 
    
         
             
                  output << "  // States"
         
     | 
| 
       68 
79 
     | 
    
         
             
                  states.sort_by { |s| s.to_s }.each do |state|
         
     | 
| 
       69 
80 
     | 
    
         
             
                    state_label = state.nil? ? "nil" : state.to_s
         
     | 
| 
       70 
     | 
    
         
            -
                     
     | 
| 
      
 81 
     | 
    
         
            +
                    quoted_name = quote_node_name(state_label)
         
     | 
| 
      
 82 
     | 
    
         
            +
                    output << "  #{quoted_name} [shape=circle];"
         
     | 
| 
       71 
83 
     | 
    
         
             
                  end
         
     | 
| 
       72 
84 
     | 
    
         
             
                end
         
     | 
| 
       73 
85 
     | 
    
         | 
| 
         @@ -77,7 +89,9 @@ module Circulator 
     | 
|
| 
       77 
89 
     | 
    
         
             
                  transitions.sort_by { |t| [t[:from].to_s, t[:to].to_s, t[:label]] }.each do |transition|
         
     | 
| 
       78 
90 
     | 
    
         
             
                    from_label = transition[:from].nil? ? "nil" : transition[:from].to_s
         
     | 
| 
       79 
91 
     | 
    
         
             
                    to_label = transition[:to].nil? ? "nil" : transition[:to].to_s
         
     | 
| 
       80 
     | 
    
         
            -
                     
     | 
| 
      
 92 
     | 
    
         
            +
                    quoted_from = quote_node_name(from_label)
         
     | 
| 
      
 93 
     | 
    
         
            +
                    quoted_to = quote_node_name(to_label)
         
     | 
| 
      
 94 
     | 
    
         
            +
                    output << "  #{quoted_from} -> #{quoted_to} [label=\"#{transition[:label]}\"];"
         
     | 
| 
       81 
95 
     | 
    
         
             
                  end
         
     | 
| 
       82 
96 
     | 
    
         
             
                end
         
     | 
| 
       83 
97 
     | 
    
         | 
    
        data/lib/circulator/flow.rb
    CHANGED
    
    | 
         @@ -83,14 +83,21 @@ module Circulator 
     | 
|
| 
       83 
83 
     | 
    
         
             
                private
         
     | 
| 
       84 
84 
     | 
    
         | 
| 
       85 
85 
     | 
    
         
             
                def validate_allow_if(allow_if)
         
     | 
| 
       86 
     | 
    
         
            -
                   
     | 
| 
       87 
     | 
    
         
            -
                   
     | 
| 
       88 
     | 
    
         
            -
                     
     | 
| 
      
 86 
     | 
    
         
            +
                  case allow_if
         
     | 
| 
      
 87 
     | 
    
         
            +
                  in Proc
         
     | 
| 
      
 88 
     | 
    
         
            +
                    # Valid, no additional validation needed
         
     | 
| 
      
 89 
     | 
    
         
            +
                  in Symbol
         
     | 
| 
      
 90 
     | 
    
         
            +
                    validate_symbol_allow_if(allow_if)
         
     | 
| 
      
 91 
     | 
    
         
            +
                  in Hash
         
     | 
| 
      
 92 
     | 
    
         
            +
                    validate_hash_allow_if(allow_if)
         
     | 
| 
      
 93 
     | 
    
         
            +
                  else
         
     | 
| 
      
 94 
     | 
    
         
            +
                    raise ArgumentError, "allow_if must be a Proc, Hash, or Symbol, got: #{allow_if.class}"
         
     | 
| 
       89 
95 
     | 
    
         
             
                  end
         
     | 
| 
      
 96 
     | 
    
         
            +
                end
         
     | 
| 
       90 
97 
     | 
    
         | 
| 
       91 
     | 
    
         
            -
             
     | 
| 
       92 
     | 
    
         
            -
                   
     | 
| 
       93 
     | 
    
         
            -
                     
     | 
| 
      
 98 
     | 
    
         
            +
                def validate_symbol_allow_if(method_name)
         
     | 
| 
      
 99 
     | 
    
         
            +
                  unless @klass.method_defined?(method_name)
         
     | 
| 
      
 100 
     | 
    
         
            +
                    raise ArgumentError, "allow_if references undefined method :#{method_name} on #{@klass}"
         
     | 
| 
       94 
101 
     | 
    
         
             
                  end
         
     | 
| 
       95 
102 
     | 
    
         
             
                end
         
     | 
| 
       96 
103 
     | 
    
         | 
    
        data/lib/circulator/version.rb
    CHANGED
    
    
    
        data/lib/circulator.rb
    CHANGED
    
    | 
         @@ -209,6 +209,9 @@ module Circulator 
     | 
|
| 
       209 
209 
     | 
    
         | 
| 
       210 
210 
     | 
    
         
             
                      # Return early if current state is not in the valid states
         
     | 
| 
       211 
211 
     | 
    
         
             
                      return unless valid_states_array.include?(current_state)
         
     | 
| 
      
 212 
     | 
    
         
            +
                    elsif transition[:allow_if].is_a?(Symbol)
         
     | 
| 
      
 213 
     | 
    
         
            +
                      # Handle symbol-based allow_if (method name)
         
     | 
| 
      
 214 
     | 
    
         
            +
                      return unless flow_target.send(transition[:allow_if])
         
     | 
| 
       212 
215 
     | 
    
         
             
                    else
         
     | 
| 
       213 
216 
     | 
    
         
             
                      # Handle proc-based allow_if (original behavior)
         
     | 
| 
       214 
217 
     | 
    
         
             
                      return unless flow_target.instance_exec(*args, **kwargs, &transition[:allow_if])
         
     | 
| 
         @@ -274,10 +277,62 @@ module Circulator 
     | 
|
| 
       274 
277 
     | 
    
         
             
                  end
         
     | 
| 
       275 
278 
     | 
    
         
             
                end
         
     | 
| 
       276 
279 
     | 
    
         | 
| 
      
 280 
     | 
    
         
            +
                # Get available actions for an attribute based on current state
         
     | 
| 
      
 281 
     | 
    
         
            +
                #
         
     | 
| 
      
 282 
     | 
    
         
            +
                # Example:
         
     | 
| 
      
 283 
     | 
    
         
            +
                #
         
     | 
| 
      
 284 
     | 
    
         
            +
                #   test_object.available_flows(:status)
         
     | 
| 
      
 285 
     | 
    
         
            +
                #   # => [:approve, :reject]
         
     | 
| 
      
 286 
     | 
    
         
            +
                def available_flows(attribute, *args, **kwargs)
         
     | 
| 
      
 287 
     | 
    
         
            +
                  model_key = Circulator.model_key(self)
         
     | 
| 
      
 288 
     | 
    
         
            +
                  flow = flows.dig(model_key, attribute)
         
     | 
| 
      
 289 
     | 
    
         
            +
                  return [] unless flow
         
     | 
| 
      
 290 
     | 
    
         
            +
             
     | 
| 
      
 291 
     | 
    
         
            +
                  current_value = send(attribute)
         
     | 
| 
      
 292 
     | 
    
         
            +
                  current_state = current_value.respond_to?(:to_sym) ? current_value.to_sym : current_value
         
     | 
| 
      
 293 
     | 
    
         
            +
             
     | 
| 
      
 294 
     | 
    
         
            +
                  flow.transition_map.select do |action, transitions|
         
     | 
| 
      
 295 
     | 
    
         
            +
                    transition = transitions[current_state]
         
     | 
| 
      
 296 
     | 
    
         
            +
                    next false unless transition
         
     | 
| 
      
 297 
     | 
    
         
            +
             
     | 
| 
      
 298 
     | 
    
         
            +
                    # Check allow_if condition if present
         
     | 
| 
      
 299 
     | 
    
         
            +
                    if transition[:allow_if]
         
     | 
| 
      
 300 
     | 
    
         
            +
                      check_allow_if(transition[:allow_if], *args, **kwargs)
         
     | 
| 
      
 301 
     | 
    
         
            +
                    else
         
     | 
| 
      
 302 
     | 
    
         
            +
                      true
         
     | 
| 
      
 303 
     | 
    
         
            +
                    end
         
     | 
| 
      
 304 
     | 
    
         
            +
                  end.keys
         
     | 
| 
      
 305 
     | 
    
         
            +
                end
         
     | 
| 
      
 306 
     | 
    
         
            +
             
     | 
| 
      
 307 
     | 
    
         
            +
                # Check if a specific action is available for an attribute
         
     | 
| 
      
 308 
     | 
    
         
            +
                #
         
     | 
| 
      
 309 
     | 
    
         
            +
                # Example:
         
     | 
| 
      
 310 
     | 
    
         
            +
                #
         
     | 
| 
      
 311 
     | 
    
         
            +
                #   test_object.available_flow?(:status, :approve)
         
     | 
| 
      
 312 
     | 
    
         
            +
                #   # => true
         
     | 
| 
      
 313 
     | 
    
         
            +
                def available_flow?(attribute, action, *args, **kwargs)
         
     | 
| 
      
 314 
     | 
    
         
            +
                  available_flows(attribute, *args, **kwargs).include?(action)
         
     | 
| 
      
 315 
     | 
    
         
            +
                end
         
     | 
| 
      
 316 
     | 
    
         
            +
             
     | 
| 
       277 
317 
     | 
    
         
             
                private
         
     | 
| 
       278 
318 
     | 
    
         | 
| 
       279 
319 
     | 
    
         
             
                def flows
         
     | 
| 
       280 
320 
     | 
    
         
             
                  self.class.flows
         
     | 
| 
       281 
321 
     | 
    
         
             
                end
         
     | 
| 
      
 322 
     | 
    
         
            +
             
     | 
| 
      
 323 
     | 
    
         
            +
                def check_allow_if(allow_if, *args, **kwargs)
         
     | 
| 
      
 324 
     | 
    
         
            +
                  case allow_if
         
     | 
| 
      
 325 
     | 
    
         
            +
                  when Hash
         
     | 
| 
      
 326 
     | 
    
         
            +
                    attribute_name, valid_states = allow_if.first
         
     | 
| 
      
 327 
     | 
    
         
            +
                    current_state = send(attribute_name)
         
     | 
| 
      
 328 
     | 
    
         
            +
                    current_state = current_state.to_sym if current_state.respond_to?(:to_sym)
         
     | 
| 
      
 329 
     | 
    
         
            +
                    valid_states_array = Array(valid_states).map { |s| s.respond_to?(:to_sym) ? s.to_sym : s }
         
     | 
| 
      
 330 
     | 
    
         
            +
                    valid_states_array.include?(current_state)
         
     | 
| 
      
 331 
     | 
    
         
            +
                  when Symbol
         
     | 
| 
      
 332 
     | 
    
         
            +
                    send(allow_if, *args, **kwargs)
         
     | 
| 
      
 333 
     | 
    
         
            +
                  else # Proc
         
     | 
| 
      
 334 
     | 
    
         
            +
                    instance_exec(*args, **kwargs, &allow_if)
         
     | 
| 
      
 335 
     | 
    
         
            +
                  end
         
     | 
| 
      
 336 
     | 
    
         
            +
                end
         
     | 
| 
       282 
337 
     | 
    
         
             
              end
         
     | 
| 
       283 
338 
     | 
    
         
             
            end
         
     |