beet 0.3.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.
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