gem_suit 0.1.0

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 (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 =~ /^-/ }