rail 0.0.7 → 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +4 -0
  3. data/Guardfile +10 -0
  4. data/README.md +10 -12
  5. data/Rakefile +4 -7
  6. data/bin/rail +1 -1
  7. data/lib/rail.rb +7 -21
  8. data/lib/rail/application.rb +10 -82
  9. data/lib/rail/browser.rb +6 -10
  10. data/lib/rail/generator.rb +13 -20
  11. data/lib/rail/pipeline.rb +13 -13
  12. data/lib/rail/precompiler.rb +58 -0
  13. data/lib/rail/processor/base.rb +1 -1
  14. data/lib/rail/processor/coffee_script.rb +1 -1
  15. data/lib/rail/processor/haml.rb +2 -2
  16. data/lib/rail/request.rb +12 -2
  17. data/lib/rail/tasks/assets.rake +1 -1
  18. data/lib/rail/version.rb +1 -1
  19. data/lib/support/generator.rb +76 -0
  20. data/lib/support/inflector.rb +16 -0
  21. data/lib/support/loader.rb +55 -0
  22. data/lib/support/query_string.rb +8 -0
  23. data/lib/support/query_struct.rb +10 -0
  24. data/rail.gemspec +5 -0
  25. data/spec/{coffee_spec.rb → features/coffee_spec.rb} +5 -7
  26. data/spec/{haml_spec.rb → features/haml_spec.rb} +6 -8
  27. data/spec/{sass_spec.rb → features/sass_spec.rb} +5 -7
  28. data/spec/{project → fixtures/project}/app/assets/javascripts/application.js.coffee +0 -0
  29. data/spec/{project → fixtures/project}/app/assets/javascripts/font.coffee +0 -0
  30. data/spec/{project → fixtures/project}/app/assets/javascripts/parser.js.coffee +0 -0
  31. data/spec/{project → fixtures/project}/app/assets/stylesheets/_reset.scss +0 -0
  32. data/spec/{project → fixtures/project}/app/assets/stylesheets/application.css.scss +0 -0
  33. data/spec/{project → fixtures/project}/app/helpers/application_helper.rb +0 -0
  34. data/spec/{project → fixtures/project}/app/views/articles/about.html.haml +0 -0
  35. data/spec/{project → fixtures/project}/app/views/layouts/application.html.haml +0 -0
  36. data/spec/{project → fixtures/project}/app/views/layouts/articles.html.haml +0 -0
  37. data/spec/{project → fixtures/project}/config/application.rb +0 -0
  38. data/spec/{project → fixtures/project}/controller.rb +0 -0
  39. data/spec/lib/rail/application_spec.rb +38 -0
  40. data/spec/lib/rail/generator_spec.rb +14 -0
  41. data/spec/lib/rail/precompiler_spec.rb +25 -0
  42. data/spec/lib/rail/request_spec.rb +16 -0
  43. data/spec/lib/support/inflector_spec.rb +18 -0
  44. data/spec/lib/support/loader_spec.rb +61 -0
  45. data/spec/lib/support/query_string_spec.rb +11 -0
  46. data/spec/lib/support/query_struct_spec.rb +28 -0
  47. data/spec/spec_helper.rb +6 -0
  48. data/spec/support/common_helper.rb +5 -0
  49. metadata +99 -32
  50. data/lib/generator.rb +0 -73
  51. data/lib/rail/support.rb +0 -7
@@ -8,7 +8,7 @@ module Rail
8
8
  end
9
9
 
10
10
  def self.extensify(filename)
11
- "#{ filename }.#{ input_extension }"
11
+ "#{filename}.#{input_extension}"
12
12
  end
13
13
 
14
14
  attr_reader :pipeline
@@ -38,7 +38,7 @@ module Rail
38
38
  end
39
39
 
40
40
  def find_requirement(name, referrer)
41
- assets = [ name, "#{ name }.coffee", "#{ name }.js.coffee" ]
41
+ assets = [name, "#{name}.coffee", "#{name}.js.coffee"]
42
42
 
43
43
  if name =~ /^\.\// # relative?
44
44
  path = File.dirname(referrer)
@@ -36,9 +36,9 @@ module Rail
36
36
  private
37
37
 
38
38
  def find_layout(filename)
39
- asset = "layouts/#{ filename.split('/')[-2] }"
39
+ asset = "layouts/#{filename.split('/')[-2]}"
40
40
 
41
- [ "#{ asset }.haml", "#{ asset }.html.haml" ].each do |asset|
41
+ ["#{asset}.haml", "#{asset}.html.haml"].each do |asset|
42
42
  filename = pipeline.find(asset)
43
43
  return filename if filename
44
44
  end
data/lib/rail/request.rb CHANGED
@@ -3,15 +3,25 @@ module Rail
3
3
  attr_reader :env
4
4
 
5
5
  def initialize(env)
6
- @env = env
6
+ @env = rewrite(env)
7
7
  end
8
8
 
9
9
  def path
10
- @path ||= env['PATH_INFO'].sub(/^\//, '').sub(/\?.*$/, '')
10
+ env['PATH_INFO']
11
11
  end
12
12
 
13
13
  def host
14
14
  env['HTTP_HOST']
15
15
  end
16
+
17
+ private
18
+
19
+ def rewrite(env)
20
+ path = env['PATH_INFO']
21
+ path = '/index.html' if [nil, '', '/'].include?(path)
22
+ path = "#{path}.html" if File.extname(path).empty?
23
+ env['PATH_INFO'] = path
24
+ env
25
+ end
16
26
  end
17
27
  end
@@ -1,4 +1,4 @@
1
1
  desc 'Precompile assets'
2
2
  task :assets do
3
- Rail.applications.first.precompile
3
+ Rail.applications.map(&:new).each(&:precompile)
4
4
  end
data/lib/rail/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Rail
2
- VERSION = '0.0.7'
2
+ VERSION = '0.0.8'
3
3
  end
@@ -0,0 +1,76 @@
1
+ require 'erb'
2
+ require 'ostruct'
3
+ require 'fileutils'
4
+
5
+ module Support
6
+ class Generator
7
+ attr_reader :destination, :source
8
+
9
+ def initialize(options)
10
+ @destination = options.fetch(:destination)
11
+ @source = options.fetch(:source)
12
+ end
13
+
14
+ def process(files, locals = {})
15
+ context = OpenStruct.new(locals)
16
+ files.each { |file| process_one(file, context) }
17
+ end
18
+
19
+ private
20
+
21
+ def process_one(file, context)
22
+ output_file = find(file)
23
+ input_file = route(file)
24
+
25
+ directory = File.dirname(input_file)
26
+ unless File.directory?(directory)
27
+ report(directory)
28
+ make(directory)
29
+ end
30
+
31
+ report(input_file)
32
+ if template?(file)
33
+ dump(transform(load(output_file), context), input_file)
34
+ else
35
+ copy(output_file, input_file)
36
+ end
37
+ end
38
+
39
+ def template?(file)
40
+ File.extname(file) == '.erb'
41
+ end
42
+
43
+ def find(file)
44
+ File.join(source, file)
45
+ end
46
+
47
+ def route(file)
48
+ File.join(destination, file).gsub(/\.erb$/, '')
49
+ end
50
+
51
+ def report(message)
52
+ puts "Creating '#{ message }'..."
53
+ end
54
+
55
+ def make(directory)
56
+ FileUtils.mkdir_p(directory)
57
+ end
58
+
59
+ def copy(output_file, input_file)
60
+ FileUtils.cp(output_file, input_file)
61
+ end
62
+
63
+ def load(file)
64
+ File.read(file)
65
+ end
66
+
67
+ def dump(content, file)
68
+ File.open(file, 'w') { |file| file.write(content) }
69
+ end
70
+
71
+ def transform(content, context)
72
+ context.singleton_class.class_eval('def get_binding; binding; end')
73
+ ERB.new(content).result(context.get_binding)
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,16 @@
1
+ module Support
2
+ module Inflector
3
+ def self.titelize(name)
4
+ name.gsub(/[^\w ]/, '')
5
+ .gsub(/^[^a-zA-Z]+/, '')
6
+ .gsub(/_/, ' ')
7
+ .gsub(/ {2,}/, ' ')
8
+ .gsub(/ +$/, '')
9
+ .gsub(/^\w| \w/, &:upcase)
10
+ end
11
+
12
+ def self.modulize(name)
13
+ name.split(/[\s_]+/).map(&:capitalize).join.to_sym
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,55 @@
1
+ require 'support/inflector'
2
+
3
+ module Support
4
+ class Loader
5
+ def initialize(pattern)
6
+ @pattern = pattern
7
+ @files = []
8
+ @names = []
9
+ end
10
+
11
+ def reload?
12
+ !last_scanned || !last_loaded || last_loaded < last_modified
13
+ end
14
+
15
+ def find
16
+ scan!
17
+
18
+ if reload?
19
+ unload!
20
+ load!
21
+ end
22
+
23
+ modules
24
+ end
25
+
26
+ private
27
+
28
+ attr_reader :pattern, :files, :names, :modules
29
+ attr_reader :last_scanned, :last_loaded
30
+
31
+ def scan!
32
+ @last_scanned = Time.now
33
+ @files = Dir[pattern]
34
+ end
35
+
36
+ def last_modified
37
+ files.map { |file| File.mtime(file) }.max || Time.at(0)
38
+ end
39
+
40
+ def unload!
41
+ names.each { |name| Object.send(:remove_const, name) }
42
+ names.clear
43
+ end
44
+
45
+ def load!
46
+ @names = files.map do |file|
47
+ load file
48
+ Support::Inflector.modulize(File.basename(file, '.rb'))
49
+ end
50
+
51
+ @modules = names.map { |name| Object.const_get(name) }
52
+ @last_loaded = last_scanned
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,8 @@
1
+ module Support
2
+ class QueryString < String
3
+ def method_missing(name, *arguments, &block)
4
+ return super unless name.to_s =~ /^(?<name>.+)\?$/
5
+ self == Regexp.last_match(:name)
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,10 @@
1
+ require 'ostruct'
2
+
3
+ module Support
4
+ class QueryStruct < OpenStruct
5
+ def method_missing(name, *arguments, &block)
6
+ return super unless name.to_s =~ /^(?<name>.+)\?$/
7
+ !!super(Regexp.last_match(:name).to_sym, *arguments, &block)
8
+ end
9
+ end
10
+ end
data/rail.gemspec CHANGED
@@ -33,4 +33,9 @@ Gem::Specification.new do |spec|
33
33
  spec.add_dependency 'uglifier', '~> 2.5'
34
34
 
35
35
  spec.add_development_dependency 'bundler', '~> 1.6'
36
+
37
+ spec.add_development_dependency 'rspec', '~> 3.0'
38
+
39
+ spec.add_development_dependency 'guard', '~> 2.6'
40
+ spec.add_development_dependency 'guard-rspec', '~> 4.3'
36
41
  end
@@ -1,15 +1,13 @@
1
- require 'minitest/spec'
2
- require 'minitest/autorun'
1
+ require 'spec_helper'
2
+ require 'fixtures/project/controller'
3
3
 
4
- require_relative 'project/controller'
5
-
6
- describe Rail::Application do
4
+ RSpec.describe Rail::Application do
7
5
  it 'handles uncompressed CoffeeScript assets' do
8
6
  controller = Controller.new do
9
7
  config.compress = false
10
8
  end
11
9
  body = controller.process('/application.js')
12
- assert_equal body.strip, <<-BODY.strip
10
+ expect(body.strip).to eq <<-BODY.strip
13
11
  (function() {
14
12
  window.Parser = (function() {
15
13
  function Parser(format) {
@@ -46,7 +44,7 @@ describe Rail::Application do
46
44
  config.compress = true
47
45
  end
48
46
  body = controller.process('/application.js')
49
- assert_equal body.strip, <<-BODY.strip
47
+ expect(body.strip).to eq <<-BODY.strip
50
48
  (function(){window.Parser=function(){function n(n){this.format=n}return n}()}).call(this),function(){window.Font=function(){function n(n){this.name=n}return n}()}.call(this),function(){var n;n=new Font(\"Benton Modern Display\")}.call(this);
51
49
  BODY
52
50
  end
@@ -1,15 +1,13 @@
1
- require 'minitest/spec'
2
- require 'minitest/autorun'
1
+ require 'spec_helper'
2
+ require 'fixtures/project/controller'
3
3
 
4
- require_relative 'project/controller'
5
-
6
- describe Rail::Application do
4
+ RSpec.describe Rail::Application do
7
5
  it 'handles uncompressed Haml assets' do
8
6
  controller = Controller.new do
9
7
  config.compress = false
10
8
  end
11
9
  body = controller.process('/')
12
- assert_equal body.strip, <<-BODY.strip
10
+ expect(body.strip).to eq <<-BODY.strip
13
11
  <!DOCTYPE html>
14
12
  <html>
15
13
  <head>
@@ -27,7 +25,7 @@ describe Rail::Application do
27
25
  config.compress = true
28
26
  end
29
27
  body = controller.process('/')
30
- assert_equal body.strip, <<-BODY.strip
28
+ expect(body.strip).to eq <<-BODY.strip
31
29
  <!DOCTYPE html>
32
30
  <html>
33
31
  <head>
@@ -45,7 +43,7 @@ describe Rail::Application do
45
43
  config.compress = false
46
44
  end
47
45
  body = controller.process('/articles/about')
48
- assert_equal body.strip, <<-BODY.strip
46
+ expect(body.strip).to eq <<-BODY.strip
49
47
  <!DOCTYPE html>
50
48
  <html>
51
49
  <head>
@@ -1,15 +1,13 @@
1
- require 'minitest/spec'
2
- require 'minitest/autorun'
1
+ require 'spec_helper'
2
+ require 'fixtures/project/controller'
3
3
 
4
- require_relative 'project/controller'
5
-
6
- describe Rail::Application do
4
+ RSpec.describe Rail::Application do
7
5
  it 'handles uncompressed Sass assests' do
8
6
  controller = Controller.new do
9
7
  config.compress = false
10
8
  end
11
9
  body = controller.process('/application.css')
12
- assert_equal body.strip, <<-BODY.strip
10
+ expect(body.strip).to eq <<-BODY.strip
13
11
  * {
14
12
  margin: 0;
15
13
  padding: 0; }
@@ -24,7 +22,7 @@ body {
24
22
  config.compress = true
25
23
  end
26
24
  body = controller.process('/application.css')
27
- assert_equal body.strip, <<-BODY.strip
25
+ expect(body.strip).to eq <<-BODY.strip
28
26
  *{margin:0;padding:0}body{font-family:'Benton Modern Display'}
29
27
  BODY
30
28
  end
File without changes
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+ require 'fixtures/project/config/application'
3
+
4
+ RSpec.describe Rail::Application do
5
+ subject { Project::Application.new }
6
+
7
+ describe '#call' do
8
+ context 'when index.html exists' do
9
+ before do
10
+ allow(File).to receive(:exist?).and_return(true)
11
+ end
12
+
13
+ ['/', '/index.html'].each do |path|
14
+ it "lets the browser serve #{path}" do
15
+ expect(subject.browser).to receive(:process)
16
+ expect(subject.pipeline).not_to receive(:process)
17
+
18
+ subject.call('PATH_INFO' => path)
19
+ end
20
+ end
21
+ end
22
+
23
+ context 'when index.html does not exist' do
24
+ before do
25
+ allow(File).to receive(:exist?).and_return(false)
26
+ end
27
+
28
+ ['/', '/index.html'].each do |path|
29
+ it "lets the pipeline serve #{path}" do
30
+ expect(subject.browser).not_to receive(:process)
31
+ expect(subject.pipeline).to receive(:process)
32
+
33
+ subject.call('PATH_INFO' => path)
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,14 @@
1
+ require 'spec_helper'
2
+ require 'rail/generator'
3
+
4
+ RSpec.describe Rail::Generator do
5
+ let(:destination) { 'project' }
6
+ subject { described_class.new(destination: destination) }
7
+
8
+ describe '#run' do
9
+ it 'raises an exception if the destination folder already exists' do
10
+ expect(File).to receive(:directory?).and_return(true)
11
+ expect { subject.run }.to raise_error(ArgumentError)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+ require 'rail/request'
3
+ require 'rail/precompiler'
4
+
5
+ RSpec.describe Rail::Precompiler do
6
+ let(:pipeline) { double }
7
+ let(:storage) { double }
8
+
9
+ subject do
10
+ precompiler = described_class.new(pipeline, storage)
11
+ allow(precompiler).to receive(:report)
12
+ precompiler
13
+ end
14
+
15
+ describe '#process' do
16
+ it 'works' do
17
+ expect(pipeline).to \
18
+ receive(:process).and_return([200, {}, ['Hello, world!']])
19
+ expect(storage).to \
20
+ receive(:write).with('index.html', ['Hello, world!'])
21
+
22
+ subject.process(['index.html'])
23
+ end
24
+ end
25
+ end