milkshake 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,190 @@
1
+
2
+ begin
3
+ require 'thor'
4
+ rescue LoadError
5
+ require 'rubygems'
6
+ retry
7
+ end
8
+
9
+ require 'pathname'
10
+
11
+ module Milkshake
12
+ class App < Thor
13
+
14
+ autoload :Helpers, 'milkshake/app/helpers'
15
+ autoload :Actions, 'milkshake/app/actions'
16
+ autoload :Defaults, 'milkshake/app/defaults'
17
+
18
+ include Helpers
19
+ include Actions
20
+ extend Defaults
21
+
22
+ class_option :app, :default => '.',
23
+ :desc => 'Path to the rails application',
24
+ :banner => 'rails_root', :type => :string,
25
+ :group => 'Global'
26
+
27
+ class_option :environment, :default => self.default_environment,
28
+ :desc => 'Rails environment',
29
+ :banner => 'env', :type => :string,
30
+ :group => 'Global'
31
+
32
+ desc 'info', 'list all loaded gems'
33
+ def info
34
+ goto_rails do
35
+ load_environment!
36
+
37
+ puts "Loaded gems:"
38
+ data = Milkshake.environment.gemspecs.collect { |gemspec| [gemspec.name, gemspec.version] }
39
+ shell.print_table(data)
40
+ end
41
+ end
42
+
43
+ desc 'create.app PATH', 'create a new rails app.'
44
+ method_option :'git-data', :default => false,
45
+ :desc => 'Initialize git for the shared directory',
46
+ :type => :boolean,
47
+ :group => 'Command'
48
+ method_option :'shared-data', :default => nil,
49
+ :desc => 'path for the shared directory',
50
+ :banner => 'path', :type => :string,
51
+ :group => 'Command'
52
+ def create_app(path)
53
+ install_rails! path
54
+ install_app!
55
+ if self.options[:'shared-data']
56
+ self.options[:'git'] = self.options[:'git-data']
57
+ ensure_extrernalized_data! self.options[:'shared-data']
58
+ end
59
+ end
60
+
61
+ desc 'create.gem PATH', 'create a new milkshake gem.'
62
+ method_option :summary, :default => self.default_summary,
63
+ :desc => 'The gem summary',
64
+ :banner => 'string', :type => :string,
65
+ :group => 'Gem'
66
+ method_option :description, :default => self.default_description,
67
+ :desc => 'The gem description',
68
+ :banner => 'string', :type => :string,
69
+ :group => 'Gem'
70
+ method_option :website, :default => self.default_website,
71
+ :desc => 'The gem website',
72
+ :banner => 'string', :type => :string,
73
+ :group => 'Gem'
74
+ method_option :author, :default => self.default_author,
75
+ :desc => 'The gem author',
76
+ :banner => 'string', :type => :string,
77
+ :group => 'Gem'
78
+ method_option :email, :default => self.default_email,
79
+ :desc => 'The gem author\'s email address',
80
+ :banner => 'string', :type => :string,
81
+ :group => 'Gem'
82
+ method_option :git, :default => false,
83
+ :desc => 'Initialize git',
84
+ :type => :boolean,
85
+ :group => 'Command'
86
+ method_option :'git-data', :default => false,
87
+ :desc => 'Initialize git for the shared directory',
88
+ :type => :boolean,
89
+ :group => 'Command'
90
+ method_option :'shared-data', :default => nil,
91
+ :desc => 'path for the shared directory',
92
+ :banner => 'path', :type => :string,
93
+ :group => 'Command'
94
+ def create_gem(path)
95
+ name = File.basename(path)
96
+ assert_valid_gem_name! name
97
+ install_rails! path
98
+ install_app!
99
+ install_gem! name
100
+ if self.options[:'shared-data']
101
+ self.options[:'git'] = self.options[:'git-data']
102
+ ensure_extrernalized_data! self.options[:'shared-data']
103
+ end
104
+ end
105
+
106
+ desc 'create.host PATH', 'create a new milkshake host app.'
107
+ method_option :'git-data', :default => false,
108
+ :desc => 'Initialize git for the shared directory',
109
+ :type => :boolean,
110
+ :group => 'Command'
111
+ method_option :'shared-data', :default => nil,
112
+ :desc => 'path for the shared directory',
113
+ :banner => 'path', :type => :string,
114
+ :group => 'Command'
115
+ def create_host(path)
116
+ install_rails! path
117
+ install_app!
118
+ install_host!
119
+ if self.options[:'shared-data']
120
+ self.options[:'git'] = self.options[:'git-data']
121
+ ensure_extrernalized_data! self.options[:'shared-data']
122
+ end
123
+ end
124
+ rename_task 'create_host' => 'create.host',
125
+ 'create_app' => 'create.app',
126
+ 'create_gem' => 'create.gem'
127
+
128
+ desc 'install.app', 'install the milkshake preinitializer'
129
+ def install_app
130
+ install_app!
131
+ end
132
+
133
+ desc 'install.gem NAME', 'make a milkshake plugin'
134
+ method_option :summary, :default => self.default_summary,
135
+ :desc => 'The gem summary',
136
+ :banner => 'string', :type => :string,
137
+ :group => 'Command'
138
+ method_option :description, :default => self.default_description,
139
+ :desc => 'The gem description',
140
+ :banner => 'string', :type => :string,
141
+ :group => 'Command'
142
+ method_option :website, :default => self.default_website,
143
+ :desc => 'The gem website',
144
+ :banner => 'string', :type => :string,
145
+ :group => 'Command'
146
+ method_option :author, :default => self.default_author,
147
+ :desc => 'The gem author',
148
+ :banner => 'string', :type => :string,
149
+ :group => 'Command'
150
+ method_option :email, :default => self.default_email,
151
+ :desc => 'The gem author\'s email address',
152
+ :banner => 'string', :type => :string,
153
+ :group => 'Command'
154
+ method_option :git, :default => false,
155
+ :desc => 'Initialize git',
156
+ :type => :boolean,
157
+ :group => 'Command'
158
+ def install_gem(name)
159
+ assert_valid_gem_name! name
160
+ install_app!
161
+ install_gem! name
162
+ end
163
+
164
+ desc 'install.host', 'make a milkshake host app'
165
+ def install_host
166
+ install_app!
167
+ install_host!
168
+ end
169
+ rename_task 'install_host' => 'install.host',
170
+ 'install_app' => 'install.app',
171
+ 'install_gem' => 'install.gem'
172
+
173
+ desc 'extract.data SHARED_DIR', 'extract all data'
174
+ method_option :git, :default => false,
175
+ :desc => 'Initialize git',
176
+ :type => :boolean,
177
+ :group => 'Command'
178
+ def extract_data(path)
179
+ externalize_data! path
180
+ end
181
+ rename_task 'extract_data' => 'extract.data'
182
+
183
+ desc 'link.data SHARED_DIR', 'link all data to an existing shared directory'
184
+ def link_data(path)
185
+ link_externalized_data! path
186
+ end
187
+ rename_task 'link_data' => 'link.data'
188
+
189
+ end
190
+ end
@@ -0,0 +1,3 @@
1
+
2
+ require 'milkshake'
3
+ Milkshake.extender.inject_milkshake!
@@ -0,0 +1,50 @@
1
+
2
+ module Milkshake
3
+ class Cache
4
+
5
+ attr_accessor :path, :entries, :history
6
+
7
+ def initialize(path)
8
+ @path = path
9
+ begin
10
+ File.open(@path, 'r') { |f| @entries = Marshal.load(f.read) }
11
+ raise 'wrong type' unless Hash === @entries
12
+ rescue
13
+ @entries = {}
14
+ end
15
+ @history = []
16
+ end
17
+
18
+ def key(name)
19
+ if @entries.key?(name.to_s)
20
+ @entries[name.to_s]
21
+ elsif block_given?
22
+ @entries[name.to_s] = yield
23
+ else
24
+ nil
25
+ end
26
+ end
27
+
28
+ def [](name)
29
+ @entries[name.to_s]
30
+ end
31
+
32
+ def []=(name, value)
33
+ @entries[name.to_s] = value
34
+ end
35
+
36
+ def reset!
37
+ @history << @entries
38
+ @entries = {}
39
+ end
40
+
41
+ def restore!
42
+ @entries = @history.pop || {}
43
+ end
44
+
45
+ def persist!
46
+ File.open(@path, 'w+') { |f| f.write Marshal.dump(@entries) }
47
+ end
48
+
49
+ end
50
+ end
@@ -0,0 +1,96 @@
1
+
2
+ require 'tsort'
3
+
4
+ module Milkshake
5
+ class DependencyResolver
6
+
7
+ attr_reader :names, :specs, :gems
8
+
9
+ def self.load_for(gems)
10
+ dependency_loader = self.new(gems)
11
+ dependency_loader.add_dependecies!
12
+ dependency_loader.order_by_dependecies!
13
+ return dependency_loader
14
+ end
15
+
16
+ def initialize(gems)
17
+ specs = gems.collect do |k,r|
18
+ find_gemspec(k, r)
19
+ end.compact
20
+
21
+ @names = specs.collect { |spec| spec.name }
22
+ @specs = specs.inject({}) { |h, spec| h[spec.name] = spec ; h }
23
+ @gems = gems
24
+ end
25
+
26
+ def add_dependecies!
27
+ @specs.values.each do |spec|
28
+ add_dependecies_for spec
29
+ end
30
+ end
31
+
32
+ def order_by_dependecies!
33
+ @names = tsort
34
+ end
35
+
36
+ def each
37
+ @names.each do |name|
38
+ yield(@specs[name])
39
+ end
40
+ end
41
+
42
+ def reverse_each
43
+ @names.reverse.each do |name|
44
+ yield(@specs[name])
45
+ end
46
+ end
47
+
48
+ private
49
+
50
+ def find_gemspec(name, options={})
51
+ requirement = options[:version] || options["version"] || Gem::Requirement.default
52
+ dep = Gem::Dependency.new(name, requirement)
53
+ specs = gemspec_index.search(dep)
54
+
55
+ return nil if specs.empty?
56
+
57
+ specs.sort! do |a,b|
58
+ b.version <=> a.version
59
+ end
60
+
61
+ gemspec = specs.first
62
+
63
+ gemspec
64
+ end
65
+
66
+ def gemspec_index
67
+ @gemspec_index ||= Gem::SourceIndex.from_installed_gems
68
+ end
69
+
70
+ include TSort
71
+
72
+ def add_dependecies_for(spec)
73
+ deps = (spec.rails_dependencies || {})
74
+ deps.each do |name, options|
75
+ next if @names.include?(name)
76
+
77
+ gemspec = find_gemspec(name, options)
78
+ @specs[gemspec.name] = gemspec
79
+ @names.push(gemspec.name)
80
+ add_dependecies_for(gemspec)
81
+ end
82
+
83
+ @gems = @gems.merge(deps)
84
+ end
85
+
86
+ def tsort_each_node(&block)
87
+ @names.each(&block)
88
+ end
89
+
90
+ def tsort_each_child(node, &block)
91
+ deps = (@specs[node].rails_dependencies || {})
92
+ deps.keys.each(&block)
93
+ end
94
+
95
+ end
96
+ end
@@ -0,0 +1,82 @@
1
+
2
+ module Milkshake
3
+ class Environment
4
+
5
+ attr_reader :gems, :options, :cache
6
+
7
+ def self.load(cache, path)
8
+ if File.exist?(path)
9
+ new cache, YAML.load_file(path)
10
+ else
11
+ new cache
12
+ end
13
+ end
14
+
15
+ def initialize(cache, options={})
16
+ @cache = cache
17
+ @options = {'gems' => {}}.merge(options)
18
+
19
+ Milkshake.extender.extend_rubygems!
20
+
21
+ reload!
22
+ end
23
+
24
+ def reload!
25
+ resolver = nil
26
+ @gems = @cache.key('environment.gems') do
27
+ resolver ||= DependencyResolver.load_for(@options['gems'])
28
+ resolver.gems
29
+ end
30
+
31
+ @gemspecs = @cache.key('environment.gemspecs') do
32
+ resolver ||= DependencyResolver.load_for(@options['gems'])
33
+ specs = resolver.specs
34
+ specs.each { |(name, spec)| spec.store_persistent_load_information! }
35
+ specs
36
+ end
37
+ @gemspecs.each { |(name, spec)| spec.use_persistent_load_information! }
38
+
39
+ @order = @cache.key('environment.gems.order') do
40
+ resolver ||= DependencyResolver.load_for(@options['gems'])
41
+ resolver.names
42
+ end
43
+
44
+ @gemspecs = @ordered_gemspecs = nil
45
+ end
46
+
47
+ def gemspecs_by_name
48
+ @gemspecs ||= {}
49
+ end
50
+
51
+ def gemspecs
52
+ @ordered_gemspecs ||= @order.collect do |name|
53
+ gemspecs_by_name[name]
54
+ end
55
+ end
56
+
57
+ def gem_dependencies
58
+ @order.inject([]) do |g, name|
59
+ g << Rails::GemDependency.new(name, @gems[name])
60
+ end
61
+ end
62
+
63
+ def locale_paths
64
+ @cache.key('environment.locale_paths') do
65
+ if Gem::Version.new(Rails.version) < Gem::Version.new("2.3.4")
66
+ locale_paths = self.gemspecs.inject([]) do |paths, gemspec|
67
+ locale_path = File.join(gemspec.full_gem_path, 'config', 'locales')
68
+ if File.directory?(locale_path)
69
+ paths.push(Dir[File.join(locale_path, '*.{rb,yml}')])
70
+ else
71
+ paths
72
+ end
73
+ end
74
+ locale_paths.flatten!
75
+ else
76
+ []
77
+ end
78
+ end
79
+ end
80
+
81
+ end
82
+ end
@@ -0,0 +1,58 @@
1
+
2
+ module Milkshake
3
+ class Extender
4
+
5
+ def inject_milkshake!
6
+ if defined?(::PhusionPassenger)
7
+ extend_rails!
8
+ else
9
+ extend_boot!
10
+ end
11
+ extend_rubygems!
12
+ end
13
+
14
+ # for passenger
15
+ def extend_rails!
16
+ Object.const_set('Rails', Module.new)
17
+ r = Object.const_get('Rails')
18
+ def r.singleton_method_added(m)
19
+ if (m.to_s == 'boot!') and !@injected_milkshake
20
+ @injected_milkshake = true
21
+ k = (class << self ; self ; end)
22
+ k.send :alias_method, "milkshakeless_#{m}", m
23
+ k.send :define_method, m do
24
+ milkshakeless_boot!
25
+ Milkshake.load!
26
+ Milkshake.extender.extend_railties!
27
+ end
28
+ end
29
+ end
30
+ end
31
+
32
+ # for others
33
+ def extend_boot!
34
+ include_module Rails::VendorBoot, Milkshake::RailsExtentions::VendorBoot
35
+ include_module Rails::GemBoot, Milkshake::RailsExtentions::GemBoot
36
+ end
37
+
38
+ def extend_railties!
39
+ include_module Rails::Configuration, Milkshake::RailsExtentions::Configuration
40
+ include_module Rails::Initializer, Milkshake::RailsExtentions::Initializer
41
+ end
42
+
43
+ def extend_frameworks!
44
+ include_module ActiveRecord::Migrator, Milkshake::RailsExtentions::Migrator
45
+ end
46
+
47
+ def extend_rubygems!
48
+ include_module Gem::Specification, Milkshake::RubygemsExtentions::Specification
49
+ end
50
+
51
+ private
52
+
53
+ def include_module(base, mod)
54
+ base.send :include, mod
55
+ end
56
+
57
+ end
58
+ end
@@ -0,0 +1,64 @@
1
+
2
+ require 'snapshots'
3
+
4
+ module Milkshake
5
+ class Linker
6
+
7
+ attr_reader :environment, :validator, :cache, :current_snapshot
8
+
9
+ def initialize(environment, validator, cache)
10
+ @environment = environment
11
+ @validator = validator
12
+ @cache = cache
13
+ end
14
+
15
+ def link!
16
+ link_only_once do
17
+ if validator.relink?
18
+ @current_snapshot = Snapshots.dump
19
+
20
+ link_public_directories!
21
+
22
+ run_migrations!
23
+
24
+ validator.persist!
25
+ cache.persist!
26
+ end
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ def run_migrations!
33
+ ActiveRecord::Migrator.migrate("db/migrate/", nil)
34
+ end
35
+
36
+ def link_public_directories!
37
+ public_vendor_path = File.join(Rails.public_path, 'vendor')
38
+ FileUtils.rm_rf(public_vendor_path)
39
+ FileUtils.mkdir_p(public_vendor_path)
40
+
41
+ self.environment.gemspecs.each do |gemspec|
42
+ public_path = File.join(gemspec.full_gem_path, 'public')
43
+ if File.directory?(public_path)
44
+ FileUtils.ln_s(public_path, File.join(public_vendor_path, gemspec.name))
45
+ end
46
+ end
47
+ end
48
+
49
+ def link_only_once
50
+ lock_path = Milkshake.cache_file
51
+ FileUtils.touch(lock_path)
52
+ lock_file = File.new(lock_path)
53
+ acquired = !!lock_file.flock(File::LOCK_EX | File::LOCK_NB)
54
+ if acquired
55
+ yield
56
+ else
57
+ lock_file.flock(File::LOCK_EX)
58
+ end
59
+ ensure
60
+ lock_file.flock(File::LOCK_UN)
61
+ end
62
+
63
+ end
64
+ end
@@ -0,0 +1,34 @@
1
+
2
+ module Milkshake
3
+ class Loader
4
+
5
+ attr_accessor :environment, :cache
6
+
7
+ def initialize(environment, cache)
8
+ @environment = environment
9
+ @cache = cache
10
+ end
11
+
12
+ def load_gem_initializers!
13
+ initializers.each do |initializer|
14
+ load(initializer)
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ def initializers
21
+ @cache.key('loader.initializers') do
22
+ relative_path = %w( rails initializers ** *.rb )
23
+ paths = []
24
+ self.environment.gemspecs.each do |gemspec|
25
+ paths.concat Dir.glob(File.join(gemspec.full_gem_path, *relative_path))
26
+ end
27
+ paths.concat Dir.glob(File.join(Rails.root, *relative_path))
28
+ paths
29
+ end
30
+ end
31
+
32
+ end
33
+ end
34
+
@@ -0,0 +1,36 @@
1
+
2
+ module Milkshake
3
+ module RailsExtentions
4
+
5
+ module VendorBoot
6
+ def self.included(base)
7
+ base.module_eval do
8
+ alias_method :load_initializer_without_milkshake, :load_initializer
9
+ alias_method :load_initializer, :load_initializer_with_milkshake
10
+ end
11
+ end
12
+
13
+ def load_initializer_with_milkshake
14
+ load_initializer_without_milkshake
15
+ Milkshake.load!
16
+ Milkshake.extender.extend_railties!
17
+ end
18
+ end
19
+
20
+ module GemBoot
21
+ def self.included(base)
22
+ base.module_eval do
23
+ alias_method :load_initializer_without_milkshake, :load_initializer
24
+ alias_method :load_initializer, :load_initializer_with_milkshake
25
+ end
26
+ end
27
+
28
+ def load_initializer_with_milkshake
29
+ load_initializer_without_milkshake
30
+ Milkshake.load!
31
+ Milkshake.extender.extend_railties!
32
+ end
33
+ end
34
+
35
+ end
36
+ end
@@ -0,0 +1,36 @@
1
+
2
+ module Milkshake
3
+ module RailsExtentions
4
+ module Configuration
5
+
6
+ def self.included(base)
7
+ %w( default_gems default_i18n default_load_paths ).each do |m|
8
+ base.send :alias_method, "#{m}_without_milkshake", m
9
+ base.send :alias_method, m, "#{m}_with_milkshake"
10
+ end
11
+ end
12
+
13
+ # inject gem dependecies
14
+ def default_gems_with_milkshake
15
+ default_gems = default_gems_without_milkshake
16
+ default_gems.concat(Milkshake.environment.gem_dependencies)
17
+ default_gems
18
+ end
19
+
20
+ # inject locales from gem dependecies
21
+ def default_i18n_with_milkshake
22
+ default_i18n = default_i18n_without_milkshake
23
+ default_i18n.load_path.concat(Milkshake.environment.locale_paths)
24
+ default_i18n.load_path.uniq!
25
+ default_i18n
26
+ end
27
+
28
+ # inject fallback application controller
29
+ def default_load_paths_with_milkshake
30
+ path = File.expand_path(File.join(File.dirname(__FILE__), *%w( .. rails_fallbacks )))
31
+ default_load_paths_without_milkshake.push(path)
32
+ end
33
+
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,50 @@
1
+
2
+ module Milkshake
3
+ module RailsExtentions
4
+ module Initializer
5
+
6
+ def self.included(base)
7
+ %w( require_frameworks check_for_unbuilt_gems load_application_initializers process ).each do |m|
8
+ base.send :alias_method, "#{m}_without_milkshake", m
9
+ base.send :alias_method, m, "#{m}_with_milkshake"
10
+ end
11
+ end
12
+
13
+ def require_frameworks_with_milkshake
14
+ require_frameworks_without_milkshake
15
+ Milkshake.extender.extend_frameworks!
16
+ end
17
+
18
+ def check_for_unbuilt_gems_with_milkshake
19
+ check_for_unbuilt_gems_without_milkshake
20
+
21
+ Milkshake.linker.link!
22
+ end
23
+
24
+ def load_application_initializers_with_milkshake
25
+ Milkshake.loader.load_gem_initializers!
26
+ Milkshake.persist!
27
+
28
+ load_application_initializers_without_milkshake
29
+ end
30
+
31
+ def process_with_milkshake
32
+ process_without_milkshake
33
+ rescue Exception => e
34
+ Milkshake.cache.restore!
35
+ Milkshake.environment.reload!
36
+ if Milkshake.linker.current_snapshot
37
+ Snapshots.load(Milkshake.linker.current_snapshot)
38
+ end
39
+
40
+ if @restore_relink
41
+ raise e
42
+ else
43
+ @restore_relink = true
44
+ retry
45
+ end
46
+ end
47
+
48
+ end
49
+ end
50
+ end