maccman-bowline 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. data/History.txt +4 -0
  2. data/MIT-LICENSE +20 -0
  3. data/Manifest.txt +58 -0
  4. data/README.txt +113 -0
  5. data/Rakefile +24 -0
  6. data/assets/jquery.bowline.js +96 -0
  7. data/assets/jquery.chain.js +2348 -0
  8. data/assets/jquery.js +3549 -0
  9. data/bin/bowline-gen +6 -0
  10. data/bowline.gemspec +42 -0
  11. data/examples/account_binder.rb +29 -0
  12. data/examples/example.js +24 -0
  13. data/examples/twitter.html +43 -0
  14. data/examples/twitter_binder.rb +40 -0
  15. data/examples/twitter_login.html +29 -0
  16. data/examples/users_binder.rb +39 -0
  17. data/lib/bowline.rb +42 -0
  18. data/lib/bowline/binders.rb +177 -0
  19. data/lib/bowline/binders/collection.rb +27 -0
  20. data/lib/bowline/binders/singleton.rb +25 -0
  21. data/lib/bowline/commands/console.rb +27 -0
  22. data/lib/bowline/commands/generate.rb +1 -0
  23. data/lib/bowline/commands/run.rb +13 -0
  24. data/lib/bowline/ext/array.rb +5 -0
  25. data/lib/bowline/ext/class.rb +51 -0
  26. data/lib/bowline/ext/object.rb +12 -0
  27. data/lib/bowline/ext/string.rb +9 -0
  28. data/lib/bowline/gem_dependency.rb +42 -0
  29. data/lib/bowline/generators.rb +59 -0
  30. data/lib/bowline/generators/application.rb +49 -0
  31. data/lib/bowline/generators/binder.rb +25 -0
  32. data/lib/bowline/generators/migration.rb +51 -0
  33. data/lib/bowline/generators/model.rb +20 -0
  34. data/lib/bowline/initializer.rb +596 -0
  35. data/lib/bowline/jquery.rb +31 -0
  36. data/lib/bowline/observer.rb +43 -0
  37. data/lib/bowline/tasks/app.rake +70 -0
  38. data/lib/bowline/tasks/bowline.rb +8 -0
  39. data/lib/bowline/tasks/database.rake +167 -0
  40. data/lib/bowline/tasks/log.rake +9 -0
  41. data/lib/bowline/tasks/misk.rake +3 -0
  42. data/templates/Rakefile +7 -0
  43. data/templates/binder.rb +9 -0
  44. data/templates/config/application.yml +1 -0
  45. data/templates/config/boot.rb +21 -0
  46. data/templates/config/database.yml +4 -0
  47. data/templates/config/environment.rb +12 -0
  48. data/templates/config/manifest +18 -0
  49. data/templates/config/tiapp.xml +24 -0
  50. data/templates/gitignore +15 -0
  51. data/templates/migration.rb +7 -0
  52. data/templates/model.rb +4 -0
  53. data/templates/public/index.html.erb +23 -0
  54. data/templates/public/javascripts/application.js +0 -0
  55. data/templates/public/stylesheets/application.css +0 -0
  56. data/templates/script/console +3 -0
  57. data/templates/script/init +11 -0
  58. data/templates/script/run +3 -0
  59. metadata +143 -0
@@ -0,0 +1,27 @@
1
+ module Bowline
2
+ module Binders
3
+ class Collection < Base
4
+ cattr_accessor :items
5
+ class << self
6
+ def items=(args)
7
+ @@items = args
8
+ self.item_sync!
9
+ @@items
10
+ end
11
+
12
+ def item_sync!
13
+ return unless @@items && @@elements
14
+ @@elements.each {|i|
15
+ i.updateCollection(@@items.to_js)
16
+ }
17
+ end
18
+
19
+ def find(id)
20
+ @@items.find {|item|
21
+ item.id == id if item.respond_to?(:id)
22
+ }
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,25 @@
1
+ module Bowline
2
+ module Binders
3
+ class Singleton < Base
4
+ cattr_accessor :item
5
+ class << self
6
+ def item=(arg)
7
+ @@item = arg
8
+ self.item_sync!
9
+ end
10
+
11
+ def item_sync!
12
+ return unless @@item && @@elements
13
+ # Call the chain.js function 'item' on elements
14
+ @@elements.each {|i|
15
+ i.updateSingleton(@@item.to_js)
16
+ }
17
+ end
18
+
19
+ def find(*a)
20
+ @@item
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,27 @@
1
+ irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
2
+
3
+ require 'optparse'
4
+
5
+ options = { :irb => irb }
6
+ OptionParser.new do |opt|
7
+ opt.banner = "Usage: console [environment] [options]"
8
+ opt.on("--irb=[#{irb}]", 'Invoke a different irb.') { |v| options[:irb] = v }
9
+ opt.on("--debugger", 'Enable ruby-debugging for the console.') { |v| options[:debugger] = v }
10
+ opt.parse!(ARGV)
11
+ end
12
+
13
+ libs = " -r irb/completion"
14
+ libs << %( -r "#{APP_ROOT}/config/environment")
15
+
16
+ if options[:debugger]
17
+ begin
18
+ require 'ruby-debug'
19
+ libs << " -r ruby-debug"
20
+ puts "=> Debugger enabled"
21
+ rescue Exception
22
+ puts "You need to install ruby-debug to run the console in debugging mode. With gems, use 'gem install ruby-debug'"
23
+ exit
24
+ end
25
+ end
26
+
27
+ exec "#{options[:irb]} #{libs} --simple-prompt"
@@ -0,0 +1 @@
1
+ # todo
@@ -0,0 +1,13 @@
1
+ exec_path = File.join(APP_ROOT, 'build', 'osx', 'testapp.app')
2
+
3
+ unless File.exist?(exec_path)
4
+ require 'rake'
5
+ require 'bowline/tasks/bowline'
6
+ Rake::Task['app:bundle'].invoke
7
+ end
8
+
9
+ if ENV['debug']
10
+ `open #{File.join(exec_path, 'Contents', 'MacOS', 'testapp')}`
11
+ else
12
+ `open #{exec_path}`
13
+ end
@@ -0,0 +1,5 @@
1
+ class Array
2
+ def to_js(opts = {})
3
+ collect {|i| i.to_js(opts) }
4
+ end
5
+ end
@@ -0,0 +1,51 @@
1
+ # Extends the class object with class and instance accessors for class attributes,
2
+ # just like the native attr* accessors for instance attributes.
3
+ #
4
+ # class Person
5
+ # cattr_accessor :hair_colors
6
+ # end
7
+ #
8
+ # Person.hair_colors = [:brown, :black, :blonde, :red]
9
+ class Class
10
+ def cattr_reader(*syms)
11
+ syms.flatten.each do |sym|
12
+ next if sym.is_a?(Hash)
13
+ class_eval(<<-EOS, __FILE__, __LINE__)
14
+ unless defined? @@#{sym}
15
+ @@#{sym} = nil
16
+ end
17
+
18
+ def self.#{sym}
19
+ @@#{sym}
20
+ end
21
+
22
+ def #{sym}
23
+ @@#{sym}
24
+ end
25
+ EOS
26
+ end
27
+ end
28
+
29
+ def cattr_writer(*syms)
30
+ syms.flatten.each do |sym|
31
+ class_eval(<<-EOS, __FILE__, __LINE__)
32
+ unless defined? @@#{sym}
33
+ @@#{sym} = nil
34
+ end
35
+
36
+ def self.#{sym}=(obj)
37
+ @@#{sym} = obj
38
+ end
39
+
40
+ def #{sym}=(obj)
41
+ @@#{sym} = obj
42
+ end
43
+ EOS
44
+ end
45
+ end
46
+
47
+ def cattr_accessor(*syms)
48
+ cattr_reader(*syms)
49
+ cattr_writer(*syms)
50
+ end
51
+ end
@@ -0,0 +1,12 @@
1
+ class Object
2
+ # Aim is to convert the object in:
3
+ # * A hash or
4
+ # * An array of hashes
5
+ def to_js(opts = {})
6
+ if respond_to?(:attributes)
7
+ attributes
8
+ else
9
+ self
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,9 @@
1
+ class String
2
+ def underscore
3
+ self.gsub(/::/, '/').
4
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
5
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
6
+ tr("-", "_").
7
+ downcase
8
+ end
9
+ end
@@ -0,0 +1,42 @@
1
+ module Bowline
2
+ class GemDependency
3
+ attr_accessor :lib, :source
4
+
5
+ def initialize(name, options = {})
6
+ require 'rubygems' unless Object.const_defined?(:Gem)
7
+
8
+ if options[:requirement]
9
+ req = options[:requirement]
10
+ elsif options[:version]
11
+ req = Gem::Requirement.create(options[:version])
12
+ else
13
+ req = Gem::Requirement.default
14
+ end
15
+
16
+ @dep = Gem::Dependency.new(name, req)
17
+ @lib = options[:lib]
18
+ @source = options[:source]
19
+ end
20
+
21
+ def add_load_paths
22
+ end
23
+
24
+ def name
25
+ @dep.name.to_s
26
+ end
27
+
28
+ def requirement
29
+ r = @dep.version_requirements
30
+ (r == Gem::Requirement.default) ? nil : r
31
+ end
32
+
33
+ def load
34
+ return if @loaded
35
+ require(@lib || name) unless @lib == false
36
+ @loaded = true
37
+ rescue LoadError
38
+ puts $!.to_s
39
+ $!.backtrace.each { |b| puts b }
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,59 @@
1
+ gem 'templater', '>= 0.3.2'
2
+ require 'templater'
3
+
4
+ module Bowline
5
+ module Generators
6
+ extend Templater::Manifold
7
+
8
+ desc <<-DESC
9
+ Generate components for your application or entirely new applications.
10
+ DESC
11
+ class Generator < Templater::Generator
12
+ def with_modules(modules, options={}, &block)
13
+ indent = options[:indent] || 0
14
+ text = capture(&block)
15
+ modules.each_with_index do |mod, i|
16
+ concat((" " * (indent + i)) + "module #{mod}\n", block.binding)
17
+ end
18
+ text = text.to_a.map{ |line| (" " * modules.size) + line }.join
19
+ concat(text, block.binding)
20
+ modules.reverse.each_with_index do |mod, i|
21
+ concat((" " * (indent + modules.size - i - 1)) + "end # #{mod}\n", block.binding)
22
+ end
23
+ end
24
+
25
+ def self.source_root
26
+ File.join(File.dirname(__FILE__), *%w[.. .. templates])
27
+ end
28
+ end
29
+
30
+ class NamedGenerator < Generator
31
+ # NOTE: Currently this is not inherited, it will have to be
32
+ # declared in each generator that inherits from this.
33
+ first_argument :name, :required => true
34
+
35
+ def initialize(*args)
36
+ super
37
+ end
38
+
39
+ def class_name
40
+ name.gsub('-', '_').camel_case
41
+ end
42
+ alias_method :module_name, :class_name
43
+
44
+ def file_name
45
+ name.snake_case
46
+ end
47
+ alias_method :base_name, :file_name
48
+
49
+ def symbol_name
50
+ file_name.gsub('-', '_')
51
+ end
52
+ end
53
+ end
54
+ end
55
+
56
+ require "bowline/generators/application"
57
+ require "bowline/generators/binder"
58
+ require "bowline/generators/model"
59
+ require "bowline/generators/migration"
@@ -0,0 +1,49 @@
1
+ module Bowline::Generators
2
+ class ApplicationGenerator < NamedGenerator
3
+ desc <<-DESC
4
+ Generates a new application.
5
+ DESC
6
+
7
+ def app_id
8
+ ['bowline', name].join('.')
9
+ end
10
+
11
+ def destination_root
12
+ File.join(@destination_root, base_name)
13
+ end
14
+
15
+ first_argument :name, :required => true, :desc => "application name"
16
+
17
+ empty_directory :tmp, "tmp"
18
+ empty_directory :vendor, "vendor"
19
+ empty_directory :lib, "lib"
20
+ empty_directory :db, "db"
21
+ empty_directory :build, "build"
22
+ empty_directory :log, "log"
23
+
24
+ template :rakefile, "Rakefile", "Rakefile"
25
+
26
+ file :gitignore, "gitignore", ".gitignore"
27
+
28
+ glob! "script"
29
+ glob! "public"
30
+
31
+ file :jquery, "../assets/jquery.js", "public/javascripts/jquery.js"
32
+ file :chainjs, "../assets/jquery.chain.js", "public/javascripts/jquery.chain.js"
33
+ file :bowlinejs, "../assets/jquery.bowline.js", "public/javascripts/jquery.bowline.js"
34
+
35
+ empty_directory :app, "app"
36
+ empty_directory :models, "app/models"
37
+ empty_directory :binders, "app/binders"
38
+ empty_directory :config, "config"
39
+
40
+ template :environment, "config/environment.rb", "config/environment.rb"
41
+ template :tiapp, "config/tiapp.xml", "config/tiapp.xml"
42
+ ["application.yml", "database.yml", "manifest", "boot.rb"].each {|action|
43
+ action = File.join('config', action)
44
+ file(action.downcase.gsub(/[^a-z0-9]+/, '_').to_sym, action, action)
45
+ }
46
+ end
47
+
48
+ add :app, ApplicationGenerator
49
+ end
@@ -0,0 +1,25 @@
1
+ module Bowline::Generators
2
+ class BinderGenerator < NamedGenerator
3
+ desc <<-DESC
4
+ Generates a new binder, either a collection one, or a singleton one.
5
+ DESC
6
+
7
+ def class_name
8
+ super + " < Bowline::Binders::#{type.to_s.camel_case}"
9
+ end
10
+
11
+ def modules
12
+ ['Binders']
13
+ end
14
+
15
+ first_argument :name, :required => true, :desc => "binder name"
16
+ option :type, :desc => "Binder type (collection/singleton)", :default => "collection"
17
+
18
+ template :binder do |template|
19
+ template.source = "binder.rb"
20
+ template.destination = "app/binders/#{file_name}.rb"
21
+ end
22
+ end
23
+
24
+ add :binder, BinderGenerator
25
+ end
@@ -0,0 +1,51 @@
1
+ module Bowline::Generators
2
+ class MigrationGenerator < Generator
3
+ desc <<-DESC
4
+ Generates a new database migration.
5
+ DESC
6
+
7
+ option :model, :as => :boolean, :desc => 'Specify this option to generate a migration which creates a table for the provided model'
8
+
9
+ first_argument :name, :required => true
10
+ second_argument :attributes, :as => :hash, :default => {}
11
+
12
+ def table_name
13
+ self.name.snake_case.pluralize
14
+ end
15
+
16
+ def class_name
17
+ "#{self.name.camel_case}Migration"
18
+ end
19
+
20
+ def migration_name
21
+ self.name.snake_case
22
+ end
23
+
24
+ def file_name
25
+ "#{version}_#{migration_name}_migration"
26
+ end
27
+
28
+ def version
29
+ # TODO: handle ActiveRecord timestamped migrations
30
+ n = options[:delete] ? current_migration_nr : current_migration_nr + 1
31
+ format("%03d", n)
32
+ end
33
+
34
+ template :migration do |template|
35
+ template.source = "migration.rb"
36
+ template.destination = "db/migrate/#{file_name}.rb"
37
+ end
38
+
39
+ protected
40
+ def destination_directory
41
+ File.join(destination_root, 'schema', 'migrations')
42
+ end
43
+
44
+ def current_migration_nr
45
+ current_migration_number = Dir["#{destination_directory}/*"].map do |f|
46
+ File.basename(f).match(/^(\d+)/)[0].to_i
47
+ end.max.to_i
48
+ end
49
+ end
50
+ add :migration, MigrationGenerator
51
+ end
@@ -0,0 +1,20 @@
1
+ module Bowline::Generators
2
+ class ModelGenerator < NamedGenerator
3
+ desc <<-DESC
4
+ Generates a new model.
5
+ DESC
6
+
7
+ def modules
8
+ []
9
+ end
10
+
11
+ first_argument :name, :required => true, :desc => "model name"
12
+
13
+ template :model do |template|
14
+ template.source = "model.rb"
15
+ template.destination = "app/models/#{file_name}.rb"
16
+ end
17
+ end
18
+
19
+ add :model, ModelGenerator
20
+ end
@@ -0,0 +1,596 @@
1
+ require 'logger'
2
+ require 'set'
3
+ require 'pathname'
4
+
5
+ module Bowline
6
+ class << self
7
+ # The Configuration instance used to configure the Bowline environment
8
+ def configuration
9
+ @@configuration
10
+ end
11
+
12
+ def configuration=(configuration)
13
+ @@configuration = configuration
14
+ end
15
+
16
+ def initialized?
17
+ @initialized || false
18
+ end
19
+
20
+ def initialized=(initialized)
21
+ @initialized ||= initialized
22
+ end
23
+
24
+ def logger
25
+ if defined?(BOWLINE_LOGGER)
26
+ BOWLINE_LOGGER
27
+ else
28
+ nil
29
+ end
30
+ end
31
+
32
+ def root
33
+ Pathname.new(APP_ROOT) if defined?(APP_ROOT)
34
+ end
35
+ end
36
+
37
+ class Initializer
38
+ # The Configuration instance used by this Initializer instance.
39
+ attr_reader :configuration
40
+
41
+ # Runs the initializer. By default, this will invoke the #process method,
42
+ # which simply executes all of the initialization routines. Alternately,
43
+ # you can specify explicitly which initialization routine you want:
44
+ #
45
+ # Bowline::Initializer.run(:set_load_path)
46
+ #
47
+ # This is useful if you only want the load path initialized, without
48
+ # incurring the overhead of completely loading the entire environment.
49
+ def self.run(command = :process, configuration = Configuration.new)
50
+ yield configuration if block_given?
51
+ initializer = new configuration
52
+ initializer.send(command)
53
+ initializer
54
+ end
55
+
56
+ # Create a new Initializer instance that references the given Configuration
57
+ # instance.
58
+ def initialize(configuration)
59
+ @configuration = configuration
60
+ @loaded_plugins = []
61
+ end
62
+
63
+ def require_frameworks
64
+ configuration.frameworks.each { |framework| require(framework.to_s) }
65
+ end
66
+
67
+ def set_load_path
68
+ load_paths = configuration.load_paths + configuration.framework_paths
69
+ load_paths.reverse_each { |dir| $LOAD_PATH.unshift(dir) if File.directory?(dir) }
70
+ $LOAD_PATH.uniq!
71
+ end
72
+
73
+ # Set the paths from which Bowline will automatically load source files, and
74
+ # the load_once paths.
75
+ def set_autoload_paths
76
+ ActiveSupport::Dependencies.load_paths = configuration.load_paths.uniq
77
+ ActiveSupport::Dependencies.load_once_paths = configuration.load_once_paths.uniq
78
+
79
+ extra = ActiveSupport::Dependencies.load_once_paths - ActiveSupport::Dependencies.load_paths
80
+ unless extra.empty?
81
+ abort <<-end_error
82
+ load_once_paths must be a subset of the load_paths.
83
+ Extra items in load_once_paths: #{extra * ','}
84
+ end_error
85
+ end
86
+
87
+ # Freeze the arrays so future modifications will fail rather than do nothing mysteriously
88
+ configuration.load_once_paths.freeze
89
+ end
90
+
91
+ def add_plugin_load_paths
92
+ Dir.glob(File.join(configuration.plugin_glob, 'lib')).sort.each do |path|
93
+ $LOAD_PATH << path
94
+ ActiveSupport::Dependencies.load_paths << path
95
+ unless configuration.reload_plugins?
96
+ ActiveSupport::Dependencies.load_once_paths << path
97
+ end
98
+ end
99
+ $LOAD_PATH.uniq!
100
+ end
101
+
102
+ def initialize_database
103
+ if defined?(ActiveRecord)
104
+ ActiveRecord::Base.establish_connection(configuration.database_configuration)
105
+ end
106
+ end
107
+
108
+ def initialize_logger
109
+ # if the environment has explicitly defined a logger, use it
110
+ return if Bowline.logger
111
+
112
+ unless logger = configuration.logger
113
+ begin
114
+ logger = ActiveSupport::BufferedLogger.new(configuration.log_path)
115
+ logger.level = ActiveSupport::BufferedLogger.const_get(configuration.log_level.to_s.upcase)
116
+ rescue StandardError => e
117
+ logger = ActiveSupport::BufferedLogger.new(STDERR)
118
+ logger.level = ActiveSupport::BufferedLogger::WARN
119
+ logger.warn(
120
+ "Bowline Error: Unable to access log file. Please ensure that #{configuration.log_path} exists and is chmod 0666. " +
121
+ "The log level has been raised to WARN and the output directed to STDERR until the problem is fixed."
122
+ )
123
+ end
124
+ end
125
+
126
+ silence_warnings { Object.const_set "BOWLINE_LOGGER", logger }
127
+ end
128
+
129
+ def initialize_framework_logging
130
+ ActiveRecord::Base.logger ||= Bowline.logger if defined?(ActiveRecord)
131
+ ActiveSupport::Dependencies.logger ||= Bowline.logger
132
+ end
133
+
134
+ # Loads support for "whiny nil" (noisy warnings when methods are invoked
135
+ # on +nil+ values) if Configuration#whiny_nils is true.
136
+ def initialize_whiny_nils
137
+ require('active_support/whiny_nil') if configuration.whiny_nils
138
+ end
139
+
140
+ # Sets the default value for Time.zone, and turns on ActiveRecord::Base#time_zone_aware_attributes.
141
+ # If assigned value cannot be matched to a TimeZone, an exception will be raised.
142
+ def initialize_time_zone
143
+ if configuration.time_zone
144
+ zone_default = Time.__send__(:get_zone, configuration.time_zone)
145
+
146
+ unless zone_default
147
+ raise \
148
+ 'Value assigned to config.time_zone not recognized.' +
149
+ 'Run "rake -D time" for a list of tasks for finding appropriate time zone names.'
150
+ end
151
+
152
+ Time.zone_default = zone_default
153
+
154
+ if defined?(ActiveRecord)
155
+ ActiveRecord::Base.time_zone_aware_attributes = true
156
+ ActiveRecord::Base.default_timezone = :utc
157
+ end
158
+ end
159
+ end
160
+
161
+ def initialize_framework_settings
162
+ (configuration.frameworks - [:active_support]).each do |framework|
163
+ base_class = framework.to_s.camelize.constantize.const_get("Base")
164
+ settings = configuration.send(framework)
165
+ next if !settings
166
+ settings.each do |setting, value|
167
+ base_class.send("#{setting}=", value)
168
+ end
169
+ end
170
+ configuration.active_support.each do |setting, value|
171
+ ActiveSupport.send("#{setting}=", value)
172
+ end
173
+ end
174
+
175
+ def initialize_rubygems
176
+ # todo - use custom rubygems on deployment
177
+ # $LOAD_PATH << File.join(root, 'ruby', 'rubygems')
178
+ # ::GEM_DIR = File.join(root, 'ruby', 'gems')
179
+ # $LOAD_PATH << GEM_DIR
180
+ # ENV['GEM_HOME'] = GEM_DIR
181
+ # require 'rubygems'
182
+ # Gem.use_paths(GEM_DIR, [GEM_DIR])
183
+ # Gem.source_index.refresh!
184
+ end
185
+
186
+ def add_gem_load_paths
187
+ unless configuration.gems.empty?
188
+ configuration.gems.each { |gem| gem.add_load_paths }
189
+ end
190
+ end
191
+
192
+ def load_gems
193
+ configuration.gems.each { |gem| gem.load }
194
+ end
195
+
196
+ def load_plugins
197
+ Dir.glob(File.join(configuration.plugin_glob, 'init.rb')).sort.each do |path|
198
+ config = configuration # Need local config variable
199
+ eval(IO.read(path), binding, path)
200
+ end
201
+ end
202
+
203
+ def load_application_initializers
204
+ Dir.glob(configuration.initializer_glob).sort.each do |initializer|
205
+ load(initializer)
206
+ end
207
+ end
208
+
209
+ def after_initialize
210
+ configuration.after_initialize_blocks.each do |block|
211
+ block.call
212
+ end
213
+ end
214
+
215
+ def load_application_classes
216
+ if configuration.cache_classes
217
+ configuration.eager_load_paths.each do |load_path|
218
+ matcher = /\A#{Regexp.escape(load_path)}(.*)\.rb\Z/
219
+ Dir.glob("#{load_path}/**/*.rb").sort.each do |file|
220
+ require_dependency file.sub(matcher, '\1')
221
+ end
222
+ end
223
+ end
224
+ end
225
+
226
+ # For Ruby 1.8, this initialization sets $KCODE to 'u' to enable the
227
+ # multibyte safe operations. Plugin authors supporting other encodings
228
+ # should override this behaviour and set the relevant +default_charset+
229
+ # on ActionController::Base.
230
+ #
231
+ # For Ruby 1.9, this does nothing. Specify the default encoding in the Ruby
232
+ # shebang line if you don't want UTF-8.
233
+ def initialize_encoding
234
+ $KCODE='u' if RUBY_VERSION < '1.9'
235
+ end
236
+
237
+ def initialize_name
238
+ unless configuration.name
239
+ raise "You must provide an application name in environment.rb"
240
+ end
241
+ silence_warnings { Object.const_set "APP_NAME", configuration.name }
242
+ end
243
+
244
+ def load_app_config
245
+ app_config = configuration.app_config
246
+ return unless app_config
247
+ Object.const_set("AppConfig", Class.new {
248
+ app_config.keys.each do |key|
249
+ cattr_accessor key
250
+ send("#{key}=", app_config[key])
251
+ end
252
+ })
253
+ end
254
+
255
+ def process
256
+ Bowline.configuration = configuration
257
+
258
+ set_load_path
259
+ initialize_rubygems
260
+ add_gem_load_paths
261
+
262
+ require_frameworks
263
+ set_autoload_paths
264
+ add_plugin_load_paths
265
+
266
+ initialize_encoding
267
+ initialize_database
268
+
269
+ initialize_logger
270
+ initialize_framework_logging
271
+
272
+ initialize_whiny_nils
273
+
274
+ initialize_time_zone
275
+
276
+ initialize_framework_settings
277
+
278
+ initialize_name
279
+ load_app_config
280
+
281
+ load_gems
282
+ load_plugins
283
+
284
+ load_application_initializers
285
+
286
+ after_initialize
287
+
288
+ load_application_classes
289
+
290
+ Bowline.initialized = true
291
+ end
292
+
293
+ end
294
+
295
+ # The Configuration class holds all the parameters for the Initializer and
296
+ # ships with defaults that suites most Bowline applications. But it's possible
297
+ # to overwrite everything. Usually, you'll create an Configuration file
298
+ # implicitly through the block running on the Initializer, but it's also
299
+ # possible to create the Configuration instance in advance and pass it in
300
+ # like this:
301
+ #
302
+ # config = Bowline::Configuration.new
303
+ # Bowline::Initializer.run(:process, config)
304
+ class Configuration
305
+ # The application's base directory.
306
+ attr_reader :root_path
307
+
308
+ # A stub for setting options on ActiveRecord::Base.
309
+ attr_accessor :active_record
310
+
311
+ # A stub for setting options on ActiveResource::Base.
312
+ attr_accessor :active_resource
313
+
314
+ # A stub for setting options on ActiveSupport.
315
+ attr_accessor :active_support
316
+
317
+ attr_accessor :bowline
318
+
319
+ attr_accessor :frameworks
320
+
321
+ attr_accessor :framework_paths
322
+
323
+ # Whether or not classes should be cached (set to false if you want
324
+ # application classes to be reloaded on each request)
325
+ attr_accessor :cache_classes
326
+
327
+ attr_accessor :binder_paths
328
+
329
+ # The path to the database configuration file to use. (Defaults to
330
+ # <tt>config/database.yml</tt>.)
331
+ attr_accessor :database_configuration_file
332
+
333
+ attr_accessor :app_config_file
334
+
335
+ # An array of additional paths to prepend to the load path. By default,
336
+ # all +app+, +lib+, +vendor+ and mock paths are included in this list.
337
+ attr_accessor :load_paths
338
+
339
+ # An array of paths from which Bowline will automatically load from only once.
340
+ # All elements of this array must also be in +load_paths+.
341
+ attr_accessor :load_once_paths
342
+
343
+ # An array of paths from which Bowline will eager load on boot if cache
344
+ # classes is enabled. All elements of this array must also be in
345
+ # +load_paths+.
346
+ attr_accessor :eager_load_paths
347
+
348
+ # The log level to use for the default Bowline logger.
349
+ attr_accessor :log_level
350
+
351
+ # The path to the log file to use. Defaults to log/#{environment}.log
352
+ # (e.g. log/development.log or log/production.log).
353
+ attr_accessor :log_path
354
+
355
+ # The specific logger to use. By default, a logger will be created and
356
+ # initialized using #log_path and #log_level, but a programmer may
357
+ # specifically set the logger to use via this accessor and it will be
358
+ # used directly.
359
+ attr_accessor :logger
360
+
361
+ # Set to +true+ if you want to be warned (noisily) when you try to invoke
362
+ # any method of +nil+. Set to +false+ for the standard Ruby behavior.
363
+ attr_accessor :whiny_nils
364
+
365
+ attr_accessor :reload_plugins
366
+ # Returns true if plugin reloading is enabled.
367
+ def reload_plugins?
368
+ !!@reload_plugins
369
+ end
370
+
371
+ # An array of gems that this Bowline application depends on. Bowline will automatically load
372
+ # these gems during installation, and allow you to install any missing gems with:
373
+ #
374
+ # rake gems:install
375
+ #
376
+ # You can add gems with the #gem method.
377
+ attr_accessor :gems
378
+
379
+ # Adds a single Gem dependency to the Bowline application. By default, it will require
380
+ # the library with the same name as the gem. Use :lib to specify a different name.
381
+ #
382
+ # # gem 'aws-s3', '>= 0.4.0'
383
+ # # require 'aws/s3'
384
+ # config.gem 'aws-s3', :lib => 'aws/s3', :version => '>= 0.4.0', \
385
+ # :source => "http://code.whytheluckystiff.net"
386
+ #
387
+ # To require a library be installed, but not attempt to load it, pass :lib => false
388
+ #
389
+ # config.gem 'qrp', :version => '0.4.1', :lib => false
390
+ def gem(name, options = {})
391
+ # todo
392
+ @gems << Bowline::GemDependency.new(name, options)
393
+ end
394
+
395
+ # Sets the default +time_zone+. Setting this will enable +time_zone+
396
+ # awareness for Active Record models and set the Active Record default
397
+ # timezone to <tt>:utc</tt>.
398
+ attr_accessor :time_zone
399
+
400
+ attr_accessor :plugin_glob
401
+
402
+ attr_accessor :initializer_glob
403
+
404
+ attr_accessor :name
405
+
406
+ # Create a new Configuration instance, initialized with the default
407
+ # values.
408
+ def initialize
409
+ set_root_path!
410
+
411
+ self.frameworks = default_frameworks
412
+ self.framework_paths = default_framework_paths
413
+ self.load_paths = default_load_paths
414
+ self.load_once_paths = default_load_once_paths
415
+ self.eager_load_paths = default_eager_load_paths
416
+ self.log_path = default_log_path
417
+ self.log_level = default_log_level
418
+ self.binder_paths = default_binder_paths
419
+ self.cache_classes = default_cache_classes
420
+ self.whiny_nils = default_whiny_nils
421
+ self.database_configuration_file = default_database_configuration_file
422
+ self.app_config_file = default_app_config_file
423
+ self.gems = default_gems
424
+ self.plugin_glob = default_plugin_glob
425
+ self.initializer_glob = default_initalizer_glob
426
+
427
+ for framework in default_frameworks
428
+ self.send("#{framework}=", Bowline::OrderedOptions.new)
429
+ end
430
+ end
431
+
432
+ # Set the root_path to APP_ROOT and canonicalize it.
433
+ def set_root_path!
434
+ raise 'APP_ROOT is not set' unless defined?(::APP_ROOT)
435
+ raise 'APP_ROOT is not a directory' unless File.directory?(::APP_ROOT)
436
+
437
+ @root_path =
438
+ # Pathname is incompatible with Windows, but Windows doesn't have
439
+ # real symlinks so File.expand_path is safe.
440
+ if RUBY_PLATFORM =~ /(:?mswin|mingw)/
441
+ File.expand_path(::APP_ROOT)
442
+
443
+ # Otherwise use Pathname#realpath which respects symlinks.
444
+ else
445
+ Pathname.new(::APP_ROOT).realpath.to_s
446
+ end
447
+
448
+ Object.const_set(:RELATIVE_APP_ROOT, ::APP_ROOT.dup) unless defined?(::RELATIVE_APP_ROOT)
449
+ ::APP_ROOT.replace @root_path
450
+ end
451
+
452
+ def app_config
453
+ require 'erb'
454
+ YAML::load(ERB.new(IO.read(app_config_file)).result) if File.exists?(app_config_file)
455
+ end
456
+
457
+ # Loads and returns the contents of the #database_configuration_file. The
458
+ # contents of the file are processed via ERB before being sent through
459
+ # YAML::load.
460
+ def database_configuration
461
+ require 'erb'
462
+ YAML::load(ERB.new(IO.read(database_configuration_file)).result) if File.exists?(database_configuration_file)
463
+ end
464
+
465
+ # Adds a block which will be executed after bowline has been fully initialized.
466
+ # Useful for per-environment configuration which depends on the framework being
467
+ # fully initialized.
468
+ def after_initialize(&after_initialize_block)
469
+ after_initialize_blocks << after_initialize_block if after_initialize_block
470
+ end
471
+
472
+ # Returns the blocks added with Configuration#after_initialize
473
+ def after_initialize_blocks
474
+ @after_initialize_blocks ||= []
475
+ end
476
+
477
+ private
478
+
479
+ def default_frameworks
480
+ [:active_support, :bowline]
481
+ end
482
+
483
+ def default_framework_paths
484
+ [
485
+ File.join(root_path, 'vendor', 'bowline', 'lib'),
486
+ File.join(root_path, 'vendor', 'rails', 'activesupport', 'lib'),
487
+ File.join(root_path, 'vendor', 'rails', 'activerecord', 'lib'),
488
+ File.join(root_path, 'vendor', 'rails', 'activeresource', 'lib')
489
+ ]
490
+ end
491
+
492
+ def default_load_paths
493
+ paths = []
494
+
495
+ # Followed by the standard includes.
496
+ paths.concat %w(
497
+ app
498
+ app/binders
499
+ app/models
500
+ app/remote
501
+ lib
502
+ vendor
503
+ ).map { |dir| "#{root_path}/#{dir}" }.select { |dir| File.directory?(dir) }
504
+
505
+ paths
506
+ end
507
+
508
+ # Doesn't matter since plugins aren't in load_paths yet.
509
+ def default_load_once_paths
510
+ []
511
+ end
512
+
513
+ def default_eager_load_paths
514
+ %w(
515
+ app/models
516
+ app/remote
517
+ app/binders
518
+ ).map { |dir| "#{root_path}/#{dir}" }.select { |dir| File.directory?(dir) }
519
+ end
520
+
521
+ def default_log_path
522
+ File.join(root_path, 'log', "application.log")
523
+ end
524
+
525
+ def default_log_level
526
+ :info
527
+ end
528
+
529
+ def default_database_configuration_file
530
+ File.join(root_path, 'config', 'database.yml')
531
+ end
532
+
533
+ def default_app_config_file
534
+ File.join(root_path, 'config', 'application.yml')
535
+ end
536
+
537
+ def default_binder_paths
538
+ File.join(root_path, 'app', 'binders')
539
+ end
540
+
541
+ def default_cache_classes
542
+ true
543
+ end
544
+
545
+ def default_whiny_nils
546
+ false
547
+ end
548
+
549
+ def default_gems
550
+ []
551
+ end
552
+
553
+ def default_plugin_glob
554
+ File.join(root_path, *%w{ vendor plugins * })
555
+ end
556
+
557
+ def default_initalizer_glob
558
+ File.join(root_path, *%w{ config initializers **/*.rb })
559
+ end
560
+ end
561
+ end
562
+
563
+ # Needs to be duplicated from Active Support since its needed before Active
564
+ # Support is available. Here both Options and Hash are namespaced to prevent
565
+ # conflicts with other implementations AND with the classes residing in Active Support.
566
+ class Bowline::OrderedOptions < Array #:nodoc:
567
+ def []=(key, value)
568
+ key = key.to_sym
569
+
570
+ if pair = find_pair(key)
571
+ pair.pop
572
+ pair << value
573
+ else
574
+ self << [key, value]
575
+ end
576
+ end
577
+
578
+ def [](key)
579
+ pair = find_pair(key.to_sym)
580
+ pair ? pair.last : nil
581
+ end
582
+
583
+ def method_missing(name, *args)
584
+ if name.to_s =~ /(.*)=$/
585
+ self[$1.to_sym] = args.first
586
+ else
587
+ self[name]
588
+ end
589
+ end
590
+
591
+ private
592
+ def find_pair(key)
593
+ self.each { |i| return i if i.first == key }
594
+ return false
595
+ end
596
+ end