diff --git a/CHANGELOG.md b/CHANGELOG.md index 99fb00ad..998f0f98 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ - #583: fix: Create config dir if it's missing for simpler usage - #588: fix: NullPointerException during shutdown with executable war files - #589: fix: Jetty wars don't have console logging enabled by default +- #617: fix: correct ability to configure/override/force specific traits via Config.new(forced_traits: [...]) - #591: chore: clean up obsolete & deprecated code from 2.0.x and old Bundler versions - #592: chore: relax rubyzip requirement to allow rubyzip 2.x - #593: chore: relax jruby-rack requirement to allow compatibility with upcoming 1.3.x diff --git a/README.rdoc b/README.rdoc index a00937a1..f46e947b 100644 --- a/README.rdoc +++ b/README.rdoc @@ -185,12 +185,11 @@ of how to configure Warbler to package Camping and Sinatra apps. If the default settings are not appropriate for your application, you can customize Warbler's behavior. To customize files, libraries, and gems included -in the .war file, you'll need a config/warble.rb file. There a two ways of -doing this. With the gem, simply run +in the .war file, you'll need a +config/warble.rb+ file. To generate one: warble config -Finally, edit the +config/warble.rb+ to your taste. The generated file is +Edit the +config/warble.rb+ to your taste. The generated file is fully-documented with the available options and default values. === Archive Layout diff --git a/lib/warbler/config.rb b/lib/warbler/config.rb index 365ef079..e3a9f2a1 100644 --- a/lib/warbler/config.rb +++ b/lib/warbler/config.rb @@ -29,8 +29,8 @@ class Config # Traits: an array of trait classes corresponding to # characteristics of the project that are either auto-detected or - # configured. - attr_accessor :traits + # forced enabled during `Config.new(forced_traits: [...]) do |config|` + attr_reader :traits # Directory where the war file will be written. Can be used to direct # Warbler to place your war file directly in your application server's @@ -182,8 +182,9 @@ class Config attr_reader :warbler_templates attr_reader :warbler_scripts - def initialize(warbler_home = WARBLER_HOME) - super() + # @param forced_traits [Array, nil] optional array of Warbler::Trait types to force rather than auto-detecting them + def initialize(warbler_home = WARBLER_HOME, forced_traits: nil) + super(forced_traits) @warbler_home = warbler_home @warbler_templates = "#{WARBLER_HOME}/lib/warbler/templates" diff --git a/lib/warbler/jar.rb b/lib/warbler/jar.rb index ec6a0b7b..1b25162d 100644 --- a/lib/warbler/jar.rb +++ b/lib/warbler/jar.rb @@ -176,6 +176,7 @@ def create(config_or_path) # Invoke a hook to allow the project traits to add or modify the archive contents. def apply_traits(config) + puts "Applying traits #{config.traits}" unless silent? config.update_archive(self) end diff --git a/lib/warbler/traits.rb b/lib/warbler/traits.rb index 17d3499a..7e298f2d 100644 --- a/lib/warbler/traits.rb +++ b/lib/warbler/traits.rb @@ -5,6 +5,7 @@ # See the file LICENSE.txt for details. #++ +require 'set' require 'stringio' require 'tsort' @@ -15,10 +16,13 @@ module Warbler # the kind of project and how it should be packed into the jar or # war file. module Traits - attr_accessor :traits - - def initialize - @traits = auto_detect_traits + # @param forced_traits [Array, nil] array of Warbler::Trait types to force rather than auto-detecting them + def initialize(forced_traits) + @traits = if forced_traits.nil? || forced_traits.empty? + auto_detect_traits + else + validate_traits(forced_traits) + end end def auto_detect_traits @@ -45,6 +49,18 @@ def dump_traits @trait_objects = nil @traits.collect! {|t| t.name } end + + private + def validate_traits(requested_traits) + conflicts = Set.new + requested_traits.each do |trait| + trait.conflicts.each do |conflict| + conflicts += [trait, conflict] if requested_traits.include?(conflict) + end + end + raise "Some forced traits declare conflicts with each other: #{conflicts.to_a}" unless conflicts.empty? + TraitsDependencyArray.new(requested_traits).tsort + end end # Each trait class includes this module to receive shared functionality. @@ -53,6 +69,14 @@ module ClassMethods def requirements [] end + + def conflicts + [] + end + + def detect_any_conflicts? + conflicts.any? { |c| c.detect? } + end end def self.included(base) @@ -108,7 +132,7 @@ class TraitsDependencyArray < Array alias tsort_each_node each def tsort_each_child(node, &block) - node.requirements.each(&block) + node.requirements.select { |r| include?(r) }.each(&block) end end diff --git a/lib/warbler/traits/gemspec.rb b/lib/warbler/traits/gemspec.rb index 3a868713..e8f3a0b2 100644 --- a/lib/warbler/traits/gemspec.rb +++ b/lib/warbler/traits/gemspec.rb @@ -18,6 +18,10 @@ def self.detect? !Dir['*.gemspec'].empty? end + def self.conflicts + [ Traits::NoGemspec ] + end + def before_configure; require 'yaml' @spec_file = Dir['*.gemspec'].first @spec = File.open(@spec_file) { |f| Gem::Specification.from_yaml(f) } rescue Gem::Specification.load(@spec_file) diff --git a/lib/warbler/traits/jar.rb b/lib/warbler/traits/jar.rb index b9690403..a13d765a 100644 --- a/lib/warbler/traits/jar.rb +++ b/lib/warbler/traits/jar.rb @@ -17,7 +17,11 @@ class Jar include Trait def self.detect? - !War.detect? + !detect_any_conflicts? + end + + def self.conflicts + [ Traits::War ] end def before_configure diff --git a/lib/warbler/traits/nogemspec.rb b/lib/warbler/traits/nogemspec.rb index c7384f56..82dc9d12 100644 --- a/lib/warbler/traits/nogemspec.rb +++ b/lib/warbler/traits/nogemspec.rb @@ -16,7 +16,11 @@ class NoGemspec include ExecutableHelper def self.detect? - Jar.detect? && !Gemspec.detect? + Jar.detect? && !detect_any_conflicts? + end + + def self.conflicts + [ Traits::Gemspec ] end def before_configure diff --git a/lib/warbler/traits/rack.rb b/lib/warbler/traits/rack.rb index 4c5356f4..3f9f5753 100644 --- a/lib/warbler/traits/rack.rb +++ b/lib/warbler/traits/rack.rb @@ -12,7 +12,11 @@ class Rack include Trait def self.detect? - !Rails.detect? && (File.exist?("config.ru") || !Dir['*/config.ru'].empty?) + (File.exist?("config.ru") || !Dir['*/config.ru'].empty?) && !detect_any_conflicts? + end + + def self.conflicts + [ Traits::Rails ] end def self.requirements diff --git a/lib/warbler/traits/rails.rb b/lib/warbler/traits/rails.rb index 8a31971b..45fe231a 100644 --- a/lib/warbler/traits/rails.rb +++ b/lib/warbler/traits/rails.rb @@ -15,6 +15,10 @@ def self.detect? File.exist?('config/environment.rb') end + def self.conflicts + [ Traits::Rack ] + end + def self.requirements [ Traits::War ] end diff --git a/lib/warbler/traits/war.rb b/lib/warbler/traits/war.rb index a94af5cd..af28a0f3 100644 --- a/lib/warbler/traits/war.rb +++ b/lib/warbler/traits/war.rb @@ -21,6 +21,10 @@ def self.detect? Traits::Rails.detect? || Traits::Rack.detect? end + def self.conflicts + [ Traits::Jar ] + end + def before_configure config.gem_path = DEFAULT_GEM_PATH config.pathmaps = default_pathmaps diff --git a/spec/warbler/bundler_spec.rb b/spec/warbler/bundler_spec.rb index b22b6523..3f3ebcdb 100644 --- a/spec/warbler/bundler_spec.rb +++ b/spec/warbler/bundler_spec.rb @@ -33,6 +33,10 @@ def bundle_install(*args) let(:config) { drbclient.config(@extra_config) } let(:jar) { drbclient.jar } + def apply_silently + silence { jar.apply(config) } + end + context "in a war project" do run_in_directory "spec/sample_war" cleanup_temp_files @@ -49,7 +53,7 @@ def bundle_install(*args) use_config do |config| config.gems << "rake" end - jar.apply(config) + apply_silently expect(file_list(%r{WEB-INF/Gemfile})).to_not be_empty expect(file_list(%r{WEB-INF/gems/specifications/rspec})).to_not be_empty expect(file_list(%r{WEB-INF/gems/specifications/rake})).to be_empty @@ -57,7 +61,7 @@ def bundle_install(*args) it "copies Gemfiles into the war" do File.open("Gemfile.lock", "w") {|f| f << "GEM"} - jar.apply(config) + apply_silently expect(file_list(%r{WEB-INF/Gemfile})).to_not be_empty expect(file_list(%r{WEB-INF/Gemfile.lock})).to_not be_empty end @@ -66,7 +70,7 @@ def bundle_install(*args) use_config do |config| config.gem_path = '/WEB-INF/jewels' end - jar.apply(config) + apply_silently expect(file_list(%r{WEB-INF/jewels/specifications/rspec})).to_not be_empty end @@ -76,7 +80,7 @@ def bundle_install(*args) it "works with :git entries in Gemfiles" do File.open("Gemfile", "w") {|f| f << "source 'file://#{@gem_dir}'\ngem 'tester', :git => '#{@gem_dir}'\n"} bundle_install '--local' - jar.apply(config) + apply_silently expect(file_list(%r{WEB-INF/gems/bundler/gems/tester[^/]*/lib/tester/version\.rb})).to_not be_empty expect(file_list(%r{WEB-INF/gems/bundler/gems/tester[^/]*/tester.gemspec})).to_not be_empty end @@ -84,7 +88,7 @@ def bundle_install(*args) it "bundles only the gemspec for :git entries that are excluded" do File.open("Gemfile", "w") {|f| f << "source 'https://rubygems.org'\ngem 'rake'\ngroup :test do\ngem 'tester', :git => '#{@gem_dir}'\nend\n"} bundle_install '--local' - jar.apply(config) + apply_silently expect(file_list(%r{WEB-INF/gems/bundler/gems/tester[^/]*/lib/tester/version\.rb})).to be_empty expect(file_list(%r{WEB-INF/gems/bundler/gems/tester[^/]*/tester.gemspec})).to_not be_empty end @@ -99,7 +103,7 @@ def bundle_install(*args) @gem_dir = generate_gem('tester', Dir.mktmpdir("gems-#{Time.now.to_i}")) File.open("Gemfile", "w") {|f| f << "source 'file://#{@gem_dir}'\ngem 'tester', :path => '#{@gem_dir}'\n"} bundle_install '--local' - silence { jar.apply(config) } + apply_silently expect(file_list(%r{tester})).to be_empty end @@ -110,7 +114,7 @@ def bundle_install(*args) @gem_dir = generate_gem('tester', 'gems/tester') # spec/sample_war/gems File.open("Gemfile", "w") {|f| f << "source 'https://rubygems.org'\ngem 'rake'\ngem 'tester', :path => 'gems/tester'\n"} bundle_install '--local' - jar.apply(config) + apply_silently expect(file_list(%r{tester})).to_not be_empty # included from :path as is expect(file_list(%r{WEB-INF/gems/bundler/gems/tester[^/]*/lib/tester/version\.rb})).to be_empty expect(file_list(%r{WEB-INF/gems/bundler/gems/tester[^/]*/tester.gemspec})).to be_empty @@ -123,7 +127,7 @@ def bundle_install(*args) it "does not bundle dependencies in the test group by default" do File.open("Gemfile", "w") {|f| f << "source 'https://rubygems.org'\ngem 'rake'\ngroup :test do\ngem 'rspec'\nend\n"} - jar.apply(config) + apply_silently expect(file_list(%r{WEB-INF/gems/gems/rake[^/]*/})).to_not be_empty expect(file_list(%r{WEB-INF/gems/gems/rspec[^/]*/})).to be_empty expect(file_list(%r{WEB-INF/gems/specifications/rake})).to_not be_empty @@ -160,7 +164,7 @@ def bundle_install(*args) it "works with :git entries in Gemfiles" do File.open("Gemfile", "w") {|f| f << "source 'file://#{@gem_dir}'\ngem 'tester', :git => '#{@gem_dir}'\n"} bundle_install '--local' - jar.apply(config) + apply_silently expect(file_list(%r{^bundler/gems/tester[^/]*/lib/tester/version\.rb})).to_not be_empty expect(file_list(%r{^bundler/gems/tester[^/]*/tester.gemspec})).to_not be_empty jar.add_init_file(config) @@ -182,18 +186,18 @@ def bundle_install(*args) it "includes the bundler gem" do bundle_install - jar.apply(config) + apply_silently expect(config.gems.detect{|k,v| k.name == 'bundler'}).to_not be nil expect(file_list(/bundler-/)).to_not be_empty end it "does not include the bundler cache directory" do - jar.apply(config) + apply_silently expect(file_list(%r{vendor/bundle})).to be_empty end it "includes ENV['BUNDLE_FROZEN'] in init.rb" do - jar.apply(config) + apply_silently contents = jar.contents('META-INF/init.rb') expect(contents.split("\n").grep(/ENV\['BUNDLE_FROZEN'\] = '1'/)).to_not be_empty end @@ -211,7 +215,7 @@ def bundle_install(*args) use_config do |config| config.features = %w{runnable} end - jar.apply(config) + apply_silently end after do @@ -247,7 +251,7 @@ def bundle_install(*args) it "includes the bundler gem" do bundle_install - jar.apply(config) + apply_silently expect(file_list(%r{gems/rake-13.3.0/lib})).to_not be_empty expect(file_list(%r{gems/bundler-})).to_not be_empty expect(file_list(%r{gems/bundler-.*/exe})).to_not be_empty diff --git a/spec/warbler/jar_spec.rb b/spec/warbler/jar_spec.rb index 9ffedcaa..3809e33c 100644 --- a/spec/warbler/jar_spec.rb +++ b/spec/warbler/jar_spec.rb @@ -26,6 +26,10 @@ def apply_extra_config(config) let(:config) { Warbler::Config.new {|c| apply_extra_config(c) } } let(:jar) { Warbler::Jar.new } + def apply_silently + silence { jar.apply(config) } + end + before do # We repeatedly load the same gemspec, but we modify it between # loads. RubyGems treats the filename as the cache key, not taking @@ -42,17 +46,17 @@ def apply_extra_config(config) end it "collects java libraries" do - jar.apply(config) + apply_silently expect(file_list(%r{^META-INF/lib/jruby-.*\.jar$})).to_not be_empty end it "adds a JarMain class" do - jar.apply(config) + apply_silently expect(file_list(%r{^JarMain\.class$})).to_not be_empty end it "adds an init.rb" do - jar.apply(config) + apply_silently expect(file_list(%r{^META-INF/init.rb$})).to_not be_empty end @@ -76,13 +80,13 @@ def apply_extra_config(config) end it "adds a main.rb" do - jar.apply(config) + apply_silently expect(file_list(%r{^META-INF/main.rb$})).to_not be_empty end it "adds script_files" do config.script_files << __FILE__ - jar.apply(config) + apply_silently expect(file_list(%r{^META-INF/#{File.basename(__FILE__)}$})).to_not be_empty end @@ -91,13 +95,13 @@ def apply_extra_config(config) use_config do |config| config.manifest_file = 'manifest' end - jar.apply(config) + apply_silently expect(jar.files['META-INF/MANIFEST.MF']).to eq "manifest" end it "accepts a MANIFEST.MF file if it exists in the project root" do touch 'MANIFEST.MF' - jar.apply(config) + apply_silently expect(jar.files['META-INF/MANIFEST.MF']).to eq "MANIFEST.MF" end @@ -135,7 +139,7 @@ def apply_extra_config(config) end it "detects gem dependencies" do - jar.apply(config) + apply_silently expect(file_list(%r{^gems/rubyzip.*/lib/(zip/)?zip.rb})).to_not be_empty expect(file_list(%r{^specifications/rubyzip.*\.gemspec})).to_not be_empty end @@ -147,7 +151,7 @@ def apply_extra_config(config) end it "loads the default executable in main.rb" do - jar.apply(config) + apply_silently contents = jar.contents('META-INF/main.rb') expect(contents).to eq "load 'sample_jar/sbin/sample_jar'" end @@ -155,7 +159,7 @@ def apply_extra_config(config) it "includes compiled .rb and .class files" do config.compiled_ruby_files = %w(lib/sample_jar.rb) jar.compile(config) - jar.apply(config) + apply_silently expect(file_list(%r{^sample_jar/lib/sample_jar\.class$})).to_not be_empty expect(jar.contents('sample_jar/lib/sample_jar.rb')).to match /load __FILE__\.sub/ end @@ -163,7 +167,7 @@ def apply_extra_config(config) it "includes only specified dirs" do config.dirs = %w(bin) jar.compile(config) - jar.apply(config) + apply_silently expect(file_list(%r{^sample_jar/lib$})).to be_empty expect(file_list(%r{^sample_jar/bin$})).to_not be_empty end @@ -172,7 +176,7 @@ def apply_extra_config(config) config.compiled_ruby_files = %w(lib/sample_jar.rb) config.excludes += FileList["lib/sample_jar.*"] jar.compile(config) - jar.apply(config) + apply_silently expect(file_list(%r{^sample_jar/lib/sample_jar\.class$})).to be_empty expect(jar.contents('sample_jar/lib/sample_jar.rb')).to_not match /load __FILE__\.sub/ end @@ -181,7 +185,7 @@ def apply_extra_config(config) config.compile_gems = true config.compiled_ruby_files = %w(lib/sample_jar.rb) jar.compile(config) - jar.apply(config) + apply_silently expect(file_list(%r{sample_jar.*\.rb$}).size).to eq 2 expect(file_list(%r{gems.*\.class$}).size).to be >= 45 # depending on RubyZip version end @@ -189,7 +193,7 @@ def apply_extra_config(config) it "does not compile included gems by default" do config.compiled_ruby_files = %w(lib/sample_jar.rb) jar.compile(config) - jar.apply(config) + apply_silently expect(file_list(%r{sample_jar.*\.rb$}).size).to eq 2 expect(file_list(%r{gems.*\.class$}).size).to eq 0 end @@ -198,7 +202,7 @@ def apply_extra_config(config) config.jrubyc_options = [ '--java' ] config.compiled_ruby_files = %w(lib/sample_jar.rb) jar.compile(config) - jar.apply(config) + apply_silently expect( FileList['*'] ).to include 'SampleJar.java' end @@ -223,7 +227,7 @@ def apply_extra_config(config) end it "loads the first bin/executable in main.rb" do - silence { jar.apply(config) } + apply_silently contents = jar.contents('META-INF/main.rb') expect(contents).to match /load.*sample_jar\/bin\/another_jar/ end @@ -232,7 +236,7 @@ def apply_extra_config(config) use_config do |config| config.executable = 'bin/sample_jar' end - silence { jar.apply(config) } + apply_silently contents = jar.contents('META-INF/main.rb') expect(contents).to match /load.*sample_jar\/bin\/sample_jar/ end @@ -255,14 +259,14 @@ def apply_extra_config(config) use_config do |config| config.gems << "rake" end - jar.apply(config) + apply_silently expect(file_list(%r{^gems/rake.*/lib/rake.rb})).to_not be_empty expect(file_list(%r{^specifications/rake.*\.gemspec})).to_not be_empty end it "collects all project files in the directory" do touch "extra.foobar" - jar.apply(config) + apply_silently expect(file_list(%r{^sample_jar/bin$})).to_not be_empty expect(file_list(%r{^sample_jar/test$})).to_not be_empty expect(file_list(%r{^sample_jar/lib/sample_jar.rb$})).to_not be_empty @@ -276,7 +280,7 @@ def apply_extra_config(config) end it "loads the first bin/executable in main.rb" do - jar.apply(config) + apply_silently contents = jar.contents('META-INF/main.rb') expect(contents).to match /load.*sample_jar\/bin\/sample_jar/ end @@ -285,7 +289,7 @@ def apply_extra_config(config) use_config do |config| config.executable = 'bin/sample_jar_extra' end - jar.apply(config) + apply_silently contents = jar.contents('META-INF/main.rb') expect(contents).to match /load.*sample_jar\/bin\/sample_jar_extra/ end @@ -295,13 +299,13 @@ def apply_extra_config(config) config.gems = [ "rake" ] config.executable = ['rake', 'bin/rake'] end - jar.apply(config) + apply_silently contents = jar.contents('META-INF/main.rb') expect(contents).to match /load.*gems\/rake.*\/bin\/rake/ end it "does not set parameters in main.rb" do - jar.apply(config) + apply_silently contents = jar.contents('META-INF/main.rb') expect(contents).to_not match /ARGV.*/m end @@ -310,7 +314,7 @@ def apply_extra_config(config) use_config do |config| config.executable_params = 'do_something' end - jar.apply(config) + apply_silently contents = jar.contents('META-INF/main.rb') expect(contents).to match /ARGV\.unshift.*do_something/m end @@ -332,7 +336,7 @@ def apply_extra_config(config) end it "collects files in public" do - jar.apply(config) + apply_silently expect(file_list(%r{^index\.html})).to_not be_empty end @@ -340,7 +344,7 @@ def apply_extra_config(config) use_config do |config| config.gems << 'rake' end - jar.apply(config) + apply_silently expect(file_list(%r{WEB-INF/gems/gems/rake.*/lib/rake.rb})).to_not be_empty expect(file_list(%r{WEB-INF/gems/specifications/rake.*\.gemspec})).to_not be_empty end @@ -350,7 +354,7 @@ def apply_extra_config(config) config.gems << 'virtus' config.gem_dependencies = true end - jar.apply(config) + apply_silently expect(file_list(%r{WEB-INF/gems/gems/axiom-types.*/lib})).to_not be_empty expect(file_list(%r{WEB-INF/gems/specifications/axiom-types.*\.gemspec})).to_not be_empty expect(file_list(%r{WEB-INF/gems/gems/equalizer.*/lib/equalizer/version.rb$})).to_not be_empty @@ -359,7 +363,7 @@ def apply_extra_config(config) # config.gems << "rdoc" # config.gem_dependencies = true #end - #jar.apply(config) + #apply_silently #expect(file_list(%r{WEB-INF/gems/gems/json.*/lib/json.rb})).to_not be_empty #expect(file_list(%r{WEB-INF/gems/specifications/json.*\.gemspec})).to_not be_empty end @@ -369,7 +373,7 @@ def apply_extra_config(config) config.gems << 'virtus' config.gem_dependencies = false end - jar.apply(config) + apply_silently expect(file_list(%r{WEB-INF/gems/gems/axiom-types.*/lib})).to be_empty expect(file_list(%r{WEB-INF/gems/specifications/axiom-types.*\.gemspec})).to be_empty expect(file_list(%r{WEB-INF/gems/gems/equalizer.*/lib/equalizer/version.rb$})).to be_empty @@ -377,7 +381,7 @@ def apply_extra_config(config) # config.gems << "rdoc" # config.gem_dependencies = false #end - #jar.apply(config) + #apply_silently #expect(file_list(%r{WEB-INF/gems/gems/json.*/lib/json.rb})).to be_empty #expect(file_list(%r{WEB-INF/gems/specifications/json.*\.gemspec})).to be_empty end @@ -397,13 +401,13 @@ def apply_extra_config(config) end it "does not include log files by default" do - jar.apply(config) + apply_silently expect(file_list(%r{WEB-INF/log})).to_not be_empty expect(file_list(%r{WEB-INF/log/.*\.log})).to be_empty end def expand_webxml - jar.apply(config) + apply_silently expect(jar.files).to include("WEB-INF/web.xml") require 'rexml/document' REXML::Document.new(jar.files["WEB-INF/web.xml"]).root.elements @@ -479,25 +483,25 @@ def expand_webxml it "uses a config/web.xml if it exists" do mkdir_p "config" touch "config/web.xml" - jar.apply(config) + apply_silently expect(jar.files["WEB-INF/web.xml"]).to eq "config/web.xml" end it "uses a config/web.xml.erb if it exists" do mkdir_p "config" File.open("config/web.xml.erb", "w") {|f| f << "Hi <%= webxml.public.root %>" } - jar.apply(config) + apply_silently expect(jar.files["WEB-INF/web.xml"]).to_not be nil expect(jar.files["WEB-INF/web.xml"].read).to eq "Hi /" end it "collects java libraries" do - jar.apply(config) + apply_silently expect(file_list(%r{WEB-INF/lib/jruby-.*\.jar$})).to_not be_empty end it "collects application files" do - jar.apply(config) + apply_silently expect(file_list(%r{WEB-INF/app$})).to_not be_empty expect(file_list(%r{WEB-INF/config$})).to_not be_empty expect(file_list(%r{WEB-INF/lib$})).to_not be_empty @@ -530,7 +534,7 @@ def expand_webxml use_config do |config| config.excludes += FileList['lib/tasks/utils.rake'] end - jar.apply(config) + apply_silently expect(file_list(%r{lib/tasks/utils.rake})).to be_empty end @@ -538,7 +542,7 @@ def expand_webxml use_config do |config| config.excludes += FileList['public/robots.txt'] end - jar.apply(config) + apply_silently expect(file_list(%r{robots.txt})).to be_empty end @@ -562,7 +566,7 @@ def expand_webxml end expect { Warbler::Task.new "warble", config - jar.apply(config) + apply_silently }.to raise_error(Gem::MissingSpecError) end @@ -579,14 +583,14 @@ def expand_webxml use_config do |config| config.gems = [dep] end - silence { jar.apply(config) } + apply_silently end it "copies loose java classes to WEB-INF/classes" do use_config do |config| config.java_classes = FileList["Rakefile"] end - jar.apply(config) + apply_silently expect(file_list(%r{WEB-INF/classes/Rakefile$})).to_not be_empty end @@ -619,13 +623,13 @@ class << t; public :instance_variable_get; end end it "moves jar files to WEB-INF/lib" do - jar.apply(config) + apply_silently expect(file_list(%r{WEB-INF/lib/app-sample.jar})).to_not be_empty expect(file_list(%r{WEB-INF/app/sample.jar})).to_not be_empty end it "leaves jar files alone that are already in WEB-INF/lib" do - jar.apply(config) + apply_silently expect(file_list(%r{WEB-INF/lib/lib-existing.jar})).to be_empty expect(file_list(%r{WEB-INF/lib/existing.jar})).to_not be_empty end @@ -633,7 +637,7 @@ class << t; public :instance_variable_get; end context "with move_jars_to_webinf_lib not set" do it "moves jar files to WEB-INF/lib" do - jar.apply(config) + apply_silently expect(file_list(%r{WEB-INF/lib/app-sample.jar})).to be_empty expect(file_list(%r{WEB-INF/app/sample.jar})).to_not be_empty end @@ -654,14 +658,14 @@ class << t; public :instance_variable_get; end end it "moves jar files that match to WEB-INF/lib" do - jar.apply(config) + apply_silently expect(file_list(%r{WEB-INF/lib/app-sample.jar})).to_not be_empty expect(file_list(%r{WEB-INF/lib/app-sample2.jar})).to_not be_empty expect(file_list(%r{WEB-INF/lib/.*?another.jar})).to be_empty end it "removes default jars not matched by filter from WEB-INF/lib" do - jar.apply(config) + apply_silently expect(file_list(%r{WEB-INF/lib/jruby-rack.*\.jar})).to be_empty expect(file_list(%r{WEB-INF/lib/jruby-core.*\.jar})).to be_empty end @@ -678,7 +682,7 @@ class << t; public :instance_variable_get; end config.webserver = "test" config.features << "executable" end - jar.apply(config) + apply_silently expect(file_list(%r{^WarMain\.class$})).to_not be_empty expect(file_list(%r{^JarMain\.class$})).to_not be_empty end @@ -690,7 +694,7 @@ class << t; public :instance_variable_get; end use_config do |config| config.features << "runnable" end - jar.apply(config) + apply_silently expect(file_list(%r{^WarMain\.class$})).to_not be_empty expect(file_list(%r{^JarMain\.class$})).to_not be_empty end @@ -894,7 +898,7 @@ def mock_rails_module end it 'automatically adds webpack manifest files into WEB-INF/public/packs' do - jar.apply(config) + apply_silently expect(file_list(%r{^WEB-INF/public/packs/manifest\.json})).to_not be_empty expect(file_list(%r{^WEB-INF/public/packs/manifest\.json.gz})).to_not be_empty end @@ -918,7 +922,7 @@ def mock_rails_module end it "auto-detects a Rack application with a config.ru file" do - jar.apply(config) + apply_silently expect(jar.files['WEB-INF/config.ru']).to eq 'config.ru' end @@ -935,7 +939,7 @@ def mock_rails_module use_config do |config| config.dirs = %w(lib notexist) end - silence { jar.apply(config) } + apply_silently expect(file_list(%r{WEB-INF/lib})).to_not be_empty expect(file_list(%r{WEB-INF/notexist})).to be_empty end @@ -946,7 +950,7 @@ def mock_rails_module use_config do |config| config.dirs += ["tmp"] end - jar.apply(config) + apply_silently expect(file_list(%r{WEB-INF/tmp/war})).to be_empty expect(file_list(%r{WEB-INF/tmp/war/index\.html})).to be_empty end @@ -969,7 +973,7 @@ def mock_rails_module use_config do |config| config.webinf_files = FileList['myserver-web.xml'] end - jar.apply(config) + apply_silently expect(file_list(%r{WEB-INF/myserver-web.xml})).to_not be_empty end @@ -981,7 +985,7 @@ def mock_rails_module use_config do |config| config.webinf_files = FileList['myserver-web.xml.erb'] end - jar.apply(config) + apply_silently expect(file_list(%r{WEB-INF/myserver-web.xml})).to_not be_empty expect(jar.contents('WEB-INF/myserver-web.xml')).to match /web-app.*production/ end @@ -993,7 +997,7 @@ def mock_rails_module use_config do |config| config.webinf_files = FileList['webserver.properties'] end - jar.apply(config) + apply_silently expect(file_list(%r{WEB-INF/webserver\.properties})).to_not be_empty expect(jar.contents('WEB-INF/webserver.properties')).to eq 'foo' end @@ -1002,12 +1006,12 @@ def mock_rails_module use_config do |config| config.gem_excludes += [/^(test|spec)\//] end - jar.apply(config) + apply_silently expect(file_list(%r{WEB-INF/gems/gems/rake([^/]+)/test/test_rake.rb})).to be_empty end it "creates a META-INF/init.rb file with startup config" do - jar.apply(config) + apply_silently expect(file_list(%r{META-INF/init.rb})).to_not be_empty end @@ -1032,7 +1036,7 @@ def mock_rails_module use_config do |config| config.webxml.dummy = '' end - jar.apply(config) + apply_silently expect(jar.contents('META-INF/init.rb')).to match // end end diff --git a/spec/warbler/jbundler_spec.rb b/spec/warbler/jbundler_spec.rb index b4f15674..e8ce5745 100644 --- a/spec/warbler/jbundler_spec.rb +++ b/spec/warbler/jbundler_spec.rb @@ -23,6 +23,10 @@ def use_config(&block) let(:config) { drbclient.config(@extra_config) } let(:jar) { drbclient.jar } + def apply_silently + silence { jar.apply(config) } + end + context "in a war project" do run_in_directory "spec/sample_war" cleanup_temp_files @@ -40,14 +44,14 @@ def use_config(&block) use_config do |config| config.java_libs << "local.jar" end - jar.apply(config) + apply_silently expect(file_list(%r{WEB-INF/libs/local.jar})).to be_empty end it "copies jars from jbundler classpath into the war" do File.open(".jbundler/classpath.rb", "w") {|f| f << "JBUNDLER_CLASSPATH = ['some.jar']"} File.open("some.jar", "w") {|f| f << ""} - jar.apply(config) + apply_silently expect(file_list(%r{WEB-INF/lib/some.jar})).to_not be_empty end @@ -69,14 +73,14 @@ def use_config(&block) it "does not include the jbundler gem (as it is in the development group)" do pending( "needs JRuby to work" ) unless defined? JRUBY_VERSION - jar.apply(config) + apply_silently expect(config.gems.detect{|k,v| k.name == 'jbundler'}).to be nil expect(file_list(/jbundler-/)).to be_empty end it "does not include the jbundler runtime config" do pending( "needs JRuby to work" ) unless defined? JRUBY_VERSION - jar.apply(config) + apply_silently expect(file_list(%r{WEB-INF/.jbundler})).to be_empty end end diff --git a/spec/warbler/traits_spec.rb b/spec/warbler/traits_spec.rb index 0e3ff8e3..b08495f9 100644 --- a/spec/warbler/traits_spec.rb +++ b/spec/warbler/traits_spec.rb @@ -8,13 +8,75 @@ require File.expand_path('../../spec_helper', __FILE__) describe Warbler::Traits do - it "are ordered by fewer dependencies first" do - traits = Warbler::TraitsDependencyArray.new( Warbler::Traits.constants.map {|t| Warbler::Traits.const_get(t)}) - result = traits.shuffle!.tsort + describe "TraitsDependencyArray" do + it "all traits are ordered by fewer dependencies first" do + traits = Warbler::TraitsDependencyArray.new(Warbler::Traits.constants.map {|t| Warbler::Traits.const_get(t)}) + result = traits.shuffle!.tsort - result.each do |trait| - trait.requirements.each do |requirement| - expect(result.index(requirement)).to be <= result.index(trait) + result.each do |trait| + trait.requirements.each do |requirement| + expect(result.index(requirement)).to be <= result.index(trait) + end + end + end + + it "traits subsets are ordered by fewer dependencies first" do + traits = Warbler::TraitsDependencyArray.new([Warbler::Traits::Bundler, Warbler::Traits::Jar]) + expect(traits.tsort).to eq([Warbler::Traits::Jar, Warbler::Traits::Bundler]) + end + end + + describe "#auto_detect_traits" do + context "in a Rack project with Bundler" do + run_in_directory 'spec/sample_bundler' + + it "auto-detects traits" do + config = Warbler::Config.new + expect(config.traits).to eq([Warbler::Traits::War, Warbler::Traits::Rack, Warbler::Traits::Bundler]) + end + end + end + + describe "forced_traits" do + context "with no forced traits" do + run_in_directory 'spec/sample_jar' + + it "behaves identically to auto-detection" do + default_config = Warbler::Config.new + forced_config = Warbler::Config.new(forced_traits: []) + expect(forced_config.traits).to eq(default_config.traits) + end + + it "fails if forced to conflicting gemspec/nogemspec traits" do + expect { + Warbler::Config.new(forced_traits: [Warbler::Traits::Gemspec, Warbler::Traits::NoGemspec]) + }.to raise_error(RuntimeError, "Some forced traits declare conflicts with each other: [Warbler::Traits::Gemspec, Warbler::Traits::NoGemspec]") + end + end + + context "in a Rack project with Bundler with Jar forced" do + run_in_directory 'spec/sample_bundler' + + it "only has the Jar trait" do + config = Warbler::Config.new(forced_traits: [Warbler::Traits::Jar]) + expect(config.traits).to eq([Warbler::Traits::Jar]) + end + end + + context "in a bundled Rails project" do + run_in_directory 'spec/sample_war' + + it "are ordered by fewer dependencies first" do + config = Warbler::Config.new(forced_traits: [Warbler::Traits::Rails, Warbler::Traits::War]) do |config| + config.webxml.rails.env = 'development' + end + expect(config.traits).to eq([Warbler::Traits::War, Warbler::Traits::Rails]) + end + + it "fails if forced to conflicting rails/rack traits" do + expect { + Warbler::Config.new(forced_traits: [Warbler::Traits::Rack, Warbler::Traits::Rails]) + }.to raise_error(RuntimeError, "Some forced traits declare conflicts with each other: [Warbler::Traits::Rack, Warbler::Traits::Rails]") end end end diff --git a/warble.rb b/warble.rb index 573841d3..5fcc3f4d 100644 --- a/warble.rb +++ b/warble.rb @@ -1,8 +1,25 @@ -# Disable Rake-environment-task framework detection by uncommenting/setting to false +# Warbler is designed to auto-detect "traits" of your application based on the presence of various files. +# In some cases this is ambiguous, or there is a desire to disable certain traits from being detected +# and a list of traits can be supplied to override this. +# +# This is an advanced option, and must be a complete list of traits. Using this capability disables +# some of Warbler's validation and conflict detection that causes traits to be automatically enabled and +# disabled as required. +# - `Warbler::Traits::Jar` - package as a normal executable java archive (jar) application +# - `Warbler::Traits::War` - package as an executable web archive (war) application +# - `Warbler::Traits::Bundler` - package with the gems implied by a bundle Gemfile/Gemfile.lock +# - `Warbler::Traits::Gemspec` - package with the gems implied by a gemspec +# - `Warbler::Traits::NoGemspec` - package only with the gems implied by that in a local `lib` folder +# - `Warbler::Traits::JBundler` - (experimental) package with the jars implied by a `Jarfile` +# - `Warbler::Traits::Rails` - package with the necessary startup logic to boot a Rails application +# - `Warbler::Traits::Rack` - package with the necessary startup logic to boot a standalone Rack (non-Rails) application +# forced_traits = [Warbler::Traits::Jar] + +# Disable Rake-environment-task framework detection for Rails applications by uncommenting/setting to false # Warbler.framework_detection = false -# Warbler web application assembly configuration file -Warbler::Config.new do |config| +# Configure a new Warbler application assembly +Warbler::Config.new(forced_traits: defined?(forced_traits) ? forced_traits : nil) do |config| # Features: additional options controlling how the jar is built. # Currently the following features are supported: # - *gemjar*: package the gem repository in a jar file in WEB-INF/lib