bowline 0.1.6
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/History.txt +4 -0
- data/MIT-LICENSE +20 -0
- data/Manifest.txt +58 -0
- data/README.txt +136 -0
- data/Rakefile +25 -0
- data/assets/jquery.bowline.js +96 -0
- data/assets/jquery.chain.js +2348 -0
- data/assets/jquery.js +3549 -0
- data/bin/bowline-gen +6 -0
- data/bowline.gemspec +45 -0
- data/examples/account.rb +31 -0
- data/examples/example.js +24 -0
- data/examples/tweets.rb +28 -0
- data/examples/twitter.html +39 -0
- data/examples/users.rb +37 -0
- data/lib/bowline.rb +42 -0
- data/lib/bowline/binders.rb +177 -0
- data/lib/bowline/binders/collection.rb +27 -0
- data/lib/bowline/binders/singleton.rb +25 -0
- data/lib/bowline/commands/console.rb +27 -0
- data/lib/bowline/commands/generate.rb +1 -0
- data/lib/bowline/commands/run.rb +11 -0
- data/lib/bowline/ext/array.rb +5 -0
- data/lib/bowline/ext/class.rb +51 -0
- data/lib/bowline/ext/object.rb +12 -0
- data/lib/bowline/ext/string.rb +9 -0
- data/lib/bowline/gem_dependency.rb +42 -0
- data/lib/bowline/generators.rb +59 -0
- data/lib/bowline/generators/application.rb +58 -0
- data/lib/bowline/generators/binder.rb +25 -0
- data/lib/bowline/generators/migration.rb +51 -0
- data/lib/bowline/generators/model.rb +20 -0
- data/lib/bowline/initializer.rb +596 -0
- data/lib/bowline/jquery.rb +31 -0
- data/lib/bowline/observer.rb +43 -0
- data/lib/bowline/tasks/app.rake +90 -0
- data/lib/bowline/tasks/bowline.rb +8 -0
- data/lib/bowline/tasks/database.rake +167 -0
- data/lib/bowline/tasks/log.rake +9 -0
- data/lib/bowline/tasks/misk.rake +3 -0
- data/templates/Rakefile +7 -0
- data/templates/binder.rb +9 -0
- data/templates/config/application.yml +1 -0
- data/templates/config/boot.rb +13 -0
- data/templates/config/database.yml +4 -0
- data/templates/config/environment.rb +12 -0
- data/templates/config/manifest +18 -0
- data/templates/config/tiapp.xml +24 -0
- data/templates/gitignore +15 -0
- data/templates/migration.rb +7 -0
- data/templates/model.rb +4 -0
- data/templates/public/index.html +25 -0
- data/templates/public/javascripts/application.js +0 -0
- data/templates/public/stylesheets/application.css +0 -0
- data/templates/script/console +3 -0
- data/templates/script/init +18 -0
- data/templates/script/run +3 -0
- metadata +155 -0
@@ -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,11 @@
|
|
1
|
+
require "config/environment"
|
2
|
+
exec_path = File.join(APP_ROOT, 'build', 'osx', "#{APP_NAME}.app")
|
3
|
+
|
4
|
+
unless File.exist?(exec_path)
|
5
|
+
require 'rake'
|
6
|
+
require 'bowline/tasks/bowline'
|
7
|
+
Rake::Task['app:bundle'].invoke
|
8
|
+
end
|
9
|
+
|
10
|
+
# Debug view
|
11
|
+
`open #{File.join(exec_path, 'Contents', 'MacOS', APP_NAME)}`
|
@@ -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,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,58 @@
|
|
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
|
+
# Todo - only works relative
|
13
|
+
File.join(@destination_root, base_name)
|
14
|
+
end
|
15
|
+
|
16
|
+
def full_name
|
17
|
+
File.basename(name).camel_case
|
18
|
+
end
|
19
|
+
|
20
|
+
first_argument :name, :required => true, :desc => "application name"
|
21
|
+
|
22
|
+
empty_directory :tmp, "tmp"
|
23
|
+
empty_directory :vendor, "vendor"
|
24
|
+
empty_directory :lib, "lib"
|
25
|
+
empty_directory :db, "db"
|
26
|
+
empty_directory :build, "build"
|
27
|
+
empty_directory :log, "log"
|
28
|
+
|
29
|
+
template :rakefile, "Rakefile", "Rakefile"
|
30
|
+
|
31
|
+
file :gitignore, "gitignore", ".gitignore"
|
32
|
+
|
33
|
+
empty_directory :public, "public"
|
34
|
+
template :index, "public/index.html", "public/index.html"
|
35
|
+
glob! "public/javascripts"
|
36
|
+
glob! "public/stylesheets"
|
37
|
+
|
38
|
+
glob! "script"
|
39
|
+
|
40
|
+
file :jquery, "../assets/jquery.js", "public/javascripts/jquery.js"
|
41
|
+
file :chainjs, "../assets/jquery.chain.js", "public/javascripts/jquery.chain.js"
|
42
|
+
file :bowlinejs, "../assets/jquery.bowline.js", "public/javascripts/jquery.bowline.js"
|
43
|
+
|
44
|
+
empty_directory :app, "app"
|
45
|
+
empty_directory :models, "app/models"
|
46
|
+
empty_directory :binders, "app/binders"
|
47
|
+
empty_directory :config, "config"
|
48
|
+
|
49
|
+
template :environment, "config/environment.rb", "config/environment.rb"
|
50
|
+
template :tiapp, "config/tiapp.xml", "config/tiapp.xml"
|
51
|
+
["application.yml", "database.yml", "manifest", "boot.rb"].each {|action|
|
52
|
+
action = File.join('config', action)
|
53
|
+
file(action.downcase.gsub(/[^a-z0-9]+/, '_').to_sym, action, action)
|
54
|
+
}
|
55
|
+
end
|
56
|
+
|
57
|
+
add :app, ApplicationGenerator
|
58
|
+
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
|