From 5832151ac545404ab720d06e5767012f583e7726 Mon Sep 17 00:00:00 2001 From: John Bintz Date: Fri, 16 Mar 2012 11:27:58 -0400 Subject: [PATCH] remove the need for the separate delivey gem, there was so little left in it anyway that was necessary --- flowerbox.gemspec | 6 +- lib/flowerbox.rb | 5 +- lib/flowerbox/run/base.rb | 2 +- lib/flowerbox/runner/base.rb | 2 +- lib/flowerbox/server.rb | 123 ++++++++++++++++++ lib/flowerbox/sprockets_handler.rb | 57 ++++++++ lib/flowerbox/unique_asset_list.rb | 17 +++ .../delivery/template_renderer_spec.rb | 59 +++++++++ .../delivery/tilt/js_template_spec.rb | 62 +++++++++ spec/flowerbox/server_spec.rb | 100 ++++++++++++++ spec/flowerbox/sprockets_handler_spec.rb | 52 ++++++++ spec/flowerbox/unique_asset_list_spec.rb | 18 +++ spec/spec_helper.rb | 7 + 13 files changed, 504 insertions(+), 6 deletions(-) create mode 100644 lib/flowerbox/server.rb create mode 100644 lib/flowerbox/sprockets_handler.rb create mode 100644 lib/flowerbox/unique_asset_list.rb create mode 100644 spec/flowerbox/delivery/template_renderer_spec.rb create mode 100644 spec/flowerbox/delivery/tilt/js_template_spec.rb create mode 100644 spec/flowerbox/server_spec.rb create mode 100644 spec/flowerbox/sprockets_handler_spec.rb create mode 100644 spec/flowerbox/unique_asset_list_spec.rb create mode 100644 spec/spec_helper.rb diff --git a/flowerbox.gemspec b/flowerbox.gemspec index 81675f9..85fc657 100644 --- a/flowerbox.gemspec +++ b/flowerbox.gemspec @@ -4,8 +4,8 @@ require File.expand_path('../lib/flowerbox/version', __FILE__) Gem::Specification.new do |gem| gem.authors = ["John Bintz"] gem.email = ["john@coswellproductions.com"] - gem.description = %q{TODO: Write a gem description} - gem.summary = %q{TODO: Write a gem summary} + gem.description = %q{No-nonsense JavaScript testing solution.} + gem.summary = %q{No-nonsense JavaScript testing solution.} gem.homepage = "" gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } @@ -19,9 +19,9 @@ Gem::Specification.new do |gem| gem.add_development_dependency 'rspec' gem.add_dependency 'jasmine-core' - gem.add_dependency 'flowerbox-delivery' gem.add_dependency 'thor' gem.add_dependency 'selenium-webdriver' gem.add_dependency 'sinatra' gem.add_dependency 'rainbow' + gem.add_dependency 'sprockets-vendor_gems' end diff --git a/lib/flowerbox.rb b/lib/flowerbox.rb index fdc870a..f94f5b8 100644 --- a/lib/flowerbox.rb +++ b/lib/flowerbox.rb @@ -1,5 +1,4 @@ require "flowerbox/version" -require 'flowerbox-delivery' require 'rainbow' module Flowerbox @@ -40,6 +39,10 @@ module Flowerbox autoload :Reporter, 'flowerbox/reporter' + autoload :Server, 'flowerbox/server' + autoload :UniqueAssetList, 'flowerbox/unique_asset_list' + autoload :SprocketsHandler, 'flowerbox/sprockets_handler' + class << self attr_writer :reporters attr_accessor :port diff --git a/lib/flowerbox/run/base.rb b/lib/flowerbox/run/base.rb index 189fde5..3f1fea1 100644 --- a/lib/flowerbox/run/base.rb +++ b/lib/flowerbox/run/base.rb @@ -33,7 +33,7 @@ module Flowerbox::Run end def sprockets - Flowerbox::Delivery::SprocketsHandler.new( + Flowerbox::SprocketsHandler.new( :asset_paths => [ Flowerbox.path.join("lib/assets/javascripts"), Flowerbox.path.join("vendor/assets/javascripts"), diff --git a/lib/flowerbox/runner/base.rb b/lib/flowerbox/runner/base.rb index 43365c5..aeb8848 100644 --- a/lib/flowerbox/runner/base.rb +++ b/lib/flowerbox/runner/base.rb @@ -106,7 +106,7 @@ module Flowerbox server_options[:logging] = true if options[:verbose_server] server_options[:port] = Flowerbox.port - @server = Flowerbox::Delivery::Server.new(server_options) + @server = Flowerbox::Server.new(server_options) Flowerbox::Rack.runner = self Flowerbox::Rack.sprockets = @sprockets diff --git a/lib/flowerbox/server.rb b/lib/flowerbox/server.rb new file mode 100644 index 0000000..2e64e27 --- /dev/null +++ b/lib/flowerbox/server.rb @@ -0,0 +1,123 @@ +require 'rack' +require 'net/http' +require 'socket' +require 'rack/builder' +require 'thin' + +module Flowerbox + class Server + attr_reader :options + + def initialize(options = {}) + @options = { :logging => false }.merge(options || {}) + end + + def start + @server_thread = Thread.new do + server_options = { :Port => port, :Host => interface } + + app = options[:app] + + Thin::Logging.silent = !options[:logging] + + if options[:logging] + real_app = app + app = ::Rack::Builder.new do + use ::Rack::CommonLogger, STDOUT + run real_app + end + end + + ::Rack::Handler::Thin.run(app, server_options) do |server| + Thread.current[:server] = server + + trap('QUIT') { server.stop } + end + end + + while !@server_thread[:server] && @server_thread.alive? + sleep 0.1 + end + + raise StandardError.new("Server died") if !@server_thread[:server].running? + end + + def stop + if @server_thread + @server_thread[:server].stop + + wait_for_server_to_stop + end + end + + def interface + options[:interface] || '0.0.0.0' + end + + def port + return @port if @port ||= options[:port] + + attempts = 20 + + begin + attempts -= 1 + + current_port = random_port + + begin + socket = TCPSocket.new(interface, current_port) + socket.close + rescue Errno::ECONNREFUSED => e + @port = current_port + end + end while !@port and attempts > 0 + + raise StandardError.new("can't start server") if attempts == 0 + + @port + end + + def address + "http://#{interface}:#{port}/" + end + + def alive? + @server_thread.alive? + end + + private + def wait_for_server_to_start + while true do + begin + connect_interface = '127.0.0.1' if interface == '0.0.0.0' + + TCPSocket.new(connect_interface, port) + break + rescue Errno::ECONNREFUSED => e + end + + sleep 0.1 + end + end + + def wait_for_server_to_stop + while alive? do + begin + connect_interface = '127.0.0.1' if interface == '0.0.0.0' + + socket = TCPSocket.new(connect_interface, port) + socket.close + rescue Errno::ECONNREFUSED => e + return + end + + sleep 0.1 + end + end + + def random_port + 25000 + Kernel.rand(1000) + end + end +end + diff --git a/lib/flowerbox/sprockets_handler.rb b/lib/flowerbox/sprockets_handler.rb new file mode 100644 index 0000000..0666f40 --- /dev/null +++ b/lib/flowerbox/sprockets_handler.rb @@ -0,0 +1,57 @@ +require 'sprockets' +require 'sprockets/engines' +require 'forwardable' +require 'sprockets-vendor_gems' + +module Flowerbox + class SprocketsHandler + extend Forwardable + + attr_reader :files, :options + + def_delegators :environment, :append_path, :register_engine, :[] + + def self.gem_asset_paths + @gem_asset_paths ||= Sprockets.find_gem_vendor_paths + end + + def initialize(options) + @options = options + + @files = Flowerbox::UniqueAssetList.new + end + + def add(asset) + paths_for(asset).each { |path| add_paths_for_compiled_asset(path) } + end + + def paths_for(asset) + environment.find_asset(asset).to_a.collect(&:pathname) + end + + def expire_index! + @environment.send(:expire_index!) + end + + def environment + return @environment if @environment + + @environment = Sprockets::Environment.new + @environment.cache = Sprockets::Cache::FileStore.new(".tmp") + + self.class.gem_asset_paths.each { |path| append_path(path) } + options[:asset_paths].each { |path| append_path(path) } + + @environment + end + + def asset_for(*args) + environment.find_asset(*args) + end + + def add_paths_for_compiled_asset(path) + asset_for(path, :bundle => false).to_a.each { |file_path| @files.add(file_path) } + end + end +end + diff --git a/lib/flowerbox/unique_asset_list.rb b/lib/flowerbox/unique_asset_list.rb new file mode 100644 index 0000000..8011b9d --- /dev/null +++ b/lib/flowerbox/unique_asset_list.rb @@ -0,0 +1,17 @@ +module Flowerbox + class UniqueAssetList < ::Array + def add(files) + [ files ].flatten.each { |file| self << file if !include?(file) } + end + + def to_json + collect(&:logical_path) + end + + private + def include?(file) + any? { |other_file| other_file.pathname.to_s == file.pathname.to_s } + end + end +end + diff --git a/spec/flowerbox/delivery/template_renderer_spec.rb b/spec/flowerbox/delivery/template_renderer_spec.rb new file mode 100644 index 0000000..7c790d0 --- /dev/null +++ b/spec/flowerbox/delivery/template_renderer_spec.rb @@ -0,0 +1,59 @@ +require 'spec_helper' + +describe Flowerbox::Delivery::TemplateRenderer do + let(:template_renderer) { described_class.new(:template => template, :files => files) } + let(:template) { 'template' } + let(:files) { 'files' } + + let(:rendered_template) { 'rendered template' } + let(:erb_template) { "#{rendered_template} <%= resource_tags %>" } + + describe '#render' do + subject { template_renderer.render } + + let(:rendered_files) { 'with files' } + let(:result) { "#{rendered_template} #{rendered_files}" } + + before do + template_renderer.expects(:resource_tags).returns(rendered_files) + template_renderer.expects(:template).returns(erb_template) + end + + it { should == result } + end + + describe '#template' do + include FakeFS::SpecHelpers + + before do + File.open(template, 'wb') { |fh| fh.print erb_template } + end + + subject { template_renderer.template } + + it { should == erb_template } + end + + describe '#resource_tags' do + subject { template_renderer.resource_tags } + + context 'success' do + let(:files) { [ js, css ] } + let(:js) { 'file.js' } + let(:css) { 'file.css' } + + it { should == [ + %{}, + %{} + ].join } + end + + context 'failure' do + let(:files) { [ 'what.ever' ] } + + it 'should raise error' do + expect { subject }.to raise_error(Flowerbox::Delivery::TemplateRenderer::FileTypeError) + end + end + end +end diff --git a/spec/flowerbox/delivery/tilt/js_template_spec.rb b/spec/flowerbox/delivery/tilt/js_template_spec.rb new file mode 100644 index 0000000..41a680b --- /dev/null +++ b/spec/flowerbox/delivery/tilt/js_template_spec.rb @@ -0,0 +1,62 @@ +require 'spec_helper' + +describe Flowerbox::Delivery::Tilt::JSTemplate do + let(:js_template) { described_class.new { '' } } + + describe '#evaluate' do + subject { js_template.evaluate(Object.new, {}) } + + before do + js_template.stubs(:file).returns(file) + end + + context '.js' do + let(:file) { 'file.js' } + + it { should == file } + end + + context 'other extension' do + let(:file) { 'file.coffee' } + let(:temp_file) { 'temp file' } + + before do + js_template.expects(:save).returns(temp_file) + end + + it { should == temp_file } + end + end + + describe '#save' do + include FakeFS::SpecHelpers + + let(:temp_file) { 'dir/temp file' } + let(:data) { 'data' } + + before do + js_template.stubs(:temp_file).returns(temp_file) + js_template.stubs(:data).returns(data) + end + + it 'should save the file to disk and return the temp path' do + js_template.save.should == temp_file + + File.read(temp_file).should == data + end + end + + describe '#temp_file' do + subject { js_template.temp_file } + + let(:filename) { "#{root_filename}.ext" } + let(:root_filename) { "dir/file.js" } + + before do + js_template.stubs(:file).returns(filename) + end + + it { should == File.join(Dir.pwd, '.tmp/sprockets', root_filename) } + end +end + diff --git a/spec/flowerbox/server_spec.rb b/spec/flowerbox/server_spec.rb new file mode 100644 index 0000000..5a73362 --- /dev/null +++ b/spec/flowerbox/server_spec.rb @@ -0,0 +1,100 @@ +require 'spec_helper' +require 'socket' +require 'thread' + +describe Flowerbox::Delivery::Server do + let(:server) { described_class.new(options) } + let(:options) { nil } + + subject { server } + + describe '#initialize' do + let(:options) { { :port => port, :interface => interface } } + let(:port) { 'port' } + let(:interface) { 'interface' } + + its(:port) { should == port } + its(:interface) { should == interface } + end + + describe '#start' do + let(:port) { 12345 } + let(:interface) { '127.0.0.1' } + + before do + server.stubs(:port).returns(port) + server.stubs(:interface).returns(interface) + end + + it 'should start a Rack server' do + server.start + + TCPSocket.new(server.interface, server.port) + end + end + + describe '#interface' do + subject { server.interface } + + it { should == '0.0.0.0' } + end + + describe '#port' do + let(:interface) { '127.0.0.1' } + let(:base) { 25000 } + let(:initial) { base + @offset } + + before do + server.stubs(:interface).returns(interface) + + @offset = 0 + ok = true + + begin + [ 0, 1 ].each do |index| + begin + TCPSocket.new(interface, base + @offset + index) + @offset += 1 + ok = false + rescue Errno::ECONNREFUSED => e + end + end + end while !ok + end + + subject { server.port } + + context 'no running service' do + before do + Kernel.stubs(:rand).returns(@offset) + end + + it { should == initial } + end + + context 'running service' do + before do + @server = Thread.new do + TCPServer.new(interface, initial) + end + + server.stubs(:random_port).returns(initial, initial + 1) + + while true + begin + TCPSocket.new(interface, initial) + break + rescue Errno::ECONNREFUSED + end + end + end + + it { should == initial + 1 } + + after do + @server.kill + end + end + end +end + diff --git a/spec/flowerbox/sprockets_handler_spec.rb b/spec/flowerbox/sprockets_handler_spec.rb new file mode 100644 index 0000000..2635a7a --- /dev/null +++ b/spec/flowerbox/sprockets_handler_spec.rb @@ -0,0 +1,52 @@ +require 'spec_helper' + +describe Flowerbox::Delivery::SprocketsHandler do + let(:sprockets_handler) { described_class.new(options) } + let(:options) { { :asset_paths => asset_paths } } + let(:asset_paths) { [ File.expand_path('asset path') ] } + + describe '#add' do + let(:asset) { 'asset' } + let(:path) { 'path' } + let(:paths) { [ path ] } + + let(:pathname_path) { 'pathname path' } + + before do + sprockets_handler.expects(:paths_for).with(asset).returns(paths) + sprockets_handler.expects(:path_for_compiled_asset).with(path).returns(pathname_path) + end + + it 'should add the asset to the list of ones to work with' do + sprockets_handler.add(asset) + + sprockets_handler.files.should == [ pathname_path ] + end + end + + describe '#paths_for' do + subject { sprockets_handler.paths_for(asset) } + + let(:asset) { 'asset' } + let(:environment) { stub } + let(:bundled_asset) { stub(:to_a => [ processed_asset ]) } + let(:processed_asset) { stub(:pathname => path) } + + let(:path) { 'path' } + + before do + sprockets_handler.stubs(:environment).returns(environment) + environment.expects(:find_asset).with(asset).returns(bundled_asset) + end + + it { should == [ path ] } + end + + describe '#environment' do + subject { sprockets_handler.environment } + + it { should be_a_kind_of(Sprockets::Environment) } + its(:paths) { should == asset_paths } + end +end + diff --git a/spec/flowerbox/unique_asset_list_spec.rb b/spec/flowerbox/unique_asset_list_spec.rb new file mode 100644 index 0000000..1def2c1 --- /dev/null +++ b/spec/flowerbox/unique_asset_list_spec.rb @@ -0,0 +1,18 @@ +require 'spec_helper' + +describe Flowerbox::Delivery::UniqueAssetList do + let(:unique_asset_list) { described_class.new } + + describe "#add" do + let(:first) { Pathname.new('one') } + let(:second) { Pathname.new('one') } + let(:third) { Pathname.new('two') } + + it 'should not add assets already added' do + unique_asset_list.add(first) + unique_asset_list.add([ second, third ]) + + unique_asset_list.should == [ first, third ] + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 0000000..fe4f8b5 --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1,7 @@ +require 'flowerbox-delivery' +require 'mocha' +require 'fakefs/spec_helpers' + +RSpec.configure do |c| + c.mock_with :mocha +end