convoy 1.0.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.
Files changed (109) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +20 -0
  3. data/.irbrc +3 -0
  4. data/.rspec +3 -0
  5. data/.ruby-version +1 -0
  6. data/.travis.yml +8 -0
  7. data/Gemfile +4 -0
  8. data/LICENSE +22 -0
  9. data/README.md +705 -0
  10. data/Rakefile +1 -0
  11. data/convoy.gemspec +24 -0
  12. data/examples/.my_apprc +24 -0
  13. data/examples/basic +10 -0
  14. data/examples/basic_config_file +16 -0
  15. data/examples/basic_conflicts +17 -0
  16. data/examples/basic_depends_on +25 -0
  17. data/examples/basic_flags +15 -0
  18. data/examples/basic_options +14 -0
  19. data/examples/basic_options_multi +15 -0
  20. data/examples/basic_require_arguments +17 -0
  21. data/examples/basic_texts +21 -0
  22. data/examples/basic_validations +21 -0
  23. data/examples/basic_with_everything +30 -0
  24. data/examples/commands/example_command.rb +13 -0
  25. data/examples/suite_complex +65 -0
  26. data/examples/suite_simple +19 -0
  27. data/examples/suite_with_sub_commands +94 -0
  28. data/lib/convoy.rb +83 -0
  29. data/lib/convoy/action_command/base.rb +85 -0
  30. data/lib/convoy/action_command/escort_utility_command.rb +53 -0
  31. data/lib/convoy/app.rb +127 -0
  32. data/lib/convoy/arguments.rb +20 -0
  33. data/lib/convoy/auto_options.rb +71 -0
  34. data/lib/convoy/error/error.rb +33 -0
  35. data/lib/convoy/formatter/command.rb +87 -0
  36. data/lib/convoy/formatter/commands.rb +37 -0
  37. data/lib/convoy/formatter/cursor_position.rb +29 -0
  38. data/lib/convoy/formatter/default_help_formatter.rb +117 -0
  39. data/lib/convoy/formatter/global_command.rb +17 -0
  40. data/lib/convoy/formatter/option.rb +152 -0
  41. data/lib/convoy/formatter/options.rb +28 -0
  42. data/lib/convoy/formatter/shell_command_executor.rb +49 -0
  43. data/lib/convoy/formatter/stream_output_formatter.rb +88 -0
  44. data/lib/convoy/formatter/string_grid.rb +108 -0
  45. data/lib/convoy/formatter/string_splitter.rb +50 -0
  46. data/lib/convoy/formatter/terminal.rb +30 -0
  47. data/lib/convoy/global_pre_parser.rb +43 -0
  48. data/lib/convoy/logger.rb +75 -0
  49. data/lib/convoy/option_dependency_validator.rb +82 -0
  50. data/lib/convoy/option_parser.rb +155 -0
  51. data/lib/convoy/setup/configuration/generator.rb +75 -0
  52. data/lib/convoy/setup/configuration/instance.rb +34 -0
  53. data/lib/convoy/setup/configuration/loader.rb +43 -0
  54. data/lib/convoy/setup/configuration/locator/base.rb +19 -0
  55. data/lib/convoy/setup/configuration/locator/chaining.rb +29 -0
  56. data/lib/convoy/setup/configuration/locator/descending_to_home.rb +23 -0
  57. data/lib/convoy/setup/configuration/locator/executing_script_directory.rb +15 -0
  58. data/lib/convoy/setup/configuration/locator/specified_directory.rb +21 -0
  59. data/lib/convoy/setup/configuration/merge_tool.rb +38 -0
  60. data/lib/convoy/setup/configuration/reader.rb +36 -0
  61. data/lib/convoy/setup/configuration/writer.rb +46 -0
  62. data/lib/convoy/setup/dsl/action.rb +17 -0
  63. data/lib/convoy/setup/dsl/command.rb +67 -0
  64. data/lib/convoy/setup/dsl/config_file.rb +13 -0
  65. data/lib/convoy/setup/dsl/global.rb +29 -0
  66. data/lib/convoy/setup/dsl/options.rb +81 -0
  67. data/lib/convoy/setup_accessor.rb +206 -0
  68. data/lib/convoy/trollop.rb +861 -0
  69. data/lib/convoy/utils.rb +21 -0
  70. data/lib/convoy/validator.rb +45 -0
  71. data/spec/integration/basic_config_file_spec.rb +126 -0
  72. data/spec/integration/basic_conflicts_spec.rb +47 -0
  73. data/spec/integration/basic_depends_on_spec.rb +275 -0
  74. data/spec/integration/basic_options_spec.rb +41 -0
  75. data/spec/integration/basic_options_with_multi_spec.rb +30 -0
  76. data/spec/integration/basic_spec.rb +38 -0
  77. data/spec/integration/basic_validations_spec.rb +77 -0
  78. data/spec/integration/basic_with_arguments_spec.rb +35 -0
  79. data/spec/integration/basic_with_text_fields_spec.rb +21 -0
  80. data/spec/integration/suite_simple_spec.rb +45 -0
  81. data/spec/integration/suite_sub_command_spec.rb +51 -0
  82. data/spec/lib/convoy/action_command/base_spec.rb +200 -0
  83. data/spec/lib/convoy/formatter/command_spec.rb +238 -0
  84. data/spec/lib/convoy/formatter/global_command_spec.rb +50 -0
  85. data/spec/lib/convoy/formatter/option_spec.rb +300 -0
  86. data/spec/lib/convoy/formatter/shell_command_executor_spec.rb +59 -0
  87. data/spec/lib/convoy/formatter/stream_output_formatter_spec.rb +214 -0
  88. data/spec/lib/convoy/formatter/string_grid_spec.rb +59 -0
  89. data/spec/lib/convoy/formatter/string_splitter_spec.rb +50 -0
  90. data/spec/lib/convoy/formatter/terminal_spec.rb +19 -0
  91. data/spec/lib/convoy/setup/configuration/generator_spec.rb +101 -0
  92. data/spec/lib/convoy/setup/configuration/loader_spec.rb +79 -0
  93. data/spec/lib/convoy/setup/configuration/locator/chaining_spec.rb +81 -0
  94. data/spec/lib/convoy/setup/configuration/locator/descending_to_home_spec.rb +57 -0
  95. data/spec/lib/convoy/setup/configuration/locator/executing_script_directory_spec.rb +29 -0
  96. data/spec/lib/convoy/setup/configuration/locator/specified_directory_spec.rb +33 -0
  97. data/spec/lib/convoy/setup/configuration/merge_tool_spec.rb +41 -0
  98. data/spec/lib/convoy/setup/configuration/reader_spec.rb +41 -0
  99. data/spec/lib/convoy/setup/configuration/writer_spec.rb +75 -0
  100. data/spec/lib/convoy/setup_accessor_spec.rb +226 -0
  101. data/spec/lib/convoy/utils_spec.rb +30 -0
  102. data/spec/spec_helper.rb +29 -0
  103. data/spec/support/integration_helpers.rb +2 -0
  104. data/spec/support/matchers/execute_action_for_command_matcher.rb +21 -0
  105. data/spec/support/matchers/execute_action_with_arguments_matcher.rb +25 -0
  106. data/spec/support/matchers/execute_action_with_options_matcher.rb +29 -0
  107. data/spec/support/matchers/exit_with_code_matcher.rb +29 -0
  108. data/spec/support/shared_contexts/integration_setup.rb +34 -0
  109. metadata +292 -0
@@ -0,0 +1,15 @@
1
+ module Convoy
2
+ module Setup
3
+ module Configuration
4
+ module Locator
5
+ class ExecutingScriptDirectory < Base
6
+ def locate
7
+ location_directory = File.dirname($0)
8
+ filepath = File.expand_path(File.join(location_directory, filename))
9
+ File.exists?(filepath) ? filepath : nil
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,21 @@
1
+ module Convoy
2
+ module Setup
3
+ module Configuration
4
+ module Locator
5
+ class SpecifiedDirectory < Base
6
+ attr_reader :location_directory
7
+
8
+ def initialize(filename, location_directory)
9
+ super(filename)
10
+ @location_directory = location_directory
11
+ end
12
+
13
+ def locate
14
+ filepath = File.expand_path(File.join(location_directory, filename))
15
+ File.exists?(filepath) ? filepath : nil
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,38 @@
1
+ module Convoy
2
+ module Setup
3
+ module Configuration
4
+ class MergeTool
5
+ def initialize(new_config_hash, old_config_hash)
6
+ @new_config_hash = new_config_hash
7
+ @old_config_hash = old_config_hash
8
+ end
9
+
10
+ def config_hash
11
+ merge_config(@new_config_hash, @old_config_hash)
12
+ end
13
+
14
+ private
15
+
16
+ def merge_config(new_hash, old_hash)
17
+ new_hash.keys.each do |key|
18
+ new_config_value = new_hash[key]
19
+ old_config_value = old_hash[key]
20
+
21
+ if new_config_value.kind_of?(Hash) && old_config_value.kind_of?(Hash)
22
+ new_hash[key] = merge_config(new_config_value, old_config_value)
23
+ elsif old_config_value.nil?
24
+ new_hash[key] = new_config_value
25
+ else
26
+ new_hash[key] = old_config_value
27
+ end
28
+
29
+ if key == :user
30
+ new_hash[key] = old_config_value
31
+ end
32
+ end
33
+ new_hash
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,36 @@
1
+ require 'json'
2
+
3
+ module Convoy
4
+ module Setup
5
+ module Configuration
6
+ class Reader
7
+ attr_reader :path
8
+
9
+ def initialize(path)
10
+ @path = path
11
+ end
12
+
13
+ def read
14
+ data = {}
15
+ data = load_config_at_path if path
16
+ Instance.new(path, data)
17
+ end
18
+
19
+ private
20
+
21
+ def load_config_at_path
22
+ data = {}
23
+ begin
24
+ json = File.read(path)
25
+ hash = ::JSON.parse(json)
26
+ data = Convoy::Utils.symbolize_keys(hash)
27
+ rescue => e
28
+ error_logger.warn { "Found config at #{path}, but failed to load it, perhaps your JSON syntax is invalid. Attempting to continue without..." }
29
+ error_logger.debug(e)
30
+ end
31
+ data
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,46 @@
1
+ require 'json'
2
+ require 'fileutils'
3
+
4
+ module Convoy
5
+ module Setup
6
+ module Configuration
7
+ class Writer
8
+ attr_reader :path, :data
9
+
10
+ def initialize(path, data)
11
+ @path = path
12
+ @data = data
13
+ end
14
+
15
+ def write
16
+ if path && !File.exists?(path)
17
+ save_to_file
18
+ Instance.new(path, data)
19
+ else
20
+ Instance.blank
21
+ end
22
+ end
23
+
24
+ def update
25
+ current_data = {}
26
+ if File.exists? path
27
+ current_data = Reader.new(path).read.data
28
+ end
29
+ @data = Convoy::Setup::Configuration::MergeTool.new(data, current_data).config_hash
30
+ save_to_file
31
+ Instance.new(path, data)
32
+ end
33
+
34
+ private
35
+
36
+ def save_to_file
37
+ current_path = File.expand_path(path)
38
+ FileUtils.mkdir_p(File.dirname(current_path))
39
+ File.open(current_path, "w") do |f|
40
+ f.puts ::JSON.pretty_generate(data)
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,17 @@
1
+ module Convoy
2
+ module Setup
3
+ module Dsl
4
+ class Action
5
+ class << self
6
+ def action(command_name, instance, &block)
7
+ instance.instance_variable_set(:"@block", block)
8
+ end
9
+ end
10
+
11
+ def initialize(command_name = :global)
12
+ @command_name = command_name
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,67 @@
1
+ module Convoy
2
+ module Setup
3
+ module Dsl
4
+ class Command
5
+ def initialize(name, options = {}, &block)
6
+ reset(name)
7
+ @description = options[:description] || options[:desc] || ""
8
+ @aliases = [options[:aliases] || []].flatten
9
+ @requires_arguments ||= options[:requires_arguments]
10
+ block.call(self) if block_given?
11
+ rescue => e
12
+ raise Convoy::ClientError.new("Problem with syntax of command '#{@name}' configuration", e)
13
+ end
14
+
15
+ def options(&block)
16
+ Options.options(@name, @options, &block)
17
+ end
18
+
19
+ def action(&block)
20
+ Action.action(@name, @action, &block)
21
+ end
22
+
23
+ def command(name, options = {}, &block)
24
+ options[:requires_arguments] = @requires_arguments
25
+ command = Command.new(name.to_sym, options, &block)
26
+ aliases = [options[:aliases] || []].flatten + [name]
27
+ aliases.each do |name|
28
+ @commands[name.to_sym] = command
29
+ end
30
+ end
31
+
32
+ def requires_arguments(boolean = true)
33
+ raise Convoy::ClientError.new("Parameter to 'requires_arguments' must be a boolean") unless [true, false].include?(boolean)
34
+ @requires_arguments = boolean
35
+ @commands.each do |command|
36
+ command.requires_arguments(boolean)
37
+ end
38
+ end
39
+
40
+ def summary(summary)
41
+ @summary = summary
42
+ end
43
+
44
+ def description(description)
45
+ @description = description
46
+ end
47
+
48
+ private
49
+
50
+ def reset(name)
51
+ @name = name
52
+ @summary = nil
53
+ @description = nil
54
+ @requires_arguments = false
55
+ @commands = {}
56
+ @options = Options.new(name)
57
+ @action = Action.new(name)
58
+ custom_reset
59
+ end
60
+
61
+ def custom_reset
62
+ @aliases = []
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,13 @@
1
+ module Convoy
2
+ module Setup
3
+ module Dsl
4
+ class ConfigFile
5
+ def initialize(name, options = {})
6
+ @name = name
7
+ #TODO possibly raise error if autocreate is not a boolean
8
+ @autocreate = options[:autocreate] || false
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,29 @@
1
+ module Convoy
2
+ module Setup
3
+ module Dsl
4
+ class Global < Command
5
+ def initialize(&block)
6
+ reset(:global)
7
+ block.call(self)
8
+ rescue => e
9
+ raise Convoy::ClientError.new("Problem with syntax of global configuration", e)
10
+ end
11
+
12
+ def config_file(name, options = {})
13
+ @config_file = ConfigFile.new(name, options)
14
+ end
15
+
16
+ def version(version)
17
+ @version = version
18
+ end
19
+
20
+ private
21
+
22
+ def custom_reset
23
+ @version = nil
24
+ @config_file = nil
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,81 @@
1
+ module Convoy
2
+ module Setup
3
+ module Dsl
4
+ class Options
5
+ class << self
6
+ def options(command_name, instance, &block)
7
+ block.call(instance) if block_given?
8
+ rescue => e
9
+ raise Convoy::ClientError.new("Problem with syntax of #{instance.instance_variable_get(:"@command_name")} options block", e)
10
+ end
11
+ end
12
+
13
+ def initialize(command_name = :global)
14
+ @command_name = command_name
15
+ @options = {}
16
+ @dependencies = {}
17
+ @conflicts = {}
18
+ @validations = {}
19
+ end
20
+
21
+ def opt(name, desc="", opts={})
22
+ opts[:desc] ||= desc
23
+ @options[name] ||= opts
24
+ dependency(name, :on => opts[:depends_on]) if opts[:depends_on]
25
+ conflict(*[name, opts[:conflicts_with]].flatten) if opts[:conflicts_with]
26
+ end
27
+
28
+ def validate(name, description, &block)
29
+ @validations[name] ||= []
30
+ @validations[name] << {:desc => description, :block => block}
31
+ end
32
+
33
+ def dependency(option_name, opts = {})
34
+ ensure_dependency_specification_syntax(opts)
35
+ @dependencies[option_name] ||= []
36
+ rules_as_array(opts).each do |rule|
37
+ ensure_no_self_dependency(option_name, rule)
38
+ @dependencies[option_name] << rule
39
+ end
40
+ end
41
+
42
+ def conflict(*opts)
43
+ opts.each do |opt|
44
+ conflicts_for_opt = opts.reject { |value| value == opt }
45
+ @conflicts[opt] ||= []
46
+ @conflicts[opt] += conflicts_for_opt
47
+ @conflicts[opt].uniq!
48
+ end
49
+ end
50
+
51
+ private
52
+ def ensure_no_self_dependency(option_name, rule)
53
+ case rule
54
+ when Hash
55
+ rule.each_pair do |rule_option, rule_option_value|
56
+ handle_possible_self_dependency(option_name, rule_option)
57
+ end
58
+ else
59
+ handle_possible_self_dependency(option_name, rule)
60
+ end
61
+ end
62
+
63
+ def ensure_dependency_specification_syntax(opts)
64
+ unless opts[:on]
65
+ raise Convoy::ClientError.new("Problem with syntax of dependency specification in #{@command_name} options block, #{option_name} missing ':on' condition")
66
+ end
67
+ end
68
+
69
+ def rules_as_array(opts)
70
+ [opts[:on]].flatten
71
+ end
72
+
73
+ def handle_possible_self_dependency(option_name, rule)
74
+ if option_name == rule
75
+ raise Convoy::ClientError.new("Problem with syntax of dependency specification in #{@command_name} options block, #{option_name} is set to depend on itself")
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,206 @@
1
+ module Convoy
2
+ class SetupAccessor
3
+ attr_reader :global_instance
4
+
5
+ def initialize(global_instance)
6
+ @global_instance = global_instance
7
+ end
8
+
9
+ def options_for(context = [])
10
+ with_context(context) do |current_context|
11
+ options_hash_from(current_context)
12
+ end
13
+ end
14
+
15
+ def conflicting_options_for(context = [])
16
+ with_context(context) do |current_context|
17
+ conflicts_hash_for(current_context)
18
+ end
19
+ end
20
+
21
+ def validations_for(context = [])
22
+ with_context(context) do |current_context|
23
+ validations_hash_from(current_context)
24
+ end
25
+ end
26
+
27
+ def dependencies_for(context = [])
28
+ with_context(context) do |current_context|
29
+ dependencies_hash_from(current_context)
30
+ end
31
+ end
32
+
33
+ def command_names_for(context = [])
34
+ with_context(context) do |current_context|
35
+ command_names_from(current_context)
36
+ end
37
+ end
38
+
39
+ def canonical_command_names_for(context = [])
40
+ with_context(context) do |current_context|
41
+ canonical_command_names_from(current_context)
42
+ end
43
+ end
44
+
45
+ def action_for(context = [])
46
+ with_context(context) do |current_context|
47
+ action_block_from(current_context)
48
+ end
49
+ end
50
+
51
+ def arguments_required_for(context = [])
52
+ with_context(context) do |current_context|
53
+ context_requires_arguments(current_context)
54
+ end
55
+ end
56
+
57
+ def has_config_file?
58
+ config_file_object != nil
59
+ end
60
+
61
+ def config_file_autocreatable?
62
+ autocreatable = fetch_instance_variable_from(config_file_object, :autocreate)
63
+ end
64
+
65
+ def config_file
66
+ name = fetch_instance_variable_from(config_file_object, :name)
67
+ end
68
+
69
+ def version
70
+ version_string = fetch_instance_variable_from(global_instance, :version)
71
+ end
72
+
73
+ def summary_for(context = [])
74
+ with_context(context) do |current_context|
75
+ fetch_instance_variable_from(current_context, :summary)
76
+ end
77
+ end
78
+
79
+ def description_for(context = [])
80
+ with_context(context) do |current_context|
81
+ fetch_instance_variable_from(current_context, :description)
82
+ end
83
+ end
84
+
85
+ def command_description_for(command_name, context = [])
86
+ with_context(context) do |current_context|
87
+ commands = fetch_instance_variable_from(current_context, :commands)
88
+ fetch_instance_variable_from(commands[command_name], :description)
89
+ end
90
+ end
91
+
92
+ def command_summary_for(command_name, context = [])
93
+ with_context(context) do |current_context|
94
+ commands = fetch_instance_variable_from(current_context, :commands)
95
+ fetch_instance_variable_from(commands[command_name], :summary)
96
+ end
97
+ end
98
+
99
+ def command_aliases_for(command_name, context = [])
100
+ with_context(context) do |current_context|
101
+ commands = fetch_instance_variable_from(current_context, :commands)
102
+ description = fetch_instance_variable_from(commands[command_name], :aliases)
103
+ end
104
+ end
105
+
106
+ def add_global_option(name, desc, options = {})
107
+ with_context([]) do |current_context|
108
+ options_object = options_object_from(current_context)
109
+ options_object.opt name, desc, options
110
+ end
111
+ end
112
+
113
+ def add_global_command(name, options = {}, &block)
114
+ global_instance.command(name, options, &block)
115
+ end
116
+
117
+ private
118
+
119
+ def config_file_object
120
+ config_file = fetch_instance_variable_from(global_instance, :config_file)
121
+ end
122
+
123
+ def with_context(context = [], &block)
124
+ context = [] if context.nil? || context.empty?
125
+ context = [context] unless context.kind_of?(Array)
126
+ current_context = global_instance
127
+ context.each do |command_name|
128
+ commands = fetch_instance_variable_from(current_context, :commands)
129
+ current_context = commands[command_name.to_sym]
130
+ end
131
+ block.call(current_context)
132
+ end
133
+
134
+ def context_requires_arguments(context_object)
135
+ requires_arguments = fetch_instance_variable_from(context_object, :requires_arguments)
136
+ end
137
+
138
+ def action_block_from(context_object)
139
+ action_object = fetch_instance_variable_from(context_object, :action)
140
+ block = fetch_instance_variable_from(action_object, :block)
141
+ #TODO make sure that if there is no block we exit with a client error
142
+ end
143
+
144
+ def command_names_from(context_object)
145
+ commands = fetch_instance_variable_from(context_object, :commands)
146
+ commands.keys
147
+ #TODO make sure there can be no errors here and at worst it is an empty array
148
+ end
149
+
150
+ def canonical_command_names_from(context_object)
151
+ commands = fetch_instance_variable_from(context_object, :commands)
152
+ commands.select do |key, command|
153
+ aliases = fetch_instance_variable_from(command, :aliases)
154
+ !aliases.include?(key)
155
+ end.keys
156
+ #TODO make sure there can be no errors here and at worst it is an empty array
157
+ end
158
+
159
+ def options_hash_from(context_object)
160
+ ensure_context_object(context_object, {}) do
161
+ options_object = options_object_from(context_object)
162
+ fetch_instance_variable_from(options_object, :options)
163
+ end
164
+ end
165
+
166
+ def options_object_from(context_object)
167
+ ensure_context_object(context_object, nil) do
168
+ fetch_instance_variable_from(context_object, :options)
169
+ end
170
+ end
171
+
172
+ def conflicts_hash_for(context_object)
173
+ ensure_context_object(context_object, {}) do
174
+ options_object = options_object_from(context_object)
175
+ fetch_instance_variable_from(options_object, :conflicts)
176
+ end
177
+ end
178
+
179
+ def validations_hash_from(context_object)
180
+ ensure_context_object(context_object, {}) do
181
+ validations_object = fetch_instance_variable_from(context_object, :options)
182
+ fetch_instance_variable_from(validations_object, :validations)
183
+ end
184
+ end
185
+
186
+ def dependencies_hash_from(context_object)
187
+ ensure_context_object(context_object, {}) do
188
+ options_object = options_object_from(context_object)
189
+ fetch_instance_variable_from(options_object, :dependencies)
190
+ end
191
+ end
192
+
193
+ def fetch_instance_variable_from_setup(instance_variable)
194
+ fetch_instance_variable_from(global_instance, instance_variable)
195
+ end
196
+
197
+ def fetch_instance_variable_from(instance, instance_variable)
198
+ instance_variable_symbol = :"@#{instance_variable.to_s}"
199
+ instance.instance_variable_get(instance_variable_symbol)
200
+ end
201
+
202
+ def ensure_context_object(context_object, default_value, &block)
203
+ context_object ? block.call : default_value
204
+ end
205
+ end
206
+ end