bahuvrihi-tap 0.10.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History +69 -0
- data/MIT-LICENSE +21 -0
- data/README +119 -0
- data/bin/tap +114 -0
- data/cmd/console.rb +42 -0
- data/cmd/destroy.rb +16 -0
- data/cmd/generate.rb +16 -0
- data/cmd/run.rb +126 -0
- data/doc/Class Reference +362 -0
- data/doc/Command Reference +153 -0
- data/doc/Tutorial +237 -0
- data/lib/tap.rb +32 -0
- data/lib/tap/app.rb +720 -0
- data/lib/tap/constants.rb +8 -0
- data/lib/tap/env.rb +640 -0
- data/lib/tap/file_task.rb +547 -0
- data/lib/tap/generator/base.rb +109 -0
- data/lib/tap/generator/destroy.rb +37 -0
- data/lib/tap/generator/generate.rb +61 -0
- data/lib/tap/generator/generators/command/command_generator.rb +21 -0
- data/lib/tap/generator/generators/command/templates/command.erb +32 -0
- data/lib/tap/generator/generators/config/config_generator.rb +26 -0
- data/lib/tap/generator/generators/config/templates/doc.erb +12 -0
- data/lib/tap/generator/generators/config/templates/nodoc.erb +8 -0
- data/lib/tap/generator/generators/file_task/file_task_generator.rb +27 -0
- data/lib/tap/generator/generators/file_task/templates/file.txt +11 -0
- data/lib/tap/generator/generators/file_task/templates/result.yml +6 -0
- data/lib/tap/generator/generators/file_task/templates/task.erb +33 -0
- data/lib/tap/generator/generators/file_task/templates/test.erb +29 -0
- data/lib/tap/generator/generators/root/root_generator.rb +55 -0
- data/lib/tap/generator/generators/root/templates/Rakefile +86 -0
- data/lib/tap/generator/generators/root/templates/gemspec +27 -0
- data/lib/tap/generator/generators/root/templates/tapfile +8 -0
- data/lib/tap/generator/generators/root/templates/test/tap_test_helper.rb +3 -0
- data/lib/tap/generator/generators/root/templates/test/tap_test_suite.rb +5 -0
- data/lib/tap/generator/generators/root/templates/test/tapfile_test.rb +15 -0
- data/lib/tap/generator/generators/task/task_generator.rb +27 -0
- data/lib/tap/generator/generators/task/templates/task.erb +14 -0
- data/lib/tap/generator/generators/task/templates/test.erb +21 -0
- data/lib/tap/generator/manifest.rb +14 -0
- data/lib/tap/patches/rake/rake_test_loader.rb +8 -0
- data/lib/tap/patches/rake/testtask.rb +55 -0
- data/lib/tap/patches/ruby19/backtrace_filter.rb +51 -0
- data/lib/tap/patches/ruby19/parsedate.rb +16 -0
- data/lib/tap/root.rb +581 -0
- data/lib/tap/support/aggregator.rb +55 -0
- data/lib/tap/support/assignments.rb +172 -0
- data/lib/tap/support/audit.rb +418 -0
- data/lib/tap/support/batchable.rb +47 -0
- data/lib/tap/support/batchable_class.rb +107 -0
- data/lib/tap/support/class_configuration.rb +194 -0
- data/lib/tap/support/command_line.rb +98 -0
- data/lib/tap/support/comment.rb +270 -0
- data/lib/tap/support/configurable.rb +114 -0
- data/lib/tap/support/configurable_class.rb +296 -0
- data/lib/tap/support/configuration.rb +122 -0
- data/lib/tap/support/constant.rb +70 -0
- data/lib/tap/support/constant_utils.rb +127 -0
- data/lib/tap/support/declarations.rb +111 -0
- data/lib/tap/support/executable.rb +111 -0
- data/lib/tap/support/executable_queue.rb +82 -0
- data/lib/tap/support/framework.rb +71 -0
- data/lib/tap/support/framework_class.rb +199 -0
- data/lib/tap/support/instance_configuration.rb +147 -0
- data/lib/tap/support/lazydoc.rb +428 -0
- data/lib/tap/support/manifest.rb +89 -0
- data/lib/tap/support/run_error.rb +39 -0
- data/lib/tap/support/shell_utils.rb +71 -0
- data/lib/tap/support/summary.rb +30 -0
- data/lib/tap/support/tdoc.rb +404 -0
- data/lib/tap/support/tdoc/tdoc_html_generator.rb +38 -0
- data/lib/tap/support/tdoc/tdoc_html_template.rb +42 -0
- data/lib/tap/support/templater.rb +180 -0
- data/lib/tap/support/validation.rb +410 -0
- data/lib/tap/support/versions.rb +97 -0
- data/lib/tap/task.rb +259 -0
- data/lib/tap/tasks/dump.rb +56 -0
- data/lib/tap/tasks/rake.rb +93 -0
- data/lib/tap/test.rb +37 -0
- data/lib/tap/test/env_vars.rb +29 -0
- data/lib/tap/test/file_methods.rb +377 -0
- data/lib/tap/test/script_methods.rb +144 -0
- data/lib/tap/test/subset_methods.rb +420 -0
- data/lib/tap/test/tap_methods.rb +237 -0
- data/lib/tap/workflow.rb +187 -0
- metadata +145 -0
@@ -0,0 +1,82 @@
|
|
1
|
+
module Tap
|
2
|
+
module Support
|
3
|
+
|
4
|
+
# ExecutableQueue allows thread-safe enqueing and dequeing of
|
5
|
+
# Executable methods and inputs for execution.
|
6
|
+
class ExecutableQueue
|
7
|
+
include MonitorMixin
|
8
|
+
|
9
|
+
# Creates a new ExecutableQueue
|
10
|
+
def initialize
|
11
|
+
# required for MonitorMixin
|
12
|
+
super()
|
13
|
+
@queue = []
|
14
|
+
end
|
15
|
+
|
16
|
+
# Clears all methods and inputs. Returns the existing queue as an array.
|
17
|
+
def clear
|
18
|
+
synchronize do
|
19
|
+
current = self.queue
|
20
|
+
self.queue = []
|
21
|
+
current
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Returns the number of enqueued methods
|
26
|
+
def size
|
27
|
+
queue.length
|
28
|
+
end
|
29
|
+
|
30
|
+
# True if no methods are enqueued
|
31
|
+
def empty?
|
32
|
+
queue.empty?
|
33
|
+
end
|
34
|
+
|
35
|
+
# Enqueues the method and inputs. Raises an error if the
|
36
|
+
# method is not an Executable.
|
37
|
+
def enq(method, inputs)
|
38
|
+
synchronize do
|
39
|
+
check_method(method)
|
40
|
+
queue.push [method, inputs]
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Enqueues the method and inputs, but to the top of the queue.
|
45
|
+
# Raises an error if the method is not an Executable.
|
46
|
+
def unshift(method, inputs)
|
47
|
+
synchronize do
|
48
|
+
check_method(method)
|
49
|
+
queue.unshift [method, inputs]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Dequeues the next method and inputs as an array like
|
54
|
+
# [method, inputs]. Returns nil if the queue is empty.
|
55
|
+
def deq
|
56
|
+
synchronize { queue.shift }
|
57
|
+
end
|
58
|
+
|
59
|
+
def concat(array)
|
60
|
+
synchronize do
|
61
|
+
array.each do |method, inputs|
|
62
|
+
enq(method, inputs)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Converts self to an array.
|
68
|
+
def to_a
|
69
|
+
queue.dup
|
70
|
+
end
|
71
|
+
|
72
|
+
protected
|
73
|
+
|
74
|
+
attr_accessor :queue
|
75
|
+
|
76
|
+
# Checks if the input method is extended with Executable
|
77
|
+
def check_method(method) # :nodoc:
|
78
|
+
raise "not Executable: #{method}" unless method.kind_of?(Executable)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'tap/support/batchable'
|
2
|
+
require 'tap/support/executable'
|
3
|
+
require 'tap/support/framework_class'
|
4
|
+
|
5
|
+
module Tap
|
6
|
+
module Support
|
7
|
+
|
8
|
+
# Framework encapsulates the basic framework functionality (batching,
|
9
|
+
# configuration, documentation, etc) used by Task and Workflow. Note
|
10
|
+
# that Framework does NOT encapsulate the functionality needed to
|
11
|
+
# make a class useful in workflows, such as enq and on_complete.
|
12
|
+
module Framework
|
13
|
+
include Batchable
|
14
|
+
include Configurable
|
15
|
+
|
16
|
+
def self.included(mod)
|
17
|
+
mod.extend Support::BatchableClass
|
18
|
+
mod.extend Support::ConfigurableClass
|
19
|
+
mod.extend Support::FrameworkClass
|
20
|
+
end
|
21
|
+
|
22
|
+
# The application used to load config_file templates
|
23
|
+
# (and hence, to initialize batched objects).
|
24
|
+
attr_reader :app
|
25
|
+
|
26
|
+
# The name of self.
|
27
|
+
attr_accessor :name
|
28
|
+
|
29
|
+
# Initializes a new instance and associated batch objects. Batch
|
30
|
+
# objects will be initialized for each configuration template
|
31
|
+
# specified by app.each_config_template(config_file) where
|
32
|
+
# config_file = app.config_filepath(name).
|
33
|
+
def initialize(config={}, name=nil, app=App.instance)
|
34
|
+
super()
|
35
|
+
@app = app
|
36
|
+
@name = name || self.class.default_name
|
37
|
+
initialize_config(config)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Creates a new batched object and adds the object to batch. The batched object
|
41
|
+
# will be a duplicate of the current object but with a new name and/or
|
42
|
+
# configurations.
|
43
|
+
def initialize_batch_obj(overrides={}, name=nil)
|
44
|
+
obj = super().reconfigure(overrides)
|
45
|
+
obj.name = name if name
|
46
|
+
obj
|
47
|
+
end
|
48
|
+
|
49
|
+
# Logs the inputs to the application logger (via app.log)
|
50
|
+
def log(action, msg="", level=Logger::INFO)
|
51
|
+
# TODO - add a task identifier?
|
52
|
+
app.log(action, msg, level)
|
53
|
+
end
|
54
|
+
|
55
|
+
# Raises a TerminateError if app.state == State::TERMINATE.
|
56
|
+
# check_terminate may be called at any time to provide a
|
57
|
+
# breakpoint in long-running processes.
|
58
|
+
def check_terminate
|
59
|
+
if app.state == App::State::TERMINATE
|
60
|
+
raise App::TerminateError.new
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Returns self.name
|
65
|
+
def to_s
|
66
|
+
name
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,199 @@
|
|
1
|
+
require 'tap/support/command_line'
|
2
|
+
|
3
|
+
module Tap
|
4
|
+
module Support
|
5
|
+
|
6
|
+
# FrameworkClass encapsulates class methods related to Framework.
|
7
|
+
module FrameworkClass
|
8
|
+
|
9
|
+
# Returns the default name for the class: to_s.underscore
|
10
|
+
attr_accessor :default_name
|
11
|
+
|
12
|
+
def self.extended(base)
|
13
|
+
caller.each_with_index do |line, index|
|
14
|
+
case line
|
15
|
+
when /\/framework.rb/ then next
|
16
|
+
when /^(([A-z]:)?[^:]+):(\d+)/
|
17
|
+
base.instance_variable_set(:@source_file, File.expand_path($1))
|
18
|
+
break
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
base.instance_variable_set(:@default_name, base.to_s.underscore)
|
23
|
+
end
|
24
|
+
|
25
|
+
def inherited(child)
|
26
|
+
unless child.instance_variable_defined?(:@source_file)
|
27
|
+
caller.first =~ /^(([A-z]:)?[^:]+):(\d+)/
|
28
|
+
child.instance_variable_set(:@source_file, File.expand_path($1))
|
29
|
+
end
|
30
|
+
|
31
|
+
child.instance_variable_set(:@default_name, child.to_s.underscore)
|
32
|
+
super
|
33
|
+
end
|
34
|
+
|
35
|
+
def subclass(const_name, configs={}, options={}, &block)
|
36
|
+
# Generate the nesting module
|
37
|
+
current, constants = const_name.to_s.constants_split
|
38
|
+
raise ArgumentError, "#{current} is already defined!" if constants.empty?
|
39
|
+
|
40
|
+
subclass_const = constants.pop
|
41
|
+
constants.each {|const| current = current.const_set(const, Module.new)}
|
42
|
+
|
43
|
+
# Generate the subclass
|
44
|
+
subclass = Class.new(self)
|
45
|
+
configs = configs[0] if configs.kind_of?(Array) && configs.length == 1 && configs[0].kind_of?(Hash)
|
46
|
+
|
47
|
+
case configs
|
48
|
+
when Hash
|
49
|
+
subclass.send(:attr_accessor, *configs.keys)
|
50
|
+
configs.each_pair do |key, value|
|
51
|
+
subclass.configurations.add(key, value)
|
52
|
+
end
|
53
|
+
when Array
|
54
|
+
configs.each do |method, key, value, opts, config_block|
|
55
|
+
subclass.send(method, key, value, opts, &config_block)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
block_method = options[:block_method] || :process
|
60
|
+
subclass.send(:define_method, block_method, &block)
|
61
|
+
subclass.default_name = const_name
|
62
|
+
|
63
|
+
const_name = current == Object ? subclass_const : "#{current}::#{subclass_const}"
|
64
|
+
caller.each_with_index do |line, index|
|
65
|
+
case line
|
66
|
+
when /\/tap\/support\/declarations.rb/ then next
|
67
|
+
when /^(([A-z]:)?[^:]+):(\d+)/
|
68
|
+
subclass.source_file = File.expand_path($1)
|
69
|
+
subclass.lazydoc[const_name, false]['manifest'] = subclass.lazydoc.register($3.to_i - 1)
|
70
|
+
break
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
arity = options[:arity] || block.arity
|
75
|
+
comment = Comment.new
|
76
|
+
comment.subject = case
|
77
|
+
when arity > 0
|
78
|
+
Array.new(arity, "INPUT").join(' ')
|
79
|
+
when arity < 0
|
80
|
+
array = Array.new(-1 * arity - 1, "INPUT")
|
81
|
+
array << "INPUTS..."
|
82
|
+
array.join(' ')
|
83
|
+
else ""
|
84
|
+
end
|
85
|
+
subclass.lazydoc[const_name, false]['args'] ||= comment
|
86
|
+
|
87
|
+
# Set the subclass constant
|
88
|
+
current.const_set(subclass_const, subclass)
|
89
|
+
end
|
90
|
+
|
91
|
+
DEFAULT_HELP_TEMPLATE = %Q{<%= task_class %><%= manifest.subject.to_s.strip.empty? ? '' : ' -- ' %><%= manifest.subject %>
|
92
|
+
|
93
|
+
<% unless manifest.empty? %>
|
94
|
+
<%= '-' * 80 %>
|
95
|
+
|
96
|
+
<% manifest.wrap(77, 2, nil).each do |line| %>
|
97
|
+
<%= line %>
|
98
|
+
<% end %>
|
99
|
+
<%= '-' * 80 %>
|
100
|
+
<% end %>
|
101
|
+
|
102
|
+
}
|
103
|
+
|
104
|
+
def instantiate(argv, app=Tap::App.instance) # => instance, argv
|
105
|
+
opts = OptionParser.new
|
106
|
+
|
107
|
+
# Add configurations
|
108
|
+
config = {}
|
109
|
+
unless configurations.empty?
|
110
|
+
opts.separator ""
|
111
|
+
opts.separator "configurations:"
|
112
|
+
end
|
113
|
+
|
114
|
+
configurations.each do |receiver, key, configuration|
|
115
|
+
opts.on(*CommandLine.configv(configuration)) do |value|
|
116
|
+
config[key] = value
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# Add options on_tail, giving priority to configurations
|
121
|
+
opts.separator ""
|
122
|
+
opts.separator "options:"
|
123
|
+
|
124
|
+
opts.on_tail("-h", "--help", "Print this help") do
|
125
|
+
args = lazydoc(true)[to_s]['args'] || Tap::Support::Comment.new
|
126
|
+
|
127
|
+
opts.banner = "#{help}usage: tap run -- #{to_s.underscore} #{args.subject}"
|
128
|
+
puts opts
|
129
|
+
exit
|
130
|
+
end
|
131
|
+
|
132
|
+
# Add option for name
|
133
|
+
name = default_name
|
134
|
+
opts.on_tail('--name NAME', /^[^-].*/, 'Specify a name') do |value|
|
135
|
+
name = value
|
136
|
+
end
|
137
|
+
|
138
|
+
# Add option to add args
|
139
|
+
use_args = []
|
140
|
+
opts.on_tail('--use FILE', /^[^-].*/, 'Loads inputs from file') do |value|
|
141
|
+
obj = YAML.load_file(value)
|
142
|
+
case obj
|
143
|
+
when Hash
|
144
|
+
obj.values.each do |array|
|
145
|
+
# error if value isn't an array
|
146
|
+
use_args.concat(array)
|
147
|
+
end
|
148
|
+
when Array
|
149
|
+
use_args.concat(obj)
|
150
|
+
else
|
151
|
+
use_args << obj
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
opts.parse!(argv)
|
156
|
+
obj = new({}, name, app)
|
157
|
+
|
158
|
+
path_configs = load_config(app.config_filepath(name))
|
159
|
+
if path_configs.kind_of?(Array)
|
160
|
+
path_configs.each_with_index do |path_config, i|
|
161
|
+
obj.initialize_batch_obj(path_config, "#{name}_#{i}") unless i == 0
|
162
|
+
end
|
163
|
+
path_configs = path_configs[0]
|
164
|
+
end
|
165
|
+
|
166
|
+
[obj.reconfigure(path_configs).reconfigure(config), argv + use_args]
|
167
|
+
end
|
168
|
+
|
169
|
+
def lazydoc(resolve=false, args_method=:process)
|
170
|
+
if resolve
|
171
|
+
lazydoc = super(false)
|
172
|
+
lazydoc.resolve(nil, /^\s*def\s+#{args_method}(\((.*?)\))?/) do |comment, match|
|
173
|
+
comment.subject = match[2].to_s.split(',').collect do |arg|
|
174
|
+
arg = arg.strip.upcase
|
175
|
+
case arg
|
176
|
+
when /^&/ then nil
|
177
|
+
when /^\*/ then arg[1..-1] + "..."
|
178
|
+
else arg
|
179
|
+
end
|
180
|
+
end.join(', ')
|
181
|
+
|
182
|
+
lazydoc.default_attributes['args'] ||= comment
|
183
|
+
end
|
184
|
+
|
185
|
+
super(true)
|
186
|
+
else
|
187
|
+
super(false)
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
def help
|
192
|
+
Tap::Support::Templater.new(DEFAULT_HELP_TEMPLATE,
|
193
|
+
:task_class => self,
|
194
|
+
:manifest => lazydoc(true)[to_s]['manifest'] || Tap::Support::Comment.new
|
195
|
+
).build
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
@@ -0,0 +1,147 @@
|
|
1
|
+
module Tap
|
2
|
+
module Support
|
3
|
+
|
4
|
+
# InstanceConfiguration serves as a forwarding hash, where get and set operations
|
5
|
+
# for configurations are sent to instance methods rather than to an underlying data
|
6
|
+
# store.
|
7
|
+
#
|
8
|
+
# class Sample
|
9
|
+
# attr_accessor :key
|
10
|
+
# end
|
11
|
+
# sample = Sample.new
|
12
|
+
#
|
13
|
+
# class_config = ClassConfiguration.new(Sample)
|
14
|
+
# class_config.add(:key)
|
15
|
+
#
|
16
|
+
# config = InstanceConfiguration.new(class_config)
|
17
|
+
# config.bind(sample)
|
18
|
+
#
|
19
|
+
# sample.key = 'value'
|
20
|
+
# config[:key] # => 'value'
|
21
|
+
#
|
22
|
+
# config[:key] = 'another'
|
23
|
+
# sample.key # => 'another'
|
24
|
+
#
|
25
|
+
# Non-config keys are simply stored:
|
26
|
+
#
|
27
|
+
# config[:not_a_key] = 'value'
|
28
|
+
# config[:not_a_key] # => 'value'
|
29
|
+
#
|
30
|
+
# config.store # => {:not_a_key => 'value'}
|
31
|
+
# config.to_hash # => {:key => 'another', :not_a_key => 'value'}
|
32
|
+
#
|
33
|
+
class InstanceConfiguration
|
34
|
+
|
35
|
+
# The bound receiver
|
36
|
+
attr_reader :receiver
|
37
|
+
|
38
|
+
# The underlying data store for non-config keys
|
39
|
+
attr_reader :store
|
40
|
+
|
41
|
+
# The ClassConfiguration specifying config keys
|
42
|
+
attr_reader :class_config
|
43
|
+
|
44
|
+
def initialize(class_config, receiver=nil)
|
45
|
+
@receiver = receiver
|
46
|
+
@store = {}
|
47
|
+
@class_config = class_config
|
48
|
+
end
|
49
|
+
|
50
|
+
# Binds self to the specified receiver. Mapped keys are
|
51
|
+
# removed from store and sent to their writer method on
|
52
|
+
# receiver.
|
53
|
+
def bind(receiver)
|
54
|
+
raise ArgumentError.new("receiver cannot be nil") if receiver == nil
|
55
|
+
|
56
|
+
class_config.each_pair do |key, config|
|
57
|
+
receiver.send(config.writer, store.delete(key))
|
58
|
+
end
|
59
|
+
@receiver = receiver
|
60
|
+
|
61
|
+
self
|
62
|
+
end
|
63
|
+
|
64
|
+
# Returns true if self is bound to a receiver
|
65
|
+
def bound?
|
66
|
+
receiver != nil
|
67
|
+
end
|
68
|
+
|
69
|
+
# Unbinds self from the specified receiver. Mapped values
|
70
|
+
# are stored in store. Returns the unbound receiver.
|
71
|
+
def unbind
|
72
|
+
class_config.each_pair do |key, config|
|
73
|
+
store[key] = receiver.send(config.reader)
|
74
|
+
end
|
75
|
+
r = receiver
|
76
|
+
@receiver = nil
|
77
|
+
|
78
|
+
r
|
79
|
+
end
|
80
|
+
|
81
|
+
# Duplicates self, returning an unbound InstanceConfiguration.
|
82
|
+
def dup
|
83
|
+
duplicate = super()
|
84
|
+
duplicate.instance_variable_set(:@receiver, nil)
|
85
|
+
duplicate.instance_variable_set(:@store, @store.dup)
|
86
|
+
duplicate
|
87
|
+
end
|
88
|
+
|
89
|
+
# Associates the value the key. If bound? and the key
|
90
|
+
# is a class_config key, then the value will be forwarded
|
91
|
+
# to the class_config.writer method on the receiver.
|
92
|
+
def []=(key, value)
|
93
|
+
case
|
94
|
+
when bound? && config = class_config.map[key.to_sym]
|
95
|
+
receiver.send(config.writer, value)
|
96
|
+
else store[key] = value
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
# Retrieves the value corresponding to the key. If bound?
|
101
|
+
# and the key is a class_config key, then the value is
|
102
|
+
# obtained from the :key method on the receiver.
|
103
|
+
def [](key)
|
104
|
+
case
|
105
|
+
when bound? && config = class_config.map[key.to_sym]
|
106
|
+
receiver.send(config.reader)
|
107
|
+
else store[key]
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# True if the key is assigned in self.
|
112
|
+
def has_key?(key)
|
113
|
+
(bound? && class_config.key?(key)) || store.has_key?(key)
|
114
|
+
end
|
115
|
+
|
116
|
+
# Calls block once for each key-value pair stored in self.
|
117
|
+
def each_pair # :yields: key, value
|
118
|
+
class_config.each_pair do |key, config|
|
119
|
+
yield(key, receiver.send(config.reader))
|
120
|
+
end if bound?
|
121
|
+
|
122
|
+
store.each_pair do |key, value|
|
123
|
+
yield(key, value)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
# Equal if the to_hash values of self and another are equal.
|
128
|
+
def ==(another)
|
129
|
+
to_hash == another.to_hash
|
130
|
+
end
|
131
|
+
|
132
|
+
# Returns self as a hash.
|
133
|
+
def to_hash
|
134
|
+
hash = store.dup
|
135
|
+
class_config.keys.each do |key|
|
136
|
+
hash[key] = self[key]
|
137
|
+
end if bound?
|
138
|
+
hash
|
139
|
+
end
|
140
|
+
|
141
|
+
# Overrides default inspect to show the to_hash values.
|
142
|
+
def inspect
|
143
|
+
"#<#{self.class}:#{object_id} to_hash=#{to_hash.inspect}>"
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|