maccman-bowline 0.1.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.
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