rail 0.0.7 → 0.0.8

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 (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