gamebox 0.0.1
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.
- data/History.txt +18 -0
- data/Manifest.txt +85 -0
- data/README.txt +33 -0
- data/Rakefile +42 -0
- data/TODO.txt +29 -0
- data/bin/gamebox +49 -0
- data/docs/gamebox04_big.png +0 -0
- data/docs/getting_started.rdoc +99 -0
- data/docs/logo.png +0 -0
- data/lib/gamebox.rb +6 -0
- data/lib/gamebox/actor.rb +143 -0
- data/lib/gamebox/actor_factory.rb +64 -0
- data/lib/gamebox/actor_view.rb +35 -0
- data/lib/gamebox/ai/line_of_site.rb +61 -0
- data/lib/gamebox/ai/polaris.rb +107 -0
- data/lib/gamebox/ai/two_d_grid_location.rb +21 -0
- data/lib/gamebox/ai/two_d_grid_map.rb +77 -0
- data/lib/gamebox/aliasing.rb +16 -0
- data/lib/gamebox/animated.rb +84 -0
- data/lib/gamebox/behavior.rb +16 -0
- data/lib/gamebox/config_manager.rb +22 -0
- data/lib/gamebox/console_app.rb +39 -0
- data/lib/gamebox/data/fonts/Asimov.ttf +0 -0
- data/lib/gamebox/data/fonts/GAMEBOX_FONTS_GO_HERE +0 -0
- data/lib/gamebox/data/graphics/GAMEBOX_GRAPHICS_GO_HERE +0 -0
- data/lib/gamebox/data/graphics/logo.png +0 -0
- data/lib/gamebox/data/music/GAMEBOX_MUSIC_GOES_HERE +0 -0
- data/lib/gamebox/data/sounds/GAMEBOX_SOUND_FX_GO_HERE +0 -0
- data/lib/gamebox/director.rb +47 -0
- data/lib/gamebox/gamebox_application.rb +77 -0
- data/lib/gamebox/graphical.rb +24 -0
- data/lib/gamebox/graphical_actor_view.rb +31 -0
- data/lib/gamebox/inflections.rb +52 -0
- data/lib/gamebox/inflector.rb +278 -0
- data/lib/gamebox/input_manager.rb +104 -0
- data/lib/gamebox/layered.rb +34 -0
- data/lib/gamebox/level.rb +64 -0
- data/lib/gamebox/linked_list.rb +137 -0
- data/lib/gamebox/logo.rb +11 -0
- data/lib/gamebox/metaclass.rb +6 -0
- data/lib/gamebox/mode.rb +123 -0
- data/lib/gamebox/mode_manager.rb +80 -0
- data/lib/gamebox/numbers_ext.rb +3 -0
- data/lib/gamebox/physical.rb +139 -0
- data/lib/gamebox/physical_director.rb +17 -0
- data/lib/gamebox/physical_level.rb +89 -0
- data/lib/gamebox/physics.rb +27 -0
- data/lib/gamebox/publisher_ext.rb +13 -0
- data/lib/gamebox/resource_manager.rb +122 -0
- data/lib/gamebox/score.rb +35 -0
- data/lib/gamebox/sorted_list.rb +59 -0
- data/lib/gamebox/sound_manager.rb +84 -0
- data/lib/gamebox/surface_ext.rb +37 -0
- data/lib/gamebox/svg_actor.rb +55 -0
- data/lib/gamebox/svg_document.rb +160 -0
- data/lib/gamebox/template_app/README +30 -0
- data/lib/gamebox/template_app/Rakefile +20 -0
- data/lib/gamebox/template_app/config/boot.rb +5 -0
- data/lib/gamebox/template_app/config/environment.rb +29 -0
- data/lib/gamebox/template_app/config/game.yml +6 -0
- data/lib/gamebox/template_app/config/mode_level_config.yml +3 -0
- data/lib/gamebox/template_app/config/objects.yml +29 -0
- data/lib/gamebox/template_app/data/fonts/FONTS_GO_HERE +0 -0
- data/lib/gamebox/template_app/data/graphics/GRAPHICS_GO_HERE +0 -0
- data/lib/gamebox/template_app/data/music/MUSIC_GOES_HERE +0 -0
- data/lib/gamebox/template_app/data/sounds/SOUND_FX_GO_HERE +0 -0
- data/lib/gamebox/template_app/doc/README_FOR_APP +1 -0
- data/lib/gamebox/template_app/lib/code_statistics.rb +107 -0
- data/lib/gamebox/template_app/lib/diy.rb +371 -0
- data/lib/gamebox/template_app/lib/platform.rb +16 -0
- data/lib/gamebox/template_app/src/app.rb +8 -0
- data/lib/gamebox/template_app/src/demo_level.rb +20 -0
- data/lib/gamebox/template_app/src/game.rb +22 -0
- data/lib/gamebox/template_app/src/my_actor.rb +17 -0
- data/lib/gamebox/version.rb +10 -0
- data/lib/gamebox/viewport.rb +81 -0
- data/lib/gamebox/wrapped_screen.rb +15 -0
- data/script/perf_polaris.rb +36 -0
- data/test/helper.rb +25 -0
- data/test/test_actor.rb +38 -0
- data/test/test_animated.rb +64 -0
- data/test/test_line_of_site.rb +14 -0
- data/test/test_physical.rb +26 -0
- data/test/test_polaris.rb +193 -0
- data/test/test_viewport.rb +116 -0
- metadata +188 -0
@@ -0,0 +1,20 @@
|
|
1
|
+
libdir = File.dirname(__FILE__)+"/lib"
|
2
|
+
$: << libdir
|
3
|
+
confdir = File.dirname(__FILE__)+"/config"
|
4
|
+
$: << confdir
|
5
|
+
|
6
|
+
require 'environment'
|
7
|
+
STATS_DIRECTORIES = [
|
8
|
+
%w(Source src/),
|
9
|
+
%w(Config config/),
|
10
|
+
%w(Maps maps/),
|
11
|
+
%w(Unit\ tests specs/),
|
12
|
+
%w(Libraries lib/),
|
13
|
+
].collect { |name, dir| [ name, "#{APP_ROOT}/#{dir}" ] }.select { |name, dir| File.directory?(dir) }
|
14
|
+
|
15
|
+
desc "Report code statistics (KLOCs, etc) from the application"
|
16
|
+
task :stats do
|
17
|
+
require 'code_statistics'
|
18
|
+
CodeStatistics.new(*STATS_DIRECTORIES).to_s
|
19
|
+
end
|
20
|
+
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
ADDITIONAL_LOAD_PATHS = []
|
3
|
+
ADDITIONAL_LOAD_PATHS.concat %w(
|
4
|
+
src
|
5
|
+
lib
|
6
|
+
config
|
7
|
+
../../lib
|
8
|
+
).map { |dir| File.dirname(__FILE__) + "/../" + dir }.select { |dir| File.directory?(dir) }
|
9
|
+
|
10
|
+
ADDITIONAL_LOAD_PATHS.each do |path|
|
11
|
+
$:.push path
|
12
|
+
end
|
13
|
+
|
14
|
+
APP_ROOT = File.dirname(__FILE__) + "/../"
|
15
|
+
CONFIG_PATH = APP_ROOT + "config/"
|
16
|
+
DATA_PATH = APP_ROOT + "data/"
|
17
|
+
SOUND_PATH = APP_ROOT + "data/sounds/"
|
18
|
+
MUSIC_PATH = APP_ROOT + "data/music/"
|
19
|
+
GFX_PATH = APP_ROOT + "data/graphics/"
|
20
|
+
FONTS_PATH = APP_ROOT + "data/fonts/"
|
21
|
+
|
22
|
+
require 'gamebox'
|
23
|
+
|
24
|
+
GAMEBOX_DATA_PATH = GAMEBOX_PATH + "data/"
|
25
|
+
GAMEBOX_SOUND_PATH = GAMEBOX_PATH + "data/sounds/"
|
26
|
+
GAMEBOX_MUSIC_PATH = GAMEBOX_PATH + "data/music/"
|
27
|
+
GAMEBOX_GFX_PATH = GAMEBOX_PATH + "data/graphics/"
|
28
|
+
GAMEBOX_FONTS_PATH = GAMEBOX_PATH + "data/fonts/"
|
29
|
+
|
@@ -0,0 +1,29 @@
|
|
1
|
+
game:
|
2
|
+
compose:
|
3
|
+
- wrapped_screen
|
4
|
+
- input_manager
|
5
|
+
- sound_manager
|
6
|
+
- mode_manager
|
7
|
+
resource_manager:
|
8
|
+
mode_manager:
|
9
|
+
compose:
|
10
|
+
- input_manager
|
11
|
+
- resource_manager
|
12
|
+
- sound_manager
|
13
|
+
- config_manager
|
14
|
+
- actor_factory
|
15
|
+
sound_manager:
|
16
|
+
compose:
|
17
|
+
- resource_manager
|
18
|
+
- config_manager
|
19
|
+
input_manager:
|
20
|
+
wrapped_screen:
|
21
|
+
compose:
|
22
|
+
- config_manager
|
23
|
+
actor_factory:
|
24
|
+
compose:
|
25
|
+
- input_manager
|
26
|
+
- sound_manager
|
27
|
+
config_manager:
|
28
|
+
compose:
|
29
|
+
- resource_manager
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
@@ -0,0 +1 @@
|
|
1
|
+
This is where you put documentation for your app.
|
@@ -0,0 +1,107 @@
|
|
1
|
+
class CodeStatistics #:nodoc:
|
2
|
+
|
3
|
+
TEST_TYPES = %w(Units Functionals Unit\ tests Functional\ tests Integration\ tests)
|
4
|
+
|
5
|
+
def initialize(*pairs)
|
6
|
+
@pairs = pairs
|
7
|
+
@statistics = calculate_statistics
|
8
|
+
@total = calculate_total if pairs.length > 1
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_s
|
12
|
+
print_header
|
13
|
+
@pairs.each { |pair| print_line(pair.first, @statistics[pair.first]) }
|
14
|
+
print_splitter
|
15
|
+
|
16
|
+
if @total
|
17
|
+
print_line("Total", @total)
|
18
|
+
print_splitter
|
19
|
+
end
|
20
|
+
|
21
|
+
print_code_test_stats
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
def calculate_statistics
|
26
|
+
@pairs.inject({}) { |stats, pair| stats[pair.first] = calculate_directory_statistics(pair.last); stats }
|
27
|
+
end
|
28
|
+
|
29
|
+
def calculate_directory_statistics(directory, pattern = /.*\.rb$/)
|
30
|
+
stats = { "lines" => 0, "codelines" => 0, "classes" => 0, "methods" => 0 }
|
31
|
+
|
32
|
+
Dir.foreach(directory) do |file_name|
|
33
|
+
if File.stat(directory + "/" + file_name).directory? and (/^\./ !~ file_name)
|
34
|
+
newstats = calculate_directory_statistics(directory + "/" + file_name, pattern)
|
35
|
+
stats.each { |k, v| stats[k] += newstats[k] }
|
36
|
+
end
|
37
|
+
|
38
|
+
next unless file_name =~ pattern
|
39
|
+
|
40
|
+
f = File.open(directory + "/" + file_name)
|
41
|
+
|
42
|
+
while line = f.gets
|
43
|
+
stats["lines"] += 1
|
44
|
+
stats["classes"] += 1 if line =~ /class [A-Z]/
|
45
|
+
stats["methods"] += 1 if line =~ /def [a-z]/
|
46
|
+
stats["codelines"] += 1 unless line =~ /^\s*$/ || line =~ /^\s*#/
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
stats
|
51
|
+
end
|
52
|
+
|
53
|
+
def calculate_total
|
54
|
+
total = { "lines" => 0, "codelines" => 0, "classes" => 0, "methods" => 0 }
|
55
|
+
@statistics.each_value { |pair| pair.each { |k, v| total[k] += v } }
|
56
|
+
total
|
57
|
+
end
|
58
|
+
|
59
|
+
def calculate_code
|
60
|
+
code_loc = 0
|
61
|
+
@statistics.each { |k, v| code_loc += v['codelines'] unless TEST_TYPES.include? k }
|
62
|
+
code_loc
|
63
|
+
end
|
64
|
+
|
65
|
+
def calculate_tests
|
66
|
+
test_loc = 0
|
67
|
+
@statistics.each { |k, v| test_loc += v['codelines'] if TEST_TYPES.include? k }
|
68
|
+
test_loc
|
69
|
+
end
|
70
|
+
|
71
|
+
def print_header
|
72
|
+
print_splitter
|
73
|
+
puts "| Name | Lines | LOC | Classes | Methods | M/C | LOC/M |"
|
74
|
+
print_splitter
|
75
|
+
end
|
76
|
+
|
77
|
+
def print_splitter
|
78
|
+
puts "+----------------------+-------+-------+---------+---------+-----+-------+"
|
79
|
+
end
|
80
|
+
|
81
|
+
def print_line(name, statistics)
|
82
|
+
m_over_c = (statistics["methods"] / statistics["classes"]) rescue m_over_c = 0
|
83
|
+
loc_over_m = (statistics["codelines"] / statistics["methods"]) - 2 rescue loc_over_m = 0
|
84
|
+
|
85
|
+
start = if TEST_TYPES.include? name
|
86
|
+
"| #{name.ljust(18)} "
|
87
|
+
else
|
88
|
+
"| #{name.ljust(20)} "
|
89
|
+
end
|
90
|
+
|
91
|
+
puts start +
|
92
|
+
"| #{statistics["lines"].to_s.rjust(5)} " +
|
93
|
+
"| #{statistics["codelines"].to_s.rjust(5)} " +
|
94
|
+
"| #{statistics["classes"].to_s.rjust(7)} " +
|
95
|
+
"| #{statistics["methods"].to_s.rjust(7)} " +
|
96
|
+
"| #{m_over_c.to_s.rjust(3)} " +
|
97
|
+
"| #{loc_over_m.to_s.rjust(5)} |"
|
98
|
+
end
|
99
|
+
|
100
|
+
def print_code_test_stats
|
101
|
+
code = calculate_code
|
102
|
+
tests = calculate_tests
|
103
|
+
|
104
|
+
puts " Code LOC: #{code} Test LOC: #{tests} Code to Test Ratio: 1:#{sprintf("%.1f", tests.to_f/code)}"
|
105
|
+
puts ""
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,371 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'set'
|
3
|
+
|
4
|
+
#
|
5
|
+
# DIY (Dependency Injection in Yaml) is a simple dependency injection library
|
6
|
+
# which focuses on declarative composition of objects through setter injection.
|
7
|
+
#
|
8
|
+
# == Examples
|
9
|
+
#
|
10
|
+
# === A Simple Context
|
11
|
+
#
|
12
|
+
# The context is a hash specified in in a yaml file. Each top-level key identifies
|
13
|
+
# an object. When the context is created and queried for an object, by default,
|
14
|
+
# the context will require a file with the same name:
|
15
|
+
# require 'foo'
|
16
|
+
# next, by default, it will call new on a class from the camel-cased name of the key:
|
17
|
+
# Foo.new
|
18
|
+
#
|
19
|
+
# foo.rb:
|
20
|
+
# class Foo; end
|
21
|
+
#
|
22
|
+
# context.yml:
|
23
|
+
# ---
|
24
|
+
# foo:
|
25
|
+
# bar:
|
26
|
+
#
|
27
|
+
# c = DIY::Context.from_file('context.yml')
|
28
|
+
# c[:foo] #=> #<Foo:0x81eb0>
|
29
|
+
#
|
30
|
+
# === Specifying Class Name
|
31
|
+
#
|
32
|
+
# If the class name isn't the camel-cased key:
|
33
|
+
#
|
34
|
+
# foo.rb:
|
35
|
+
# class MyFoo; end
|
36
|
+
#
|
37
|
+
# context.yml:
|
38
|
+
# ---
|
39
|
+
# foo:
|
40
|
+
# class: MyFoo
|
41
|
+
# bar:
|
42
|
+
#
|
43
|
+
# === Specifying Ruby File to Require
|
44
|
+
#
|
45
|
+
# If the file the class resides in isn't named after they key:
|
46
|
+
#
|
47
|
+
# fun_stuff.rb:
|
48
|
+
# class Foo; end
|
49
|
+
#
|
50
|
+
# context.yml:
|
51
|
+
# ---
|
52
|
+
# foo:
|
53
|
+
# lib: fun_stuff
|
54
|
+
# bar:
|
55
|
+
#
|
56
|
+
# === Constructor Arguments
|
57
|
+
#
|
58
|
+
# DIY allows specification of constructor arguments as hash key-value pairs
|
59
|
+
# using the <tt>compose</tt> directive.
|
60
|
+
#
|
61
|
+
# foo.rb:
|
62
|
+
# class Foo
|
63
|
+
# def initialize(args)
|
64
|
+
# @bar = args[:bar]
|
65
|
+
# @other = args[:other]
|
66
|
+
# end
|
67
|
+
# end
|
68
|
+
#
|
69
|
+
# context.yml:
|
70
|
+
# ---
|
71
|
+
# foo:
|
72
|
+
# compose: bar, other
|
73
|
+
# bar:
|
74
|
+
# other:
|
75
|
+
#
|
76
|
+
# To make constructor definition easier use constructor:
|
77
|
+
#
|
78
|
+
# foo.rb:
|
79
|
+
# class Foo
|
80
|
+
# constructor :bar, :other
|
81
|
+
# end
|
82
|
+
#
|
83
|
+
# If the constructor argument names don't match up with the object keys
|
84
|
+
# in the context, they can be mapped explicitly.
|
85
|
+
#
|
86
|
+
# foo.rb:
|
87
|
+
# class Foo
|
88
|
+
# constructor :bar, :other
|
89
|
+
# end
|
90
|
+
#
|
91
|
+
# context.yml:
|
92
|
+
# ---
|
93
|
+
# foo:
|
94
|
+
# bar: my_bar
|
95
|
+
# other: the_other_one
|
96
|
+
# my_bar:
|
97
|
+
# the_other_one:
|
98
|
+
#
|
99
|
+
# === Non-singleton objects
|
100
|
+
#
|
101
|
+
# Non-singletons will be re-instantiated each time they are needed.
|
102
|
+
#
|
103
|
+
# context.yml:
|
104
|
+
# ---
|
105
|
+
# foo:
|
106
|
+
# singleton: false
|
107
|
+
#
|
108
|
+
# bar:
|
109
|
+
#
|
110
|
+
# engine:
|
111
|
+
# compose: foo, bar
|
112
|
+
#
|
113
|
+
module DIY
|
114
|
+
class Context
|
115
|
+
def initialize(context_hash, extra_inputs={})
|
116
|
+
raise "Nil context hash" unless context_hash
|
117
|
+
raise "Need a hash" unless context_hash.kind_of?(Hash)
|
118
|
+
[ "[]", "keys" ].each do |mname|
|
119
|
+
unless extra_inputs.respond_to?(mname)
|
120
|
+
raise "Extra inputs must respond to hash-like [] operator and methods #keys and #each"
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
# store extra inputs
|
125
|
+
if extra_inputs.kind_of?(Hash)
|
126
|
+
@extra_inputs = {}
|
127
|
+
extra_inputs.each { |k,v| @extra_inputs[k.to_s] = v } # smooth out the names
|
128
|
+
else
|
129
|
+
@extra_inputs = extra_inputs
|
130
|
+
end
|
131
|
+
|
132
|
+
# Collect object and subcontext definitions
|
133
|
+
@defs = {}
|
134
|
+
@sub_context_defs = {}
|
135
|
+
context_hash.each do |name,info|
|
136
|
+
name = name.to_s
|
137
|
+
case name
|
138
|
+
when /^\+/
|
139
|
+
# subcontext
|
140
|
+
@sub_context_defs[name.gsub(/^\+/,'')] = info
|
141
|
+
|
142
|
+
else
|
143
|
+
# Normal singleton object def
|
144
|
+
if extra_inputs_has(name)
|
145
|
+
raise ConstructionError.new(name, "Object definition conflicts with parent context")
|
146
|
+
end
|
147
|
+
@defs[name] = ObjectDef.new(:name => name, :info => info)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
|
152
|
+
# init the cache
|
153
|
+
@cache = {}
|
154
|
+
@cache['this_context'] = self
|
155
|
+
end
|
156
|
+
|
157
|
+
|
158
|
+
def self.from_yaml(io_or_string, extra_inputs={})
|
159
|
+
raise "nil input to YAML" unless io_or_string
|
160
|
+
Context.new(YAML.load(io_or_string), extra_inputs)
|
161
|
+
end
|
162
|
+
|
163
|
+
def self.from_file(fname, extra_inputs={})
|
164
|
+
raise "nil file name" unless fname
|
165
|
+
self.from_yaml(File.read(fname), extra_inputs)
|
166
|
+
end
|
167
|
+
|
168
|
+
def get_object(obj_name)
|
169
|
+
key = obj_name.to_s
|
170
|
+
obj = @cache[key]
|
171
|
+
unless obj
|
172
|
+
if extra_inputs_has(key)
|
173
|
+
obj = @extra_inputs[key]
|
174
|
+
end
|
175
|
+
end
|
176
|
+
unless obj
|
177
|
+
obj = construct_object(key)
|
178
|
+
@cache[key] = obj if @defs[key].singleton?
|
179
|
+
end
|
180
|
+
obj
|
181
|
+
end
|
182
|
+
alias :[] :get_object
|
183
|
+
|
184
|
+
def set_object(obj_name,obj)
|
185
|
+
key = obj_name.to_s
|
186
|
+
raise "object '#{key}' already exists in context" if @cache.keys.include?(key)
|
187
|
+
@cache[key] = obj
|
188
|
+
end
|
189
|
+
alias :[]= :set_object
|
190
|
+
|
191
|
+
def keys
|
192
|
+
(@defs.keys.to_set + @extra_inputs.keys.to_set).to_a
|
193
|
+
end
|
194
|
+
|
195
|
+
# Instantiate and yield the named subcontext
|
196
|
+
def within(sub_context_name)
|
197
|
+
# Find the subcontext definitaion:
|
198
|
+
context_def = @sub_context_defs[sub_context_name.to_s]
|
199
|
+
raise "No sub-context named #{sub_context_name}" unless context_def
|
200
|
+
# Instantiate a new context using self as parent:
|
201
|
+
context = Context.new( context_def, self )
|
202
|
+
|
203
|
+
yield context
|
204
|
+
end
|
205
|
+
|
206
|
+
def contains_object(obj_name)
|
207
|
+
key = obj_name.to_s
|
208
|
+
@defs.keys.member?(key) or extra_inputs_has(key)
|
209
|
+
end
|
210
|
+
|
211
|
+
def build_everything
|
212
|
+
@defs.keys.each { |k| self[k] }
|
213
|
+
end
|
214
|
+
alias :build_all :build_everything
|
215
|
+
alias :preinstantiate_singletons :build_everything
|
216
|
+
|
217
|
+
private
|
218
|
+
|
219
|
+
def construct_object(key)
|
220
|
+
# Find the object definition
|
221
|
+
obj_def = @defs[key]
|
222
|
+
raise "No object definition for '#{key}'" unless obj_def
|
223
|
+
|
224
|
+
# If object def mentions a library, load it
|
225
|
+
require obj_def.library if obj_def.library
|
226
|
+
|
227
|
+
# Resolve all components for the object
|
228
|
+
arg_hash = {}
|
229
|
+
obj_def.components.each do |name,value|
|
230
|
+
case value
|
231
|
+
when Lookup
|
232
|
+
arg_hash[name.to_sym] = get_object(value.name)
|
233
|
+
when StringValue
|
234
|
+
arg_hash[name.to_sym] = value.literal_value
|
235
|
+
else
|
236
|
+
raise "Cannot cope with component definition '#{value.inspect}'"
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
if obj_def.code.nil?
|
241
|
+
# Get a reference to the class for the object
|
242
|
+
big_c = get_class_for_name_with_module_delimeters(obj_def.class_name)
|
243
|
+
# Make and return the instance
|
244
|
+
if arg_hash.keys.size > 0
|
245
|
+
return big_c.new(arg_hash)
|
246
|
+
else
|
247
|
+
return big_c.new
|
248
|
+
end
|
249
|
+
else
|
250
|
+
eval_ret = eval(obj_def.code)
|
251
|
+
return eval_ret
|
252
|
+
end
|
253
|
+
rescue Exception => oops
|
254
|
+
cerr = ConstructionError.new(key,oops)
|
255
|
+
cerr.set_backtrace(oops.backtrace)
|
256
|
+
raise cerr
|
257
|
+
end
|
258
|
+
|
259
|
+
def get_class_for_name_with_module_delimeters(class_name)
|
260
|
+
class_name.split(/::/).inject(Object) do |mod,const_name| mod.const_get(const_name) end
|
261
|
+
end
|
262
|
+
|
263
|
+
def extra_inputs_has(key)
|
264
|
+
if key.nil? or key.strip == ''
|
265
|
+
raise ArgumentError.new("Cannot lookup objects with nil keys")
|
266
|
+
end
|
267
|
+
@extra_inputs.keys.member?(key) or @extra_inputs.keys.member?(key.to_sym)
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
class Lookup #:nodoc:
|
272
|
+
attr_reader :name
|
273
|
+
def initialize(obj_name)
|
274
|
+
@name = obj_name
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
class ObjectDef #:nodoc:
|
279
|
+
attr_accessor :name, :class_name, :library, :components, :code
|
280
|
+
def initialize(opts)
|
281
|
+
name = opts[:name]
|
282
|
+
raise "Can't make an ObjectDef without a name" if name.nil?
|
283
|
+
|
284
|
+
info = opts[:info] || {}
|
285
|
+
info = info.clone
|
286
|
+
|
287
|
+
@components = {}
|
288
|
+
|
289
|
+
# Object name
|
290
|
+
@name = name
|
291
|
+
|
292
|
+
# Class name
|
293
|
+
@class_name = info.delete 'class'
|
294
|
+
@class_name ||= info.delete 'type'
|
295
|
+
@class_name ||= camelize(@name)
|
296
|
+
|
297
|
+
# Library
|
298
|
+
@library = info.delete 'library'
|
299
|
+
@library ||= info.delete 'lib'
|
300
|
+
@library ||= underscore(@class_name)
|
301
|
+
|
302
|
+
@library = nil if @library == "nil"
|
303
|
+
|
304
|
+
# Code
|
305
|
+
@code = info.delete 'code'
|
306
|
+
|
307
|
+
# Auto-compose
|
308
|
+
compose = info.delete 'compose'
|
309
|
+
if compose
|
310
|
+
case compose
|
311
|
+
when Array
|
312
|
+
auto_names = compose.map { |x| x.to_s }
|
313
|
+
when String
|
314
|
+
auto_names = compose.split(',').map { |x| x.to_s.strip }
|
315
|
+
when Symbol
|
316
|
+
auto_names = [ compose.to_s ]
|
317
|
+
else
|
318
|
+
raise "Cannot auto compose object #{@name}, bad 'compose' format: #{compose.inspect}"
|
319
|
+
end
|
320
|
+
end
|
321
|
+
auto_names ||= []
|
322
|
+
auto_names.each do |cname|
|
323
|
+
@components[cname] = Lookup.new(cname)
|
324
|
+
end
|
325
|
+
|
326
|
+
# Singleton status
|
327
|
+
if info['singleton'].nil?
|
328
|
+
@singleton = true
|
329
|
+
else
|
330
|
+
@singleton = info['singleton']
|
331
|
+
end
|
332
|
+
info.delete 'singleton'
|
333
|
+
|
334
|
+
# Remaining keys
|
335
|
+
info.each do |key,val|
|
336
|
+
@components[key.to_s] = Lookup.new(val.to_s)
|
337
|
+
end
|
338
|
+
|
339
|
+
end
|
340
|
+
|
341
|
+
def singleton?
|
342
|
+
@singleton
|
343
|
+
end
|
344
|
+
|
345
|
+
private
|
346
|
+
# Ganked this from Inflector:
|
347
|
+
def camelize(lower_case_and_underscored_word)
|
348
|
+
lower_case_and_underscored_word.to_s.gsub(/\/(.?)/) { "::" + $1.upcase }.gsub(/(^|_)(.)/) { $2.upcase }
|
349
|
+
end
|
350
|
+
# Ganked this from Inflector:
|
351
|
+
def underscore(camel_cased_word)
|
352
|
+
camel_cased_word.to_s.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z])/,'\1_\2').gsub(/([a-z\d])([A-Z])/,'\1_\2').downcase
|
353
|
+
end
|
354
|
+
end
|
355
|
+
|
356
|
+
# Exception raised when an object can't be created which is defined in the context.
|
357
|
+
class ConstructionError < RuntimeError
|
358
|
+
def initialize(object_name, cause=nil) #:nodoc:
|
359
|
+
object_name = object_name
|
360
|
+
cause = cause
|
361
|
+
m = "Failed to construct '#{object_name}'"
|
362
|
+
if cause
|
363
|
+
m << "\n ...caused by:\n >>> #{cause}"
|
364
|
+
end
|
365
|
+
super m
|
366
|
+
end
|
367
|
+
end
|
368
|
+
end
|
369
|
+
|
370
|
+
|
371
|
+
|