milkshake 0.0.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.
@@ -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