beet 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. data/.gitignore +2 -0
  2. data/LICENSE +20 -0
  3. data/README.rdoc +13 -0
  4. data/Rakefile +59 -0
  5. data/TODO +17 -0
  6. data/VERSION +1 -0
  7. data/beet.gemspec +89 -0
  8. data/bin/beet +112 -0
  9. data/features/generating.feature +24 -0
  10. data/features/step_definitions/beet_steps.rb +7 -0
  11. data/features/step_definitions/common_steps.rb +172 -0
  12. data/features/support/common.rb +33 -0
  13. data/features/support/env.rb +25 -0
  14. data/lib/beet.rb +10 -0
  15. data/lib/beet/capistrano.rb +14 -0
  16. data/lib/beet/execution.rb +50 -0
  17. data/lib/beet/executor.rb +195 -0
  18. data/lib/beet/file_system.rb +143 -0
  19. data/lib/beet/gem_location_map.rb +64 -0
  20. data/lib/beet/interaction.rb +37 -0
  21. data/lib/beet/logger.rb +44 -0
  22. data/lib/beet/rails.rb +146 -0
  23. data/lib/beet/recipes/passenger/vhost.rb +17 -0
  24. data/lib/beet/recipes/rack/middleware.rb +17 -0
  25. data/lib/beet/recipes/rails/authlogic.rb +297 -0
  26. data/lib/beet/recipes/rails/clean_files.rb +4 -0
  27. data/lib/beet/recipes/rails/clearance.rb +22 -0
  28. data/lib/beet/recipes/rails/cms/bcms_blog.rb +21 -0
  29. data/lib/beet/recipes/rails/cms/bcms_event.rb +24 -0
  30. data/lib/beet/recipes/rails/css/blueprint.rb +5 -0
  31. data/lib/beet/recipes/rails/css/reset.rb +10 -0
  32. data/lib/beet/recipes/rails/db/mysql.rb +42 -0
  33. data/lib/beet/recipes/rails/db/postgres.rb +52 -0
  34. data/lib/beet/recipes/rails/git.rb +23 -0
  35. data/lib/beet/recipes/rails/jquery.rb +11 -0
  36. data/lib/beet/recipes/rails/rspec.rb +3 -0
  37. data/lib/beet/recipes/rails/shoulda.rb +1 -0
  38. data/lib/beet/scm.rb +36 -0
  39. data/lib/beet/scm/git.rb +15 -0
  40. data/lib/beet/scm/svn.rb +5 -0
  41. data/lib/beet/template_location_map.rb +13 -0
  42. data/test/executor_test.rb +24 -0
  43. data/test/file_system_test.rb +59 -0
  44. data/test/test_helper.rb +12 -0
  45. metadata +110 -0
@@ -0,0 +1,33 @@
1
+ module CommonHelpers
2
+ def in_tmp_folder(&block)
3
+ FileUtils.chdir(@tmp_root, &block)
4
+ end
5
+
6
+ def in_project_folder(&block)
7
+ project_folder = @active_project_folder || @tmp_root
8
+ FileUtils.chdir(project_folder, &block)
9
+ end
10
+
11
+ def in_project_subfolder(folder, &block)
12
+ FileUtils.chdir(File.join(@active_project_folder, folder), &block)
13
+ end
14
+
15
+ def in_home_folder(&block)
16
+ FileUtils.chdir(@home_path, &block)
17
+ end
18
+
19
+ def force_local_lib_override(project_name = @project_name)
20
+ rakefile = File.read(File.join(project_name, 'Rakefile'))
21
+ File.open(File.join(project_name, 'Rakefile'), "w+") do |f|
22
+ f << "$:.unshift('#{@lib_path}')\n"
23
+ f << rakefile
24
+ end
25
+ end
26
+
27
+ def setup_active_project_folder project_name
28
+ @active_project_folder = File.join(@tmp_root, project_name)
29
+ @project_name = project_name
30
+ end
31
+ end
32
+
33
+ World(CommonHelpers)
@@ -0,0 +1,25 @@
1
+ gem 'cucumber'
2
+ require 'cucumber'
3
+ gem 'rspec'
4
+ require 'spec'
5
+
6
+ Before do
7
+ @tmp_root = File.dirname(__FILE__) + "/../../tmp"
8
+ @home_path = File.expand_path(File.join(@tmp_root, "home"))
9
+ FileUtils.rm_rf @tmp_root
10
+ FileUtils.mkdir_p @home_path
11
+ ENV['HOME'] = @home_path
12
+ end
13
+
14
+ After do
15
+ unless ENV['LEAVE_CUCUMBER_GENERATED_OUTPUT']
16
+ FileUtils.rm_rf @active_project_folder
17
+ end
18
+ end
19
+
20
+ gem "fakeweb"
21
+ require "fakeweb"
22
+
23
+ Before do
24
+ FakeWeb.allow_net_connect = false
25
+ end
data/lib/beet.rb ADDED
@@ -0,0 +1,10 @@
1
+ # TODO similarly, figure out how to require things as needed by the template
2
+ require 'beet/execution'
3
+ require 'beet/file_system'
4
+ require 'beet/interaction'
5
+ require 'beet/capistrano'
6
+ require 'beet/rails'
7
+ require 'beet/scm'
8
+ require 'beet/executor'
9
+ require 'beet/gem_location_map'
10
+ require 'beet/template_location_map'
@@ -0,0 +1,14 @@
1
+ module Beet
2
+ module Capistrano
3
+ # Just run the capify command in root
4
+ #
5
+ # ==== Example
6
+ #
7
+ # capify!
8
+ #
9
+ def capify!
10
+ log 'capifying'
11
+ in_root { run('capify .', false) }
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,50 @@
1
+ module Beet
2
+ module Execution
3
+ # Executes a command
4
+ #
5
+ # ==== Example
6
+ #
7
+ # inside('vendor') do
8
+ # run('ln -s ~/edge rails)
9
+ # end
10
+ #
11
+ def run(command, log_action = true)
12
+ log 'executing', "#{command} from #{Dir.pwd}" if log_action
13
+ system(command)
14
+ end
15
+
16
+ # Executes a command with sudo
17
+ #
18
+ # ==== Example
19
+ #
20
+ # inside('vendor') do
21
+ # sudo('mkdir /var/log/something')
22
+ # end
23
+ #
24
+ def sudo(command, log_action = true)
25
+ command = "#{SUDO}#{command}"
26
+ run(command,log_action)
27
+ end
28
+
29
+ # Executes a ruby script (taking into account WIN32 platform quirks)
30
+ def run_ruby_script(command, log_action = true)
31
+ ruby_command = RUBY_PLATFORM=~ /win32/ ? 'ruby ' : ''
32
+ run("#{ruby_command}#{command}", log_action)
33
+ end
34
+
35
+ # Runs the supplied rake task
36
+ #
37
+ # ==== Example
38
+ #
39
+ # rake("db:migrate")
40
+ # rake("db:migrate", :env => "production")
41
+ # rake("gems:install", :sudo => true)
42
+ #
43
+ def rake(command, options = {})
44
+ log 'rake', command
45
+ env = options[:env] || 'development'
46
+ sudo = options[:sudo] ? 'sudo ' : ''
47
+ in_root { run("#{sudo}rake #{command} RAILS_ENV=#{env}", false) }
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,195 @@
1
+ require 'open-uri'
2
+ require 'beet/logger'
3
+
4
+ module Beet
5
+ class Executor
6
+ BEET_DATA_FILE = "~/.beet.yml"
7
+ include Beet::Execution
8
+ include Beet::FileSystem
9
+ include Beet::Interaction
10
+
11
+ include Beet::Rails
12
+ include Beet::Capistrano
13
+ include Beet::SCM
14
+
15
+ attr_reader :root, :logger, :options, :template
16
+ attr_accessor :recipes, :project_name, :gems, :todo_items
17
+
18
+ def initialize(project_name, options={}) # :nodoc:
19
+ @root = calculate_project_root(project_name)
20
+ @project_name = ((project_name == '.') ? File.basename(Dir.pwd) : project_name)
21
+ @logger = Beet::Logger.new
22
+ @gems = []
23
+ @template = options[:template]
24
+ @options = options
25
+ @todo_items = ''
26
+ @recipes = []
27
+ @project_type = options[:project_type]
28
+ @generate = true unless options[:generate] == false
29
+ @display = options[:display]
30
+ extract_commands_from_options
31
+ end
32
+
33
+ def start
34
+ if @options[:use]
35
+ puts "Loading saved configuration: #{@options[:use]}"
36
+ data = load_saved_recipe_file
37
+ if config = data[@options[:use]]
38
+ @gems.concat(config[:gems]) if config[:gems]
39
+ @template = config[:template] if config[:template]
40
+ @recipes.concat(config[:recipes]) if config[:recipes]
41
+ end
42
+ end
43
+
44
+ if @display && @template
45
+ puts open(TEMPLATE_LOCATIONS[@template]).read
46
+ else
47
+ case @project_type
48
+ when :rails
49
+ if @generate
50
+ puts "Generating rails project #{project_name}..."
51
+ if @template
52
+ system("rails #{project_name} -m #{TEMPLATE_LOCATIONS[@template]}")
53
+ else
54
+ system("rails #{project_name}")
55
+ end
56
+ end
57
+ end
58
+
59
+ add_gems
60
+
61
+ print_todo
62
+
63
+ if @options[:save]
64
+ save_run
65
+ end
66
+ end
67
+
68
+ run_recipes
69
+
70
+ end
71
+
72
+ def run_recipes
73
+ @recipes.each do |recipe|
74
+ begin
75
+ code = open(recipe).read
76
+ if @display
77
+ puts code
78
+ else
79
+ in_root { instance_eval(code)}
80
+ end
81
+ rescue LoadError, Errno::ENOENT => e
82
+ raise "The recipe [#{recipe}] could not be loaded. Error: #{e}"
83
+ end
84
+ end
85
+ end
86
+
87
+ def log(*args)
88
+ logger.log(*args)
89
+ end
90
+
91
+ def todo(string=nil, &block)
92
+ self.todo_items << (string || block.call)
93
+ end
94
+
95
+ private
96
+
97
+ def print_todo
98
+ unless todo_items.empty?
99
+ puts '#' * 30
100
+ puts "TODO Items:"
101
+ puts todo_items
102
+ puts '#' * 30
103
+ end
104
+ end
105
+
106
+ def calculate_project_root(project_name)
107
+ # if the name looks like ~/projects/foobar then thats the root
108
+ if project_name.include?('/')
109
+ project_name
110
+ # if we're running inside the app, then current dir is it
111
+ elsif File.basename(Dir.pwd) == project_name
112
+ Dir.pwd
113
+ # assume the root is ./project_name
114
+ else
115
+ File.join(Dir.pwd, project_name)
116
+ end
117
+ end
118
+
119
+ def add_gems
120
+ if @gems
121
+ @gems.each do |gem_data|
122
+ gem(gem_data[:name], :source => gem_data[:source])
123
+ end
124
+ end
125
+ end
126
+
127
+ def extract_commands_from_options
128
+ if @options[:gems]
129
+ @options[:gems].split(/[\s,]+/).each do |gem|
130
+ if location = gem_location(gem)
131
+ @gems << {:name => gem, :source => location}
132
+ else
133
+ puts "gem: #{gem} not found. Did you spell it correctly? If so, submit a patch with its location!"
134
+ end
135
+ end
136
+ end
137
+ if @options[:recipes]
138
+ @options[:recipes].split(/[\s,]+/).each do |recipe|
139
+ if file = recipe_location(recipe)
140
+ @recipes << file
141
+ else
142
+ puts "Can't find recipe #{recipe}"
143
+ end
144
+ end
145
+ end
146
+ end
147
+
148
+ def save_run
149
+ require 'yaml'
150
+ name = if options[:save] == true
151
+ ask("Enter a name for this configuration: ")
152
+ else
153
+ options[:save]
154
+ end
155
+ data = load_saved_recipe_file
156
+ data[name] = {:gems => @gems, :recipes => @recipes, :template => @template}
157
+ write_saved_recipe_file(data)
158
+ end
159
+
160
+ def beet_data_file
161
+ File.expand_path(BEET_DATA_FILE)
162
+ end
163
+
164
+ def load_saved_recipe_file
165
+ if File.exists?(beet_data_file)
166
+ YAML.load_file(beet_data_file)
167
+ else
168
+ {}
169
+ end
170
+ end
171
+
172
+ def write_saved_recipe_file(data)
173
+ File.open(beet_data_file, "wb") do |f|
174
+ f.write(YAML::dump(data))
175
+ end
176
+ end
177
+
178
+ def gem_location(gem_name)
179
+ GEM_LOCATIONS[gem_name]
180
+ end
181
+
182
+ def recipe_location(recipe)
183
+ return recipe if File.exists?(recipe) or recipe.include?('http://')
184
+ locations = []
185
+ locations << File.expand_path(ENV['BEET_RECIPES_DIR']) if ENV['BEET_RECIPES_DIR']
186
+ locations << File.expand_path(File.join(File.dirname(__FILE__), 'recipes'))
187
+ locations.each do |location|
188
+ filename = File.join(location, "#{recipe}.rb")
189
+ return filename if File.exists?(filename)
190
+ end
191
+ nil
192
+ end
193
+ end
194
+ end
195
+
@@ -0,0 +1,143 @@
1
+ require 'fileutils'
2
+ module Beet
3
+ module FileSystem
4
+ # Create a new file in the project folder. Specify the
5
+ # relative path from the project's root. Data is the return value of a block
6
+ # or a data string.
7
+ #
8
+ # ==== Examples
9
+ #
10
+ # file("lib/fun_party.rb") do
11
+ # hostname = ask("What is the virtual hostname I should use?")
12
+ # "vhost.name = #{hostname}"
13
+ # end
14
+ #
15
+ # file("config/apach.conf", "your apache config")
16
+ #
17
+ def file(filename, data = nil, log_action = true, &block)
18
+ log 'file', filename if log_action
19
+ dir, file = [File.dirname(filename), File.basename(filename)]
20
+
21
+ inside(dir) do
22
+ File.open(file, "w") do |f|
23
+ if block_given?
24
+ f.write(block.call)
25
+ else
26
+ f.write(data)
27
+ end
28
+ end
29
+ end
30
+ end
31
+
32
+ # Create a new file in the lib/ directory. Code can be specified
33
+ # in a block or a data string can be given.
34
+ #
35
+ # ==== Examples
36
+ #
37
+ # lib("crypto.rb") do
38
+ # "crypted_special_value = '#{rand}--#{Time.now}--#{rand(1337)}--'"
39
+ # end
40
+ #
41
+ # lib("foreign.rb", "# Foreign code is fun")
42
+ #
43
+ def lib(filename, data = nil, &block)
44
+ log 'lib', filename
45
+ file("lib/#{filename}", data, false, &block)
46
+ end
47
+
48
+ # Create a new Rakefile with the provided code (either in a block or a string).
49
+ #
50
+ # ==== Examples
51
+ #
52
+ # rakefile("bootstrap.rake") do
53
+ # project = ask("What is the UNIX name of your project?")
54
+ #
55
+ # <<-TASK
56
+ # namespace :#{project} do
57
+ # task :bootstrap do
58
+ # puts "i like boots!"
59
+ # end
60
+ # end
61
+ # TASK
62
+ # end
63
+ #
64
+ # rakefile("seed.rake", "puts 'im plantin ur seedz'")
65
+ #
66
+ def rakefile(filename, data = nil, &block)
67
+ log 'rakefile', filename
68
+ file("lib/tasks/#{filename}", data, false, &block)
69
+ end
70
+
71
+ # Do something in the root of the project or
72
+ # a provided subfolder; the full path is yielded to the block you provide.
73
+ # The path is set back to the previous path when the method exits.
74
+ def inside(dir = '', &block)
75
+ folder = File.join(root, dir)
76
+ FileUtils.mkdir_p(folder) unless File.exist?(folder)
77
+ FileUtils.cd(folder) { block.arity == 1 ? yield(folder) : yield }
78
+ end
79
+
80
+ def in_root
81
+ FileUtils.cd(root) { yield }
82
+ end
83
+
84
+ # Run a regular expression replacement on a file
85
+ #
86
+ # ==== Example
87
+ #
88
+ # gsub_file 'app/controllers/application_controller.rb', /#\s*(filter_parameter_logging :password)/, '\1'
89
+ #
90
+ def gsub_file(relative_destination, regexp, *args, &block)
91
+ #path = destination_path(relative_destination)
92
+ path = relative_destination
93
+ content = File.read(path)
94
+ check_for = args.first || yield('')
95
+ regex = Regexp.new(regexp.source + Regexp.escape(check_for))
96
+ return if content =~ regex # if we can match the text and its leadin regex, don't add again
97
+ content = content.gsub(regexp, *args, &block)
98
+ File.open(path, 'wb') { |file| file.write(content) }
99
+ end
100
+
101
+ # Append text to a file
102
+ #
103
+ # ==== Example
104
+ #
105
+ # append_file 'config/environments/test.rb', 'config.gem "rspec"'
106
+ #
107
+ def append_file(relative_destination, data)
108
+ path = destination_path(relative_destination)
109
+ File.open(path, 'ab') { |file| file.write(data) }
110
+ end
111
+
112
+ # Add text after matching line
113
+ #
114
+ # ==== Example
115
+ #
116
+ # add_after 'config/environment.rb', '# config.gem "aws-s3", :lib => "aws/s3"'
117
+ #
118
+ def add_after(filename, matching_text, data=nil, &block)
119
+ gsub_file filename, /(\s*#{Regexp.escape(matching_text)})/mi do |match|
120
+ "#{match}\n#{data || block.call}"
121
+ end
122
+ end
123
+
124
+ # Add text before matching line
125
+ #
126
+ # ==== Example
127
+ #
128
+ # add_before 'config/environment.rb', '# config.gem "aws-s3", :lib => "aws/s3"'
129
+ #
130
+ def add_before(filename, matching_text, data=nil, &block)
131
+ gsub_file filename, /^(\s*#{Regexp.escape(matching_text)})/mi do |match|
132
+ "#{data || block.call}#{match}"
133
+ end
134
+ end
135
+
136
+ protected
137
+
138
+ def destination_path(relative_destination)
139
+ File.join(root, relative_destination)
140
+ end
141
+
142
+ end
143
+ end