autumn 3.1.8
Sign up to get free protection for your applications and to get access to all the features.
- data/AUTHORS +11 -0
- data/CHANGELOG +567 -0
- data/MANIFEST +110 -0
- data/README +1114 -0
- data/README.textile +1153 -0
- data/Rakefile +75 -0
- data/autumn.gemspec +44 -0
- data/bin/autumn +11 -0
- data/lib/autumn.rb +8 -0
- data/lib/autumn/authentication.rb +238 -0
- data/lib/autumn/channel_leaf.rb +107 -0
- data/lib/autumn/coder.rb +166 -0
- data/lib/autumn/console_boot.rb +10 -0
- data/lib/autumn/ctcp.rb +250 -0
- data/lib/autumn/daemon.rb +207 -0
- data/lib/autumn/datamapper_hacks.rb +290 -0
- data/lib/autumn/foliater.rb +231 -0
- data/lib/autumn/formatting.rb +236 -0
- data/lib/autumn/generator.rb +231 -0
- data/lib/autumn/genesis.rb +190 -0
- data/lib/autumn/inheritable_attributes.rb +162 -0
- data/lib/autumn/leaf.rb +738 -0
- data/lib/autumn/log_facade.rb +49 -0
- data/lib/autumn/misc.rb +87 -0
- data/lib/autumn/resources/daemons/Anothernet.yml +3 -0
- data/lib/autumn/resources/daemons/AustHex.yml +29 -0
- data/lib/autumn/resources/daemons/Bahamut.yml +67 -0
- data/lib/autumn/resources/daemons/Dancer.yml +3 -0
- data/lib/autumn/resources/daemons/GameSurge.yml +3 -0
- data/lib/autumn/resources/daemons/IRCnet.yml +3 -0
- data/lib/autumn/resources/daemons/Ithildin.yml +7 -0
- data/lib/autumn/resources/daemons/KineIRCd.yml +56 -0
- data/lib/autumn/resources/daemons/PTlink.yml +6 -0
- data/lib/autumn/resources/daemons/QuakeNet.yml +20 -0
- data/lib/autumn/resources/daemons/RFC1459.yml +158 -0
- data/lib/autumn/resources/daemons/RFC2811.yml +16 -0
- data/lib/autumn/resources/daemons/RFC2812.yml +36 -0
- data/lib/autumn/resources/daemons/RatBox.yml +25 -0
- data/lib/autumn/resources/daemons/Ultimate.yml +24 -0
- data/lib/autumn/resources/daemons/Undernet.yml +6 -0
- data/lib/autumn/resources/daemons/Unreal.yml +110 -0
- data/lib/autumn/resources/daemons/_Other.yml +7 -0
- data/lib/autumn/resources/daemons/aircd.yml +33 -0
- data/lib/autumn/resources/daemons/bdq-ircd.yml +3 -0
- data/lib/autumn/resources/daemons/hybrid.yml +38 -0
- data/lib/autumn/resources/daemons/ircu.yml +67 -0
- data/lib/autumn/resources/daemons/tr-ircd.yml +8 -0
- data/lib/autumn/script.rb +74 -0
- data/lib/autumn/speciator.rb +165 -0
- data/lib/autumn/stem.rb +919 -0
- data/lib/autumn/stem_facade.rb +176 -0
- data/lib/autumn/tool/bin.rb +301 -0
- data/lib/autumn/tool/create.rb +48 -0
- data/lib/autumn/tool/project_creator.rb +110 -0
- data/lib/autumn/version.rb +3 -0
- data/lib/skel/Rakefile +163 -0
- data/lib/skel/config/global.yml +2 -0
- data/lib/skel/config/seasons/testing/database.yml +4 -0
- data/lib/skel/config/seasons/testing/leaves.yml +9 -0
- data/lib/skel/config/seasons/testing/season.yml +2 -0
- data/lib/skel/config/seasons/testing/stems.yml +10 -0
- data/lib/skel/leaves/administrator/README +20 -0
- data/lib/skel/leaves/administrator/controller.rb +67 -0
- data/lib/skel/leaves/administrator/views/autumn.txt.erb +1 -0
- data/lib/skel/leaves/administrator/views/reload.txt.erb +11 -0
- data/lib/skel/leaves/insulter/README +17 -0
- data/lib/skel/leaves/insulter/controller.rb +65 -0
- data/lib/skel/leaves/insulter/views/about.txt.erb +1 -0
- data/lib/skel/leaves/insulter/views/help.txt.erb +1 -0
- data/lib/skel/leaves/insulter/views/insult.txt.erb +1 -0
- data/lib/skel/leaves/scorekeeper/README +34 -0
- data/lib/skel/leaves/scorekeeper/config.yml +2 -0
- data/lib/skel/leaves/scorekeeper/controller.rb +104 -0
- data/lib/skel/leaves/scorekeeper/helpers/general.rb +64 -0
- data/lib/skel/leaves/scorekeeper/models/channel.rb +12 -0
- data/lib/skel/leaves/scorekeeper/models/person.rb +14 -0
- data/lib/skel/leaves/scorekeeper/models/pseudonym.rb +11 -0
- data/lib/skel/leaves/scorekeeper/models/score.rb +14 -0
- data/lib/skel/leaves/scorekeeper/tasks/stats.rake +17 -0
- data/lib/skel/leaves/scorekeeper/views/about.txt.erb +1 -0
- data/lib/skel/leaves/scorekeeper/views/change.txt.erb +5 -0
- data/lib/skel/leaves/scorekeeper/views/history.txt.erb +11 -0
- data/lib/skel/leaves/scorekeeper/views/points.txt.erb +5 -0
- data/lib/skel/leaves/scorekeeper/views/usage.txt.erb +1 -0
- data/lib/skel/log/README +1 -0
- data/lib/skel/script/console +28 -0
- data/lib/skel/script/destroy +48 -0
- data/lib/skel/script/generate +48 -0
- data/lib/skel/shared/README +1 -0
- data/lib/skel/tmp/README +1 -0
- data/spec/authentication_spec.rb +328 -0
- data/spec/channel_leaf_spec.rb +142 -0
- data/spec/coder_spec.rb +146 -0
- data/spec/ctcp_spec.rb +222 -0
- data/spec/daemon_spec.rb +202 -0
- data/spec/datamapper_hacks_spec.rb +164 -0
- data/tasks/authors.rake +30 -0
- data/tasks/changelog.rake +18 -0
- data/tasks/copyright.rake +21 -0
- data/tasks/doc.rake +7 -0
- data/tasks/gem.rake +23 -0
- data/tasks/gem_installer.rake +76 -0
- data/tasks/install_dependencies.rake +6 -0
- data/tasks/manifest.rake +4 -0
- data/tasks/rcov.rake +23 -0
- data/tasks/release.rake +52 -0
- data/tasks/reversion.rake +8 -0
- data/tasks/setup.rake +24 -0
- data/tasks/spec.rake +7 -0
- data/tasks/yard.rake +4 -0
- metadata +188 -0
@@ -0,0 +1,290 @@
|
|
1
|
+
# A set of hacks to make DataMapper play more nicely with classes within
|
2
|
+
# modules.
|
3
|
+
|
4
|
+
module DataMapper # :nodoc:
|
5
|
+
|
6
|
+
#HACK Add module names to auto-generated class names in relationships
|
7
|
+
#
|
8
|
+
# When a class name is automatically inferred from a relationship name (e.g.,
|
9
|
+
# guessing that has_many :widgets refers to a Widget class), it is necessary
|
10
|
+
# to enclose these class names in the same modules as the calling class. For
|
11
|
+
# example, if MyLeaf::Factory has_many :widgets, this hack will ensure the
|
12
|
+
# inferred class name is MyLeaf::Widget, instead of just ::Widget.
|
13
|
+
#
|
14
|
+
# This hack is performed for each of the association types in DataMapper. An
|
15
|
+
# :old_behavior option is given to revert to the unhacked method.
|
16
|
+
|
17
|
+
module Associations # :nodoc:
|
18
|
+
module OneToMany # :nodoc:
|
19
|
+
class << self
|
20
|
+
alias_method :old_setup, :setup
|
21
|
+
def setup(name, model, options={})
|
22
|
+
class_name = options.fetch(:class_name, Extlib::Inflection.classify(name))
|
23
|
+
if not options[:old_behavior] and not class_name.include?('::') then
|
24
|
+
modules = model.to_s.split('::')
|
25
|
+
modules.pop
|
26
|
+
modules << class_name
|
27
|
+
options[:class_name] = modules.join('::')
|
28
|
+
end
|
29
|
+
old_setup(name, model, options)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
module OneToOne # :nodoc:
|
35
|
+
class << self
|
36
|
+
alias_method :old_setup, :setup
|
37
|
+
def setup(name, model, options={})
|
38
|
+
class_name = options.fetch(:class_name, Extlib::Inflection.classify(name))
|
39
|
+
if not options[:old_behavior] and not class_name.include?('::') then
|
40
|
+
modules = model.to_s.split('::')
|
41
|
+
modules.pop
|
42
|
+
modules << class_name
|
43
|
+
options[:class_name] = modules.join('::')
|
44
|
+
end
|
45
|
+
old_setup(name, model, options)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
module ManyToOne # :nodoc:
|
51
|
+
class << self
|
52
|
+
alias_method :old_setup, :setup
|
53
|
+
def setup(name, model, options={})
|
54
|
+
class_name = options.fetch(:class_name, Extlib::Inflection.classify(name))
|
55
|
+
if not options[:old_behavior] and not class_name.include?('::') then
|
56
|
+
modules = model.to_s.split('::')
|
57
|
+
modules.pop
|
58
|
+
modules << class_name
|
59
|
+
options[:class_name] = modules.join('::')
|
60
|
+
end
|
61
|
+
old_setup(name, model, options)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
#HACK Strip module names when auto-generating table names for has-many-through
|
69
|
+
# relationships.
|
70
|
+
#
|
71
|
+
# By default, DataMapper will not strip module names when creating the join
|
72
|
+
# tables for has-many-through relationships. So, if MyLeaf::Post has and belongs
|
73
|
+
# to many MyLeaf::Category, the join table will be called
|
74
|
+
# "my_leaf/categories_my_leaf/posts", which is clearly an invalid table name.
|
75
|
+
# This hack strips module components from a class name before generating the
|
76
|
+
# join table's name.
|
77
|
+
#
|
78
|
+
# A side effect of this hack is that no two DataMapper models for the same
|
79
|
+
# repository can share the same name, even if they are in separate modules.
|
80
|
+
#
|
81
|
+
# This also fixes a bug that can occur when script/console is launched. The
|
82
|
+
# double assignment of the relationship variable seems to mess up IRb, so it has
|
83
|
+
# been split into two assignments.
|
84
|
+
|
85
|
+
DataMapper::Associations::ManyToMany.module_eval do # :nodoc:
|
86
|
+
def self.setup(name, model, options={})
|
87
|
+
class_name = options.fetch(:class_name, Extlib::Inflection.classify(name))
|
88
|
+
if not options[:old_behavior] and not class_name.include?('::') then
|
89
|
+
modules = model.to_s.split('::')
|
90
|
+
modules.pop
|
91
|
+
modules << class_name
|
92
|
+
options[:class_name] = modules.join('::')
|
93
|
+
end
|
94
|
+
|
95
|
+
assert_kind_of 'name', name, Symbol
|
96
|
+
assert_kind_of 'model', model, DataMapper::Model
|
97
|
+
assert_kind_of 'options', options, Hash
|
98
|
+
|
99
|
+
repository_name = model.repository.name
|
100
|
+
|
101
|
+
model.class_eval <<-EOS, __FILE__, __LINE__
|
102
|
+
def #{name}(query = {})
|
103
|
+
#{name}_association.all(query)
|
104
|
+
end
|
105
|
+
|
106
|
+
def #{name}=(children)
|
107
|
+
#{name}_association.replace(children)
|
108
|
+
end
|
109
|
+
|
110
|
+
private
|
111
|
+
|
112
|
+
def #{name}_association
|
113
|
+
@#{name}_association ||= begin
|
114
|
+
unless relationship = model.relationships(#{repository_name.inspect})[#{name.inspect}]
|
115
|
+
raise ArgumentError, "Relationship #{name.inspect} does not exist in \#{model}"
|
116
|
+
end
|
117
|
+
association = Proxy.new(relationship, self)
|
118
|
+
parent_associations << association
|
119
|
+
association
|
120
|
+
end
|
121
|
+
end
|
122
|
+
EOS
|
123
|
+
|
124
|
+
opts = options.dup
|
125
|
+
opts.delete(:through)
|
126
|
+
opts[:child_model] ||= opts.delete(:class_name) || Extlib::Inflection.classify(name)
|
127
|
+
opts[:parent_model] = model
|
128
|
+
opts[:repository_name] = repository_name
|
129
|
+
opts[:remote_relationship_name] ||= opts.delete(:remote_name) || Extlib::Inflection.tableize(opts[:child_model])
|
130
|
+
opts[:parent_key] = opts[:parent_key]
|
131
|
+
opts[:child_key] = opts[:child_key]
|
132
|
+
opts[:mutable] = true
|
133
|
+
|
134
|
+
names = [ opts[:child_model].demodulize, opts[:parent_model].name.demodulize ].sort
|
135
|
+
model_name = names.join.gsub("::", "")
|
136
|
+
storage_name = Extlib::Inflection.tableize(Extlib::Inflection.pluralize(names[0]) + names[1])
|
137
|
+
model_module = model.to_s.split('::')
|
138
|
+
model_module.pop
|
139
|
+
model_module = model_module.join('::')
|
140
|
+
model_module = model_module.empty? ? Object : eval("::#{model_module}")
|
141
|
+
|
142
|
+
opts[:near_relationship_name] = Extlib::Inflection.tableize(model_name).to_sym
|
143
|
+
|
144
|
+
model.has(model.n, opts[:near_relationship_name], :old_behavior => true)
|
145
|
+
|
146
|
+
relationship = DataMapper::Associations::RelationshipChain.new(opts)
|
147
|
+
model.relationships(repository_name)[name] = relationship
|
148
|
+
|
149
|
+
unless model_module.const_defined?(model_name)
|
150
|
+
model = DataMapper::Model.new(storage_name)
|
151
|
+
|
152
|
+
model.class_eval <<-EOS, __FILE__, __LINE__
|
153
|
+
def self.name; #{model_name.inspect} end
|
154
|
+
def self.default_repository_name; #{repository_name.inspect} end
|
155
|
+
def self.many_to_many; true end
|
156
|
+
EOS
|
157
|
+
|
158
|
+
names.each do |n|
|
159
|
+
model.belongs_to(Extlib::Inflection.underscore(n).gsub("/", "_").to_sym, :class_name => n)
|
160
|
+
end
|
161
|
+
|
162
|
+
model_module.const_set(model_name, model)
|
163
|
+
end
|
164
|
+
|
165
|
+
relationship
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
#HACK Update methods in RelationshipChain to use the scoped repository.
|
170
|
+
#
|
171
|
+
# This hack will update methods to use the currently-scoped repository, instead
|
172
|
+
# of always using the default repository.
|
173
|
+
|
174
|
+
module DataMapper # :nodoc:
|
175
|
+
module Associations # :nodoc:
|
176
|
+
class RelationshipChain # :nodoc:
|
177
|
+
def near_relationship
|
178
|
+
parent_model.relationships(repository.name)[@near_relationship_name]
|
179
|
+
end
|
180
|
+
|
181
|
+
def remote_relationship
|
182
|
+
return nil unless near_relationship
|
183
|
+
near_relationship.child_model.relationships(repository.name)[@remote_relationship_name] ||
|
184
|
+
near_relationship.child_model.relationships(repository.name)[@remote_relationship_name.to_s.singularize.to_sym]
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
DataMapper::Model.class_eval do
|
191
|
+
|
192
|
+
#HACK Determine the child key from the given repository, not the default one.
|
193
|
+
#
|
194
|
+
# Updates this method to use the hacked child_key method.
|
195
|
+
|
196
|
+
def properties_with_subclasses(repository_name = default_repository_name)
|
197
|
+
properties = DataMapper::PropertySet.new
|
198
|
+
([ self ].to_set + (respond_to?(:descendants) ? descendants : [])).each do |model|
|
199
|
+
model.relationships(repository_name).each_value { |relationship| relationship.child_key(repository_name) }
|
200
|
+
model.many_to_one_relationships.each do |relationship| relationship.child_key(repository_name) end
|
201
|
+
model.properties(repository_name).each do |property|
|
202
|
+
properties << property unless properties.has_property?(property.name)
|
203
|
+
end
|
204
|
+
end
|
205
|
+
properties
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
DataMapper::Associations::Relationship.class_eval do
|
210
|
+
|
211
|
+
#HACK Determine the child key from the given repository, not the default one.
|
212
|
+
#
|
213
|
+
# Updates this method to take a repository name. The child key will be
|
214
|
+
# determined from the properties scoped to the given repository.
|
215
|
+
#
|
216
|
+
# The @child_key class variable is changed to a hash that maps repository
|
217
|
+
# names to the appropriate key.
|
218
|
+
|
219
|
+
def child_key(repository_name=nil)
|
220
|
+
repository_name ||= repository.name
|
221
|
+
@child_key ||= Hash.new
|
222
|
+
@child_key[repository_name] ||= begin
|
223
|
+
child_key = nil
|
224
|
+
repository(repository_name).scope do |r|
|
225
|
+
model_properties = child_model.properties(repository_name)
|
226
|
+
|
227
|
+
child_key = parent_key(repository_name).zip(@child_properties || []).map do |parent_property,property_name|
|
228
|
+
# TODO: use something similar to DM::NamingConventions to determine the property name
|
229
|
+
parent_name = Extlib::Inflection.underscore(Extlib::Inflection.demodulize(parent_model.base_model.name))
|
230
|
+
property_name ||= "#{parent_name}_#{parent_property.name}".to_sym
|
231
|
+
|
232
|
+
if model_properties.has_property?(property_name)
|
233
|
+
model_properties[property_name]
|
234
|
+
else
|
235
|
+
options = {}
|
236
|
+
|
237
|
+
[ :length, :precision, :scale ].each do |option|
|
238
|
+
options[option] = parent_property.send(option)
|
239
|
+
end
|
240
|
+
|
241
|
+
# NOTE: hack to make each many to many child_key a true key,
|
242
|
+
# until I can figure out a better place for this check
|
243
|
+
if child_model.respond_to?(:many_to_many)
|
244
|
+
options[:key] = true
|
245
|
+
end
|
246
|
+
|
247
|
+
child_model.property(property_name, parent_property.primitive, options)
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
251
|
+
DataMapper::PropertySet.new(child_key)
|
252
|
+
end
|
253
|
+
return @child_key[repository_name]
|
254
|
+
end
|
255
|
+
|
256
|
+
#HACK Determine the parent key from the given repository, not the default one.
|
257
|
+
#
|
258
|
+
# Updates this method to take a repository name. The parent key will be
|
259
|
+
# determined from the properties scoped to the given repository.
|
260
|
+
#
|
261
|
+
# The @parent_key class variable is changed to a hash that maps repository
|
262
|
+
# names to the appropriate key.
|
263
|
+
|
264
|
+
def parent_key(repository_name=nil)
|
265
|
+
repository_name ||= repository.name
|
266
|
+
@parent_key ||= Hash.new
|
267
|
+
@parent_key[repository_name] ||= begin
|
268
|
+
parent_key = nil
|
269
|
+
repository(repository_name).scope do |r|
|
270
|
+
parent_key = if @parent_properties
|
271
|
+
parent_model.properties(repository_name).slice(*@parent_properties)
|
272
|
+
else
|
273
|
+
parent_model.key(repository_name)
|
274
|
+
end
|
275
|
+
end
|
276
|
+
DataMapper::PropertySet.new(parent_key)
|
277
|
+
end
|
278
|
+
return @parent_key[repository_name]
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
# Add a method to return all models defined for a repository.
|
283
|
+
|
284
|
+
DataMapper::Repository.class_eval do
|
285
|
+
def models
|
286
|
+
DataMapper::Resource.descendants.select { |cl| not cl.properties(name).empty? or not cl.relationships(name).empty? }
|
287
|
+
#HACK we are assuming that if a model has properties or relationships
|
288
|
+
# defined for a repository, then it must be contextual to that repo
|
289
|
+
end
|
290
|
+
end
|
@@ -0,0 +1,231 @@
|
|
1
|
+
# Defines the Autumn::Foliater class, which instantiates stems and leaves and
|
2
|
+
# keeps watch over their threads.
|
3
|
+
|
4
|
+
module Autumn
|
5
|
+
|
6
|
+
# Loads Stems and Leaves and executes them in their own threads. Manages the
|
7
|
+
# threads and oversees all leaves. This is a singleton class.
|
8
|
+
|
9
|
+
class Foliater
|
10
|
+
include Singleton
|
11
|
+
|
12
|
+
# The Speciator singleton.
|
13
|
+
attr_reader :config
|
14
|
+
# A hash of all Stem instances by their config names.
|
15
|
+
attr_reader :stems
|
16
|
+
# A hash of all Leaf instances by their config names.
|
17
|
+
attr_reader :leaves
|
18
|
+
|
19
|
+
def initialize # :nodoc:
|
20
|
+
@config = Speciator.instance
|
21
|
+
@stems = Hash.new
|
22
|
+
@leaves = Hash.new
|
23
|
+
@ctcp = Autumn::CTCP.new
|
24
|
+
end
|
25
|
+
|
26
|
+
# Loads the config files and their classes, initializes all stems and leaves
|
27
|
+
# and begins the stems' execution processes in their own threads. You must
|
28
|
+
# pass the stem and leaf config hashes (from the stems.yml and leaves.yml
|
29
|
+
# files).
|
30
|
+
#
|
31
|
+
# If +invoke+ is set to false, start_stems will not be called.
|
32
|
+
|
33
|
+
def load(stem_config, leaf_config, invoke=true)
|
34
|
+
load_configs stem_config, leaf_config
|
35
|
+
load_leaf_classes
|
36
|
+
load_leaves
|
37
|
+
load_all_leaf_models
|
38
|
+
load_stems
|
39
|
+
start_stems if invoke
|
40
|
+
end
|
41
|
+
|
42
|
+
# Reloads a leaf while it is running. Re-opens class definition files and
|
43
|
+
# runs them to redefine the classes. Does not work exactly as it should,
|
44
|
+
# but well enough for a rough hot-reload capability.
|
45
|
+
|
46
|
+
def hot_reload(leaf)
|
47
|
+
type = leaf.class.to_s.split('::').first
|
48
|
+
load_leaf_controller type
|
49
|
+
load_leaf_helpers type
|
50
|
+
load_leaf_models leaf
|
51
|
+
load_leaf_views type
|
52
|
+
end
|
53
|
+
|
54
|
+
# Returns true if there is at least one stem still running.
|
55
|
+
|
56
|
+
def alive?
|
57
|
+
@stem_threads and @stem_threads.any? { |name, thread| thread.alive? }
|
58
|
+
end
|
59
|
+
|
60
|
+
# This method yields each Stem that was loaded, allowing you to iterate over
|
61
|
+
# each stem. For instance, to take attendance:
|
62
|
+
#
|
63
|
+
# Foliater.instance.each_stem { |stem| stem.message "Here!" }
|
64
|
+
|
65
|
+
def each_stem
|
66
|
+
@leaves.each { |leaf| yield leaf }
|
67
|
+
end
|
68
|
+
|
69
|
+
# This method yields each Leaf subclass that was loaded, allowing you to
|
70
|
+
# iterate over each leaf. For instance, to take attendance:
|
71
|
+
#
|
72
|
+
# Foliater.instance.each_leaf { |leaf| leaf.stems.message "Here!" }
|
73
|
+
|
74
|
+
def each_leaf
|
75
|
+
@leaves.each { |leaf| yield leaf }
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
def load_configs(stem_config, leaf_config)
|
81
|
+
leaf_config.each do |name, options|
|
82
|
+
global_config_file = "#{AL_ROOT}/leaves/#{options['class'].snakecase}/config.yml"
|
83
|
+
if File.exist? global_config_file then
|
84
|
+
config.leaf name, YAML.load(File.open(global_config_file))
|
85
|
+
end
|
86
|
+
config.leaf name, options
|
87
|
+
config.leaf name, :logger => LogFacade.new(config.global(:logfile), 'Leaf', name)
|
88
|
+
end
|
89
|
+
stem_config.each do |name, options|
|
90
|
+
config.stem name, options
|
91
|
+
config.stem name, :logger => LogFacade.new(config.global(:logfile), 'Stem', name)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def load_leaf_classes
|
96
|
+
config.all_leaf_classes.each do |type|
|
97
|
+
Object.class_eval "module #{type}; end"
|
98
|
+
|
99
|
+
config.leaf type, :module => Object.const_get(type)
|
100
|
+
|
101
|
+
load_leaf_controller(type)
|
102
|
+
load_leaf_helpers(type)
|
103
|
+
load_leaf_views(type)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def load_leaf_controller(type)
|
108
|
+
controller_file = "#{AL_ROOT}/leaves/#{type.snakecase}/controller.rb"
|
109
|
+
raise "controller.rb file for leaf #{type} not found" unless File.exist? controller_file
|
110
|
+
controller_code = nil
|
111
|
+
begin
|
112
|
+
File.open("#{AL_ROOT}/leaves/#{type.snakecase}/controller.rb", 'r') { |f| controller_code = f.read }
|
113
|
+
rescue Errno::ENOENT
|
114
|
+
raise "controller.rb file for leaf #{type} not found"
|
115
|
+
end
|
116
|
+
config.leaf(type, :module).module_eval controller_code
|
117
|
+
end
|
118
|
+
|
119
|
+
def load_leaf_helpers(type)
|
120
|
+
mod = config.leaf(type, :module)
|
121
|
+
helper_code = nil
|
122
|
+
Dir.glob("#{AL_ROOT}/leaves/#{type.snakecase}/helpers/*.rb").each do |helper_file|
|
123
|
+
File.open(helper_file, 'r') { |f| helper_code = f.read }
|
124
|
+
mod.module_eval helper_code
|
125
|
+
end
|
126
|
+
|
127
|
+
leaf_class = nil
|
128
|
+
begin
|
129
|
+
leaf_class = mod.const_get('Controller')
|
130
|
+
rescue NameError
|
131
|
+
raise NameError, "Couldn't find Controller class for leaf #{type}"
|
132
|
+
end
|
133
|
+
|
134
|
+
config.leaf type, :helpers => Set.new
|
135
|
+
mod.constants.select { |const_name| const_name =~ /Helper$/ }.map { |helper_name| mod.const_get helper_name }.each do |helper|
|
136
|
+
config.leaf(type, :helpers) << helper
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def load_leaf_views(type)
|
141
|
+
views = Hash.new
|
142
|
+
view_text = nil
|
143
|
+
Dir.glob("#{AL_ROOT}/leaves/#{type.snakecase}/views/*.txt.erb").each do |view_file|
|
144
|
+
view_name = File.basename(view_file).match(/^(.+)\.txt\.erb$/)[1]
|
145
|
+
File.open(view_file, 'r') { |f| view_text = f.read }
|
146
|
+
views[view_name] = view_text
|
147
|
+
end
|
148
|
+
config.leaf type, :views => views
|
149
|
+
end
|
150
|
+
|
151
|
+
def load_leaves
|
152
|
+
config.each_leaf do |name, options|
|
153
|
+
options = config.options_for_leaf(name)
|
154
|
+
options[:root] = "#{config.global :root}/leaves/#{options[:class].snakecase}"
|
155
|
+
begin
|
156
|
+
leaf_class = options[:module].const_get('Controller')
|
157
|
+
rescue NameError
|
158
|
+
raise NameError, "Couldn't find Controller class for leaf #{name}"
|
159
|
+
end
|
160
|
+
@leaves[name] = leaf_class.new(options)
|
161
|
+
formatter = Autumn::Formatting.const_get options[:formatter].to_sym if options[:formatter] and (Autumn::Formatting.constants.include? options[:formatter] or Autumn::Formatting.constants.include? options[:formatter].to_sym)
|
162
|
+
formatter ||= Autumn::Formatting::DEFAULT
|
163
|
+
@leaves[name].extend formatter
|
164
|
+
options[:helpers].each { |helper| @leaves[name].extend helper }
|
165
|
+
# extend the formatter first so helper methods override its methods if necessary
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def load_all_leaf_models
|
170
|
+
@leaves.each { |name, instance| load_leaf_models instance }
|
171
|
+
end
|
172
|
+
|
173
|
+
def load_leaf_models(leaf)
|
174
|
+
model_code = nil
|
175
|
+
mod = config.leaf(leaf.options[:class], :module)
|
176
|
+
leaf.database do
|
177
|
+
Dir.glob("#{AL_ROOT}/leaves/#{leaf.options[:class].snakecase}/models/*.rb").each do |model_file|
|
178
|
+
File.open(model_file, 'r') { |f| model_code = f.read }
|
179
|
+
mod.module_eval model_code
|
180
|
+
end
|
181
|
+
# Need to manually set the table names of the models because we loaded
|
182
|
+
# them inside a module
|
183
|
+
unless $NO_DATABASE
|
184
|
+
mod.constants.map { |const_name| mod.const_get(const_name) }.select { |const| const.ancestors.include? DataMapper::Resource }.each do |model|
|
185
|
+
model.storage_names[leaf.database_name] = model.to_s.demodulize.snakecase.pluralize
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
def load_stems
|
192
|
+
config.each_stem do |name, options|
|
193
|
+
options = config.options_for_stem(name)
|
194
|
+
server = options[:server]
|
195
|
+
nick = options[:nick]
|
196
|
+
|
197
|
+
@stems[name] = Stem.new(server, nick, options)
|
198
|
+
leaves = options[:leaves]
|
199
|
+
leaves ||= [ options[:leaf] ]
|
200
|
+
leaves.each do |leaf|
|
201
|
+
raise "Unknown leaf #{leaf} in configuration for stem #{name}" unless @leaves[leaf]
|
202
|
+
@stems[name].add_listener @leaves[leaf]
|
203
|
+
@stems[name].add_listener @ctcp
|
204
|
+
#TODO a configurable way of specifying listeners to add by default
|
205
|
+
@leaves[leaf].stems << @stems[name]
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
def start_stems
|
211
|
+
@leaves.each { |name, leaf| leaf.preconfigure }
|
212
|
+
@leaves.each { |name, leaf| leaf.will_start_up }
|
213
|
+
@stem_threads = Hash.new
|
214
|
+
config.each_stem do |name, options|
|
215
|
+
@stem_threads[name] = Thread.new(@stems[name], Thread.current) do |stem, parent_thread|
|
216
|
+
# The thread will run the stem until it exits, then inform the main
|
217
|
+
# thread that it has exited. When the main thread wakes, it checks if
|
218
|
+
# all stems have terminated; if so, it terminates itself.
|
219
|
+
begin
|
220
|
+
stem.start
|
221
|
+
rescue
|
222
|
+
options[:logger].fatal $!
|
223
|
+
ensure
|
224
|
+
parent_thread.wakeup # Schedule the parent thread to wake up after this thread finishes
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
end
|
231
|
+
end
|