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.
- data/History.txt +4 -0
- data/MIT-LICENSE +20 -0
- data/Manifest.txt +58 -0
- data/README.txt +113 -0
- data/Rakefile +24 -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 +42 -0
- data/examples/account_binder.rb +29 -0
- data/examples/example.js +24 -0
- data/examples/twitter.html +43 -0
- data/examples/twitter_binder.rb +40 -0
- data/examples/twitter_login.html +29 -0
- data/examples/users_binder.rb +39 -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 +13 -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 +49 -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 +70 -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 +21 -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.erb +23 -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 +11 -0
- data/templates/script/run +3 -0
- 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,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,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
|