railsapp_factory 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,43 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ /vendor/bundle
5
+ .yardoc
6
+ Gemfile.lock
7
+
8
+ config/initializers/secret_token.rb
9
+ .config
10
+
11
+ InstalledFiles
12
+ .yardoc
13
+ _yardoc
14
+ coverage
15
+ doc/
16
+ lib/bundler/man
17
+ pkg
18
+ rdoc
19
+ spec/reports
20
+ test/tmp
21
+ test/version_tmp
22
+ tmp
23
+ /coverage/
24
+ /public/system/*
25
+ /spec/tmp/*
26
+
27
+ *.sassc
28
+ .sass-cache
29
+ /log/*
30
+ /db/*.sqlite3
31
+
32
+ capybara-*.html
33
+
34
+ .rspec
35
+ .rvmrc
36
+ .ruby-version
37
+
38
+ .project
39
+
40
+ rerun.txt
41
+ pickle-email-*.html
42
+
43
+ /bin
data/.travis.yml ADDED
@@ -0,0 +1,36 @@
1
+ language: ruby
2
+ notifications:
3
+ email:
4
+ on_success: change
5
+ on_failure: always
6
+
7
+ before_install:
8
+ - gem update --system $RUBYGEMS_VERSION
9
+
10
+ - gem --version
11
+ - gem install bundler
12
+ - bundle --version
13
+ - mkdir -p tmp/bundle
14
+
15
+ bundler_args: "--binstubs"
16
+
17
+ rvm:
18
+ # - 1.8.7 - include below so RUBYGEMS version gets set
19
+ - 1.9.2
20
+ - 1.9.3
21
+ - 2.0.0
22
+ - 2.1.0
23
+ - jruby-head
24
+ - rbx
25
+
26
+ script: bin/rake build spec install
27
+
28
+ matrix:
29
+ allow_failures:
30
+ # error: sqlite3-1.3.9/lib/sqlite3/sqlite3_native.so: undefined symbol: RBIGNUM_DIGITS
31
+ - rvm: rbx
32
+ include:
33
+ # set RUBYGEMS_VERSION=1.8.25 for ruby 1.8.7 to avoid "undefined method `source_index' for Gem:Module" errors
34
+ - rvm: 1.8.7
35
+ env: RUBYGEMS_VERSION=1.8.25
36
+
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in railsapp_factory.gemspec
4
+ gemspec
5
+
6
+
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Ian Heggie
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,168 @@
1
+ # RailsappFactory
2
+
3
+ Rails application factory to make testing gems against multiple versions easier
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'railsapp_factory'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install railsapp_factory
18
+
19
+ ## Usage
20
+
21
+ To get the list of available versions (that can be run with the ruby version you are currently running):
22
+
23
+ RailsappFactory.versions
24
+
25
+ Depending on the ruby version in use, it will suggest versions '2.3', '3.0', '3.1', '3.2' and '4.0'. The latest in each series will be downloaded. You can also specify a specific version, eg '3.2.8' or 'edge' (edge has to be selected manually as unforsean changes may break the standard build process).
26
+
27
+ The INTENT is to end up with:
28
+
29
+ To test a gem (health_check in this case), run:
30
+
31
+ RailsappFactory.versions.each do |version|
32
+ railsapp = RailsappFactory.new(version) # also accepts "edge"
33
+ railsapp.timeout = 300 # timeout operations after 300 seconds
34
+ railsapp.db = 'mysql2' # database gem to use
35
+ railsapp.template = File.expand_path('templates/add-file.rb', File.dirname(__FILE__)) # ie full path name
36
+ # OR
37
+ railsapp.template = "http://example.com/example.rb"
38
+
39
+ # you can also append to the template defined above, or start a custom template from scratch by using append_to_template
40
+ # A temp file is created containing the combined template information
41
+
42
+ railsapp.append_to_template <<-EOF
43
+ gem "my_gem_name", :path => '#{File.expand_path('templates/add-file.rb', '..')}'
44
+ bundle install
45
+ generate(:scaffold, "person name:string")
46
+ route "root to: 'people#index'"
47
+ rake("db:migrate")
48
+ EOF
49
+
50
+ # following commands return a struct with stdout, stderr and exit_status
51
+ # and an exception is raised if build fails
52
+
53
+ puts "Latest version in #{railsapp.release} series is #{railsapp.version}"
54
+
55
+ railsapp.build # run,rake,runner,console will all trigger this if you forget
56
+
57
+ railsapp.append_to_template 'gem "halo"'
58
+
59
+ railsapp.process_template # apply template with rake command
60
+
61
+ # runs an expression in runner and ruby respectively, and uses to_json to return the result.
62
+ # exceptions are passed through, except for syntax errors
63
+
64
+ railsapp.rails_eval 'Some.rails(code)' # with rails loaded
65
+ railsapp.ruby_eval 'Some.ruby(code)' # without rails (except for 2.3* which requires rails to pass results back)
66
+
67
+ railsapp.run
68
+
69
+ # check server is actually running
70
+ railsapp.alive?.should be_true
71
+
72
+ # some helpers for constructing urls (strings)
73
+ puts "home url: #{railsapp.url}"
74
+ puts "url: #{railsapp.url('/search', {:author => {:name => 'fred'}})"
75
+
76
+ puts "Instance of URI: #{railsapp.uri('/some/path', :name => 'value')}"
77
+
78
+ puts "port: #{railsapp.port}"
79
+
80
+ railsapp.stop
81
+
82
+ # override ENV passed to rails app processes
83
+ railsapp.override_ENV['EDITOR'] = 'vi'
84
+
85
+ railsapp.in_app do
86
+ # runs command in rails root dir without the extra environment variables bundler exec sets"
87
+ system 'some shell command'
88
+ end
89
+
90
+ railsapp.system_in_app 'another shell command'
91
+
92
+ railsapp.destroy
93
+ end
94
+
95
+ # removes all temp directories - TODO: stop any running servers
96
+ RailsappFactory.cleanup
97
+
98
+ If you use rvm (eg travis.ci) or rbenv (like I do), then you also go the other way,
99
+ and run you tests in a specific version of ruby (eg to use the later syntax), but build and/or run the rails app
100
+ with the various ruby versions you have installed.
101
+
102
+ It will attempt to find rvm / rbenv through environment variables first, the check the PATH, and lastly check the standard install directories under $HOME.
103
+ This handles RubyMine's clearing of environment variables, running rbenv or rvm in command mode only whilst running the system ruby.
104
+
105
+ RailsappFactory.rubies # lists all ruby versions available (in the format the version manager prefers)
106
+ RailsappFactory.rvm? # is RVM available
107
+ RailsappFactory.rbenv? # is rbenv available
108
+ RailsappFactory.has_ruby_version_manager? # either available?
109
+ RailsappFactory.using_system_ruby? # simple check that there are no rvm or rbenv specific directories in PATH
110
+
111
+ # example without having to build the actual rails app (run ruby commands)
112
+
113
+ RailsappFactory.rubies.each do |ruby_v|
114
+ it "provides a command prefix that will run ruby #{ruby_v}" do
115
+ prefix = RailsappFactory.ruby_command_prefix(ruby_v)
116
+ actual_ruby_v=`#{prefix} ruby -v`
117
+ end
118
+ end
119
+
120
+ ### Future Development
121
+
122
+ At the moment the rubies system isn't smart enough to work out which ruby works with which rails versions,
123
+ nor know the jruby and rbx switches that change the language modes. I intend to develop that area further.
124
+ Suggestions welcome if checking RUBY_VERSION is sufficient, along with checking if the ruby_version changes
125
+ from the default with `JRUBY_OPTS=--1.9, --1.8, --2.0` as well as `RBXOPT=-X18, -X19, -X20, -X21`,
126
+ or if I need to do more.
127
+
128
+ I am thinking of adding multiple entries in the rubies list for rbx / jruby if the env variables trigger a change in
129
+ RUBY_VERSION, eg:
130
+ * jruby-1.7.9
131
+ * jruby-1.7.9/18mode
132
+ * jruby-1.7.9/19mode
133
+ * jruby-1.7.9/20mode
134
+
135
+ # example for a rail application
136
+
137
+ @factory = RailsappFactory.new # chooses the most recent rails version compatible with RUBY_VERSION
138
+
139
+ @factory.rubies.each do |ruby_v|
140
+ @factory.use(ruby_v)
141
+ actual_ruby_v = @factory.rails_eval('RUBY_VERSION')
142
+ puts "Using #{@factory.using} ruby"
143
+ end
144
+ @factory.use(nil) # revert to the default ruby (in PATH)
145
+
146
+ # You can also pass use a block, and it will revert the ruby version afterwards
147
+ @factory.use(ruby_v)
148
+ actual_ruby_v = @factory.rails_eval('RUBY_VERSION')
149
+ end
150
+
151
+
152
+ I am considering get/put like integration tests have, but requires some thought first to be non rails version specific
153
+
154
+ #TODO: railsapp.get("/some/path") - returns status same as get in tests
155
+ #TODO: railsapp.post("/another/path", :author => { :name => 'fred' } ) - returns status same as post in tests
156
+
157
+
158
+ ## Contributing
159
+
160
+ 1. Fork it
161
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
162
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
163
+ 4. Push to the branch (`git push origin my-new-feature`)
164
+ 5. Create new Pull Request
165
+
166
+ ## License
167
+
168
+ MIT - See LICENSE.txt
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require 'bundler/gem_tasks'
2
+
3
+ require 'rspec/core/rake_task'
4
+
5
+ RSpec::Core::RakeTask.new('spec')
6
+
7
+ # If you want to make this the default task
8
+ task :default => :spec
@@ -0,0 +1,7 @@
1
+ class RailsappFactory
2
+
3
+ class BuildError < RuntimeError
4
+
5
+ end
6
+
7
+ end
@@ -0,0 +1,141 @@
1
+ require 'fileutils'
2
+ require 'tmpdir'
3
+ require 'railsapp_factory'
4
+
5
+ class RailsappFactory
6
+ module BuildMethods
7
+
8
+ # class variables used in module:
9
+ # @base_dir
10
+ # @built
11
+ # @bundled
12
+ # @root
13
+ # @release
14
+
15
+ def destroy
16
+ if @base_dir
17
+ stop
18
+ # keep last built example in last.VERSION
19
+ FileUtils.rm_rf "#{TMPDIR}/last.#{@version}"
20
+ FileUtils.mv @base_dir, "#{TMPDIR}/last.#{@version}" if File.directory?(@base_dir)
21
+ FileUtils.rm_rf @base_dir # to be sure, to be sure!
22
+ end
23
+ @base_dir = nil
24
+ @built = false
25
+ @bundled = false
26
+ @root = nil
27
+ @release = nil
28
+ end
29
+
30
+ def build
31
+ if built?
32
+ self.logger.info "Already built Rails #{@version} app in directory #{root}"
33
+ process_template
34
+ return false
35
+ end
36
+ self.use_template 'templates/add_json_pure.rb'
37
+ if self.version =~ /^2/
38
+ self.use_template 'templates/use_bundler_with_rails23.rb'
39
+ else
40
+ self.use_template 'templates/add_necessary_gems.rb'
41
+ end
42
+ new_arg = @version =~ /^2/ ? '' : ' new'
43
+ other_args = @version =~ /^2/ ? '' : '--no-rc --skip-bundle --force'
44
+ other_args <<= ' --edge' if @version == 'edge'
45
+ other_args <<= " -m #{self.template}" if self.template
46
+
47
+ self.logger.info "Creating Rails #{@version} app in directory #{root}"
48
+ unless in_app('.') { Kernel.system "sh -xc '#{rails_command} #{new_arg} #{root} -d #{db} #{other_args}' #{append_log 'rails_new.log'}" }
49
+ @built = true # avoid repeated build attempts
50
+ raise BuildError.new("rails #{new_arg}railsapp failed #{see_log 'rails_new.log'}")
51
+ end
52
+ @built = true
53
+ clear_template
54
+ expected_file = File.join(root, 'config', 'environment.rb')
55
+ raise BuildError.new("error building railsapp - missing #{expected_file}") unless File.exists?(expected_file)
56
+
57
+ command = "sh -xc 'bundle install --binstubs .bundle/bin' #{append_log 'bundle.log'}"
58
+ self.logger.info "Installing gems with binstubs using command: #{command}"
59
+ unless system_in_app command
60
+ raise BuildError.new("bundle install returned exit status #{$?} #{see_log 'bundle.log'}")
61
+ end
62
+ @bundled = true
63
+ raise BuildError.new("error installing gems - Gemfile.lock missing #{see_log 'bundle.log'}") unless File.exists?(File.join(root, 'Gemfile.lock'))
64
+ true
65
+ end
66
+
67
+ def built?
68
+ @built
69
+ end
70
+
71
+ def bundled?
72
+ @bundled
73
+ end
74
+
75
+ # release installed as reported by the rails command itself
76
+ def release
77
+ @release ||= begin
78
+ cmd = rails_command
79
+ self.logger.debug "Getting release using command: #{cmd} '-v'"
80
+ r = in_app(RailsappFactory::TMPDIR) { `#{cmd} '-v'` }.chomp.sub(/^Rails */, '')
81
+ self.logger.debug "Release: #{r}"
82
+ r
83
+ end
84
+ end
85
+
86
+ def root
87
+ @root = File.join(base_dir, 'railsapp')
88
+ end
89
+
90
+ private
91
+
92
+ def rails_command
93
+ rails_cmd_dir = "#{RailsappFactory::TMPDIR}/rails-#{@version}"
94
+ rails_path = "#{rails_cmd_dir}/bin/rails"
95
+ #command = '"%s" "%s"' % [Gem.ruby, rails_path]
96
+ command = rails_path
97
+ unless File.exists?(rails_path)
98
+ self.logger.info "Creating bootstrap Rails #{@version} as #{rails_path}"
99
+ FileUtils.rm_rf rails_cmd_dir
100
+ FileUtils.mkdir_p rails_cmd_dir
101
+ Dir.chdir(rails_cmd_dir) do
102
+ create_Gemfile
103
+ Bundler.with_clean_env do
104
+ Kernel.system "sh -xc '#{bundle_command} install --binstubs' #{append_log 'bundle.log'}"
105
+ end
106
+ end
107
+ unless File.exists?(rails_path)
108
+ raise BuildError.new("Error getting rails_command: (#{rails_path})")
109
+ end
110
+ end
111
+ command
112
+ end
113
+
114
+ def create_Gemfile
115
+ version_spec = if @version == 'edge'
116
+ "github: 'rails/rails'"
117
+ elsif @version == '2.3-lts'
118
+ ":git => 'git://github.com/makandra/rails.git', :branch => '2-3-lts'"
119
+ elsif @version =~ /\.\d+\./
120
+ "'#{@version}'"
121
+ else
122
+ "'~> #{@version}.0'"
123
+ end
124
+ gemfile_content = <<-EOF
125
+ source '#{@gem_source}'
126
+ gem 'rails', #{version_spec}
127
+ EOF
128
+
129
+ File.open('Gemfile', 'w') { |f| f.puts gemfile_content }
130
+ self.logger.debug "Created Gemfile with: <<\n#{gemfile_content}>>"
131
+ end
132
+
133
+ def base_dir
134
+ @base_dir ||= begin
135
+ FileUtils.mkdir_p RailsappFactory::TMPDIR
136
+ Dir.mktmpdir("app-#{self.version.gsub(/\W/, '_')}-", RailsappFactory::TMPDIR)
137
+ end
138
+ end
139
+
140
+ end
141
+ end
@@ -0,0 +1,155 @@
1
+ require 'cgi'
2
+ require 'fileutils'
3
+
4
+ class RailsappFactory
5
+ module ClassMethods
6
+ # encodes url query arguments, incl nested
7
+ def encode_query(args, prefix = '', suffix = '')
8
+ query = ''
9
+ args.each do |key, value|
10
+ if value.is_a?(Hash)
11
+ query <<= encode_query(value, "#{prefix}#{key}[", "]#{suffix}")
12
+ else
13
+ query <<= '&' << CGI::escape(prefix + key.to_s + suffix) << '=' << CGI::escape(value.to_s)
14
+ end
15
+ end if args
16
+ if prefix == ''
17
+ query.sub(/^&/, '?')
18
+ else
19
+ query
20
+ end
21
+ end
22
+
23
+ def cleanup
24
+ FileUtils.rm_rf RailsappFactory::TMPDIR
25
+ end
26
+
27
+ def versions(ruby_v = RUBY_VERSION)
28
+ case (ruby_v.to_s)
29
+ when /^1\.8\.6/
30
+ %w{2.3}
31
+ when /^1\.8\.7/
32
+ %w{2.3 2.3-lts 3.0 3.1 3.2}
33
+ when /^1\.9\.1/
34
+ %w{2.3}
35
+ when /^1\.9\.2/
36
+ %w{3.0 3.1 3.2}
37
+ when /^1\.9\.3/
38
+ %w{3.0 3.1 3.2 4.0}
39
+ when /^2\.[01]/
40
+ %w{4.0}
41
+ when /^1\./
42
+ []
43
+ when ''
44
+ # all
45
+ %w{2.3 2.3-lts 3.0 3.1 3.2 4.0}
46
+ else
47
+ %w{4.0} # a guess!
48
+ end
49
+ end
50
+
51
+ def rubies # (rails_v = nil)
52
+ find_ruby_version_manager
53
+ ruby_command_prefix_template
54
+ result = if @@rbenv_path
55
+ `#{@@rbenv_path} versions --bare`
56
+ elsif @@rvm_path
57
+ `#{@@rvm_path} list strings`
58
+ else
59
+ ''
60
+ end.split(/\r?\n/)
61
+ # TODO: rework this to run each one and extract the RUBY_VERSION to work out what is compatible
62
+ # TODO: extend this with JRUBY_OPTS=--1.9, --1.8, --2.0 as well as RBXOPT=-X18, -X19, -X20, -X21 and check which ones actually change the RUBY_VERSION value
63
+ # TODO: rbx is outputting an extra nil as it is running as irb sometimes .. test this
64
+ # if rails_v.nil?
65
+ # result
66
+ #else
67
+ # rails_v_compare = rails_v.sub(/^(\d+\.\d+).*?(-lts)?$/, '\1\2')
68
+ # result.select do |ruby_v|
69
+ # rails_v.nil? || versions(ruby_v).include?(rails_v_compare)
70
+ # end
71
+ #end
72
+ end
73
+
74
+ def ruby_command_prefix(ruby_v = nil)
75
+ if ruby_v.to_s == ''
76
+ ''
77
+ else
78
+ ruby_command_prefix_template % ruby_v.to_s
79
+ end
80
+ end
81
+
82
+ def rbenv?
83
+ find_ruby_version_manager
84
+ @@rbenv_path
85
+ end
86
+
87
+ def rvm?
88
+ find_ruby_version_manager
89
+ @@rvm_path
90
+ end
91
+
92
+ def has_ruby_version_manager?
93
+ find_ruby_version_manager != ''
94
+ end
95
+
96
+ def using_system_ruby?
97
+ ENV['PATH'] !~ /\/\.?rbenv\/versions\// && ENV['PATH'] !~ /\/\.?rvm\/rubies\//
98
+ end
99
+
100
+ private
101
+
102
+ def list_rubies
103
+ find_ruby_version_manager
104
+ if @@rbenv_path
105
+ `#{@@rbenv_path} versions --bare`
106
+ elsif @@rvm_path
107
+ `#{@@rvm_path} list strings`
108
+ else
109
+ ''
110
+ end.split(/\r?\n/)
111
+ end
112
+
113
+ def ruby_command_prefix_template
114
+ @@ruby_command_prefix_template ||= begin
115
+ find_ruby_version_manager
116
+ if @@rbenv_path
117
+ "env 'RBENV_VERSION=%s' #{@@rbenv_path} exec"
118
+ elsif @@rvm_path
119
+ "#{@@rvm_path} '%s' do"
120
+ else
121
+ ''
122
+ end
123
+ end
124
+ end
125
+
126
+
127
+ def find_ruby_version_manager
128
+ @@found_ruby_version_manager ||= begin
129
+ @@rbenv_path = ENV['RBENV_ROOT'] ? "#{ENV['RBENV_ROOT']}/bin/rbenv" : nil
130
+ @@rvm_path = ENV['rvm_path'] ? "#{ENV['rvm_path']}/bin/rvm" : nil
131
+ unless @@rbenv_path || @@rvm_path
132
+ # RubyMine removes RBENV_PATH when a rbenv environment is selected
133
+ ENV['PATH'].split(':').each do |exec_path|
134
+ @@rbenv_path = "#{$1}/bin/rbenv" if exec_path =~ /^(.*\/\.?rbenv)\/(bin|versions)/
135
+ @@rvm_path = "#{$1}/bin/rbenv" if exec_path =~ /^(.*\/\.?rvm)\/(bin|rubies)/
136
+ break if @@rbenv_path || @@rvm_path
137
+ end
138
+ # In case we are running from system ruby and the shell environment is not set
139
+ unless @@rbenv_path || @@rvm_path
140
+ if File.exists? "#{ENV['HOME']}/.rbenv/bin/rbenv"
141
+ @@rbenv_path = "#{ENV['HOME']}/.rbenv/bin/rbenv"
142
+ elsif File.exists? "#{ENV['HOME']}/.rvm/bin/rvm"
143
+ @@rvm_path = "#{ENV['HOME']}/.rvm/bin/rvm"
144
+ end
145
+ end
146
+ end
147
+ @@rbenv_path = nil unless @@rbenv_path && File.exists?(@@rbenv_path)
148
+ @@rvm_path = nil if @@rbenv_path || !@@rvm_path || !File.exists?(@@rvm_path)
149
+ @@rbenv_path || @@rvm_path || ''
150
+ end
151
+ end
152
+
153
+ end
154
+
155
+ end