gem_suit 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. data/.gitignore +10 -0
  2. data/.suit +4 -0
  3. data/CHANGELOG.rdoc +5 -0
  4. data/Gemfile +3 -0
  5. data/MIT-LICENSE +20 -0
  6. data/README.textile +419 -0
  7. data/Rakefile +26 -0
  8. data/VERSION +1 -0
  9. data/bin/suit +10 -0
  10. data/gem_suit.gemspec +26 -0
  11. data/lib/gem_suit/application/actions.rb +203 -0
  12. data/lib/gem_suit/application/test.rb +106 -0
  13. data/lib/gem_suit/application/utils/gemfile.rb +42 -0
  14. data/lib/gem_suit/application/utils.rb +135 -0
  15. data/lib/gem_suit/application.rb +42 -0
  16. data/lib/gem_suit/cli/application/io_buffer.rb +54 -0
  17. data/lib/gem_suit/cli/application/test_loader.rb +5 -0
  18. data/lib/gem_suit/cli/application.rb +159 -0
  19. data/lib/gem_suit/cli/base/shell.rb +41 -0
  20. data/lib/gem_suit/cli/base/utils.rb +83 -0
  21. data/lib/gem_suit/cli/base.rb +15 -0
  22. data/lib/gem_suit/cli/builder/boot +14 -0
  23. data/lib/gem_suit/cli/builder/generator.rb +154 -0
  24. data/lib/gem_suit/cli/builder/rails_app.rb +146 -0
  25. data/lib/gem_suit/cli/builder.rb +143 -0
  26. data/lib/gem_suit/cli/config/hash.rb +61 -0
  27. data/lib/gem_suit/cli/config.rb +37 -0
  28. data/lib/gem_suit/cli.rb +145 -0
  29. data/lib/gem_suit/integration_test.rb +14 -0
  30. data/lib/gem_suit/test_help.rb +17 -0
  31. data/lib/gem_suit/version.rb +9 -0
  32. data/lib/gem_suit.rb +6 -0
  33. data/templates/dynamic/CHANGELOG.rdoc +5 -0
  34. data/templates/dynamic/Gemfile +14 -0
  35. data/templates/dynamic/MIT-LICENSE +20 -0
  36. data/templates/dynamic/README.textile +35 -0
  37. data/templates/dynamic/VERSION +1 -0
  38. data/templates/dynamic/config/boot.rb +13 -0
  39. data/templates/dynamic/config/preinitializer.rb +20 -0
  40. data/templates/dynamic/gitignore +9 -0
  41. data/templates/dynamic/suit/shared/app/views/application/index.html.erb +16 -0
  42. data/templates/dynamic/suit/shared/public/stylesheets/app.css +99 -0
  43. data/templates/dynamic/suit/shared/test/test_helper.rb +37 -0
  44. data/templates/dynamic/suit/templates/shared/Gemfile +14 -0
  45. data/templates/dynamic/suit/templates/shared/config/database-mysql.yml +11 -0
  46. data/templates/dynamic/suit/templates/shared/config/database-sqlite.yml +10 -0
  47. data/templates/static/suit/shared/app/models/.gitkeep +0 -0
  48. data/templates/static/suit/shared/app/views/layouts/application.html.erb +11 -0
  49. data/templates/static/suit/shared/db/schema.rb +2 -0
  50. data/templates/static/suit/shared/db/seeds.rb +7 -0
  51. data/templates/static/suit/shared/test/fixtures/.gitkeep +0 -0
  52. data/templates/static/suit/shared/test/integration/suit/example.rb +40 -0
  53. data/templates/static/suit/shared/test/suit_application/capybara_extensions.rb +36 -0
  54. data/templates/static/suit/shared/test/suit_application.rb +27 -0
  55. data/templates/static/suit/shared/test/unit/.gitkeep +0 -0
  56. data/templates/static/suit/templates/shared/config/initializers/.gitkeep +0 -0
  57. data/templates/static/suit/templates/shared/db/schema.rb +2 -0
  58. metadata +188 -0
@@ -0,0 +1,203 @@
1
+ require "pathname"
2
+ require "rich/support/core/string/colorize"
3
+ require "gem_suit/application/utils"
4
+
5
+ module GemSuit
6
+ class Application < ::Thor::Group
7
+ module Actions
8
+
9
+ def self.included(base)
10
+ base.extend ClassMethods
11
+ base.send :include, Utils
12
+ base.send :include, InstanceMethods
13
+ base.send :include, Thor::Actions
14
+ end
15
+
16
+ module ClassMethods
17
+ def restore_all
18
+ self.new.restore_all true
19
+ end
20
+
21
+ def stash_all
22
+ self.new.stash_all
23
+ end
24
+ end
25
+
26
+ module InstanceMethods
27
+ attr_accessor :config, :verbose
28
+
29
+ def restore_files
30
+ # Implement in subclass
31
+ end
32
+
33
+ def stash_files
34
+ # Implement in subclass
35
+ end
36
+
37
+ def locals_for_template(path)
38
+ # Implement in subclass
39
+ end
40
+
41
+ def config
42
+ @config || {}
43
+ end
44
+
45
+ def locals
46
+ @locals ||= (locals_for_template @relative_path if @relative_path) || {}
47
+ end
48
+
49
+ def restore_all(force = nil)
50
+ if @prepared
51
+ unless force
52
+ log "Cannot (non-forced) restore files after having prepared the test application" unless force.nil?
53
+ return
54
+ end
55
+ end
56
+
57
+ return if restore_files == false
58
+
59
+ if File.exists?(new_files = expand_path(".new_files"))
60
+ File.readlines(new_files).each do |line|
61
+ delete line.strip
62
+ end
63
+ File.delete new_files
64
+ end
65
+
66
+ restore "app/models/**/*.#{STASHED_EXT}"
67
+ restore "app/views/**/*.#{STASHED_EXT}"
68
+ restore "db/**/*.#{STASHED_EXT}"
69
+ restore "public/**/*.#{STASHED_EXT}"
70
+ restore "test/**/*.#{STASHED_EXT}"
71
+ restore "**/*.#{STASHED_EXT}"
72
+ true
73
+ end
74
+
75
+ def stash_all
76
+ return if stash_files == false
77
+ stash "Gemfile.lock"
78
+ ["shared", "rails-#{rails_version}"].each do |dir|
79
+ dir_path = File.expand_path dir, templates_path
80
+ next unless File.exists? dir_path
81
+ root = Pathname.new dir_path
82
+ Dir[File.expand_path("**/*", root.realpath)].each do |file|
83
+ next if File.directory? file
84
+ path = Pathname.new file
85
+ write path.relative_path_from(root).to_s
86
+ end
87
+ end
88
+ true
89
+ end
90
+
91
+ def skip(action, string)
92
+ ((@skipped_files ||= {})[action] ||= []) << string
93
+ end
94
+
95
+ def skip?(action, string)
96
+ return false if @skipped_files.nil? || @skipped_files[action].nil?
97
+ !!@skipped_files[action].detect{|x| File.fnmatch? expand_path(x), string}
98
+ end
99
+
100
+ def restore(string)
101
+ Dir[expand_path(string)].each do |file|
102
+ next unless File.exists? stashed(file)
103
+ next if skip? :restore, file
104
+ delete original(file)
105
+ log :restoring, stashed(file)
106
+ File.rename stashed(file), original(file)
107
+ end
108
+ end
109
+
110
+ def delete(string)
111
+ Dir[expand_path(string)].each do |file|
112
+ next if skip? :deleting, file
113
+ log :deleting, file
114
+ File.delete file
115
+ end
116
+
117
+ dirname = expand_path File.dirname(string)
118
+ return unless File.exists?(dirname)
119
+
120
+ Dir.glob("#{dirname}/*", File::FNM_DOTMATCH) do |file|
121
+ return unless %w(. ..).include? File.basename(file)
122
+ end
123
+
124
+ log :deleting, dirname
125
+ Dir.delete dirname
126
+ end
127
+
128
+ def write(string)
129
+ return if skip? :write, string
130
+ stash string
131
+ create string
132
+ end
133
+
134
+ def stash(string)
135
+ Dir[expand_path(string)].each do |file|
136
+ next if new_file?(file) || File.exists?(stashed(file))
137
+ next if skip? :stash, file
138
+ log :stashing, original(file)
139
+ File.rename original(file), stashed(file)
140
+ end
141
+ end
142
+
143
+ def create(string)
144
+ new_files = []
145
+
146
+ ["shared", "rails-#{rails_version}"].each do |dir|
147
+ path = File.expand_path dir, templates_path
148
+ next unless File.exists? path
149
+ root = Pathname.new path
150
+ Dir[File.expand_path(string, root.realpath)].each do |file|
151
+ next if File.directory? file
152
+ next if skip? :create, file
153
+ begin
154
+ @relative_path = Pathname.new(file).relative_path_from(root).to_s
155
+ @locals = nil
156
+ new_files << @relative_path unless new_file?(expand_path(@relative_path)) || File.exists?(stashed(@relative_path))
157
+ log :creating, expand_path(@relative_path)
158
+ template file, expand_path(@relative_path), :verbose => false
159
+ ensure
160
+ @relative_path = nil
161
+ end
162
+ end
163
+ end
164
+
165
+ unless new_files.empty?
166
+ File.open(expand_path(".new_files"), "a") do |file|
167
+ file << new_files.collect{|x| "#{x}\n"}.join("")
168
+ end
169
+ end
170
+ end
171
+
172
+ def copy(source, destination)
173
+ log :copying, "#{source} -> #{destination}"
174
+ FileUtils.cp expand_path(source), expand_path(destination)
175
+ end
176
+
177
+ def generate(*args)
178
+ return if skip? :generate, args.first
179
+ command = case rails_version
180
+ when 2
181
+ "script/generate"
182
+ when 3
183
+ "rails g"
184
+ end
185
+
186
+ execute "#{command} #{args.join(" ")}"
187
+ @ran_generator = true
188
+ end
189
+
190
+ private
191
+
192
+ def method_missing(method, *args)
193
+ if locals.include? method
194
+ locals[method]
195
+ else
196
+ super
197
+ end
198
+ end
199
+ end
200
+
201
+ end
202
+ end
203
+ end
@@ -0,0 +1,106 @@
1
+ module GemSuit
2
+ class Application < ::Thor::Group
3
+ module Test
4
+
5
+ def self.included(base)
6
+ base.extend ClassMethods
7
+ base.send :include, InstanceMethods
8
+ end
9
+
10
+ module ClassMethods
11
+ def create_test_database
12
+ self.new.create_test_database
13
+ end
14
+
15
+ def setup(*args)
16
+ self.new.setup *args
17
+ end
18
+
19
+ def test(*args)
20
+ self.new.test *args
21
+ end
22
+ end
23
+
24
+ module InstanceMethods
25
+ attr_accessor :config, :verbose
26
+
27
+ def description
28
+ # Implement in subclass
29
+ end
30
+
31
+ def prepare
32
+ # Implement in subclass
33
+ end
34
+
35
+ def create_test_database
36
+ write "config/database.yml"
37
+ execute "RAILS_ENV=test rake db:create"
38
+ ensure
39
+ restore "**/*.#{STASHED_EXT}"
40
+ end
41
+
42
+ def setup(config = {})
43
+ @skipped_files = nil
44
+ @config = config
45
+
46
+ log "\n".ljust 145, "="
47
+ log "Setting up test environment for Rails #{[rails_version, description].compact.join(" - ")}\n"
48
+ log "\n".rjust 145, "="
49
+
50
+ restore_all
51
+ stash_all
52
+ bundle_install
53
+
54
+ prepare
55
+ prepare_database
56
+ @prepared = true
57
+ end
58
+
59
+ def test(config = {})
60
+ setup config
61
+
62
+ log "\n".rjust 145, "="
63
+ log "Environment for Rails #{[rails_version, description].compact.join(" - ")} is ready for testing"
64
+ log "=" .ljust 144, "="
65
+
66
+ run_environment
67
+ end
68
+
69
+ def prepare_database
70
+ return if @db_prepared
71
+ if @ran_generator
72
+ stash "db/schema.rb"
73
+ execute "rake db:test:purge"
74
+ execute "RAILS_ENV=test rake db:migrate"
75
+ else
76
+ execute "rake db:test:load"
77
+ end
78
+ @db_prepared = true
79
+ end
80
+
81
+ def run_environment
82
+ ENV["RAILS_ENV"] = "test"
83
+
84
+ require File.expand_path("config/environment.rb", root_path)
85
+ require "#{"rails/" if Rails::VERSION::MAJOR >= 3}test_help"
86
+
87
+ begin
88
+ require "gem_suit/integration_test"
89
+ rescue LoadError
90
+ gem_suit_path = File.expand_path "../../..", __FILE__
91
+ $:.unshift gem_suit_path
92
+ require "gem_suit/integration_test"
93
+ end
94
+
95
+ Dir[File.expand_path("../#{File.basename(self.class.__file__, ".rb")}/**/*.rb", self.class.__file__)].each do |file|
96
+ next if skip? :require, file
97
+ require file
98
+ end
99
+
100
+ log "\nRunning Rails #{Rails::VERSION::STRING}\n\n"
101
+ end
102
+ end
103
+
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,42 @@
1
+ module GemSuit
2
+ class Application < ::Thor::Group
3
+ module Utils
4
+ class Gemfile
5
+
6
+ # A simple class which derives a Gemfile
7
+
8
+ attr_accessor :source
9
+
10
+ def initialize(*files)
11
+ @files = files
12
+ end
13
+
14
+ def gems
15
+ @gems ||= begin
16
+ @gems = {}
17
+ gem_file = @files.detect{|file| File.exists? file}
18
+ gem_specs = File.readlines(gem_file).join "\n"
19
+ instance_eval gem_specs
20
+ gems
21
+ end
22
+ end
23
+
24
+ def source(src)
25
+ source = src
26
+ end
27
+
28
+ def gem(name, *args)
29
+ options = (args.pop if args.last.is_a?(Hash)) || {}
30
+ options[:version] = args.first if args.first.is_a?(String)
31
+ @gems[name] = options
32
+ end
33
+
34
+ def method_missing(method, *args)
35
+ # e.g. group :test do
36
+ # end
37
+ end
38
+
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,135 @@
1
+ STDOUT.sync = true
2
+
3
+ require "gem_suit/application/utils/gemfile"
4
+
5
+ module GemSuit
6
+ class Application < ::Thor::Group
7
+ module Utils
8
+
9
+ def self.included(base)
10
+ base.extend ClassMethods
11
+ base.send :include, InstanceMethods
12
+ end
13
+
14
+ module ClassMethods
15
+ attr_accessor :__file__
16
+
17
+ def inherited(klass)
18
+ klass.__file__ = caller.first[/^[^:]+/]
19
+ end
20
+
21
+ def bundle_install
22
+ self.new.bundle_install
23
+ end
24
+ end
25
+
26
+ module InstanceMethods
27
+ attr_accessor :root_path, :validate_root_path
28
+
29
+ def bundle_install
30
+ return unless bundle_install?
31
+ if verbose
32
+ execute "bundle install", "(this can take several minutes...)"
33
+ else
34
+ puts "Running `bundle install` (this can take several minutes...)".yellow
35
+ puts "(in #{root_path})"
36
+ `cd #{root_path} && bundle install`
37
+ end
38
+ end
39
+
40
+ def bundle_install?
41
+ `cd #{root_path} && bundle check`.any?{|line| line.include? "`bundle install`"}
42
+ end
43
+
44
+ def validate_root_path!(path)
45
+ unless path.match(/rails-\d/)
46
+ log "Running a #{self.class.name} instance from an invalid path: '#{path}' needs to match ".red + "/rails-\\d/".yellow
47
+ exit
48
+ end
49
+ end
50
+
51
+ def root_path
52
+ (@root_path || (Rails.root if defined? Rails) || begin
53
+ dir = File.expand_path "../..", self.class.__file__
54
+ File.exists?("#{dir}/config/environment.rb") ? dir : Dir[File.expand_path("#{dir}/../rails-*/dummy")].last
55
+ end).to_s.tap do |path|
56
+ validate_root_path! path if validate_root_path
57
+ self.class.source_root = path
58
+ end
59
+ end
60
+
61
+ def shared_path
62
+ File.expand_path("../../shared", root_path)
63
+ end
64
+
65
+ def templates_path
66
+ File.expand_path("../../templates", root_path)
67
+ end
68
+
69
+ def expand_path(path)
70
+ Pathname.new(path).absolute? ?
71
+ path :
72
+ File.expand_path(path, root_path)
73
+ end
74
+
75
+ def original(file)
76
+ expand_path file.gsub(/\.#{STASHED_EXT}$/, "")
77
+ end
78
+
79
+ def stashed(file)
80
+ expand_path file.match(/\.#{STASHED_EXT}$/) ? file : "#{file}.#{STASHED_EXT}"
81
+ end
82
+
83
+ def new_file?(file)
84
+ return false unless File.exists? expand_path(".new_files")
85
+ root = Pathname.new root_path
86
+ relative_path = Pathname.new(file).relative_path_from(root).to_s
87
+ File.readlines(expand_path(".new_files")).any?{|line| line.strip == relative_path}
88
+ end
89
+
90
+ def rails_version
91
+ root_path.match(/\/rails-(\d)\//)[1].to_i
92
+ end
93
+
94
+ def rails_gem_version
95
+ case rails_version
96
+ when 2
97
+ File.readlines(expand_path("config/environment.rb")).each do |line|
98
+ match = line.match /RAILS_GEM_VERSION\s*=\s*["']([\d\.]+)["']/
99
+ return $1 if match
100
+ end
101
+ when 3
102
+ files = [expand_path(stashed("Gemfile")), expand_path("Gemfile")]
103
+ Gemfile.new(*files).gems["rails"][:version]
104
+ end
105
+ end
106
+
107
+ def mysql_password
108
+ file = File.expand_path("mysql", shared_path)
109
+ "#{File.new(file).read}".strip if File.exists? file
110
+ end
111
+
112
+ def execute(command, text = "")
113
+ return if command.to_s.gsub(/\s/, "").size == 0
114
+ log :executing, "#{command} #{text}"
115
+ `cd #{root_path} && #{command}`
116
+ end
117
+
118
+ def log(action, string_or_force = nil, force = nil)
119
+ if %w(TrueClass FalseClass).include? string_or_force.class.name
120
+ string = nil
121
+ force = string_or_force
122
+ else
123
+ string = string_or_force
124
+ force = false
125
+ end
126
+ return unless verbose || force
127
+ output = [string || action]
128
+ output.unshift action.to_s.capitalize.ljust(10, " ") unless string.nil?
129
+ puts output.join(" ")
130
+ end
131
+ end
132
+
133
+ end
134
+ end
135
+ end
@@ -0,0 +1,42 @@
1
+ require "optparse"
2
+ require "thor/group"
3
+ require "gem_suit/application/actions"
4
+ require "gem_suit/application/test"
5
+
6
+ module GemSuit
7
+ class Application < ::Thor::Group
8
+
9
+ include Application::Actions
10
+ include Application::Test
11
+
12
+ STASHED_EXT = "stashed"
13
+
14
+ def initialize(options = {:validate_root_path => true})
15
+ super [], {}, {}
16
+
17
+ begin
18
+ OptionParser.new do |opts|
19
+ opts.on "-v", "--[no-]verbose" do |v|
20
+ options[:verbose] ||= v
21
+ end
22
+ end.parse! ARGV
23
+ rescue OptionParser::InvalidOption
24
+ end
25
+
26
+ options.each do |key, value|
27
+ send :"#{key}=", value
28
+ end
29
+ end
30
+
31
+ class << self
32
+ def source_root
33
+ @source_root
34
+ end
35
+
36
+ def source_root=(path)
37
+ @source_root = path
38
+ end
39
+ end
40
+
41
+ end
42
+ end
@@ -0,0 +1,54 @@
1
+ module GemSuit
2
+ class CLI < Thor
3
+ module Application
4
+ class IOBuffer
5
+
6
+ def self.capture(&block)
7
+ self.new.capture &block
8
+ end
9
+
10
+ def capture(&block)
11
+ @data = nil
12
+ data.record do
13
+ yield self
14
+ end
15
+ end
16
+
17
+ def log(line)
18
+ data << "GemSuit: #{line}"
19
+ end
20
+
21
+ def execute(command)
22
+ IO.popen(command) do |io|
23
+ until io.eof?
24
+ puts (data << io.gets).last
25
+ end
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def data
32
+ @data ||= BufferData.new
33
+ end
34
+
35
+ class BufferData
36
+ attr_reader :output, :start, :finish
37
+
38
+ def record(&block)
39
+ @output = []
40
+ @start = Time.now
41
+ yield
42
+ @finish = Time.now
43
+ self
44
+ end
45
+
46
+ def <<(string)
47
+ @output << string
48
+ end
49
+ end
50
+
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Load the test files from the command line.
4
+
5
+ ARGV.each { |f| load f unless f =~ /^-/ }