slinky 0.2.1 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.rspec +1 -0
- data/Gemfile +8 -3
- data/README.md +84 -6
- data/Rakefile +22 -10
- data/VERSION +1 -1
- data/bin/slinky +1 -1
- data/lib/slinky/builder.rb +8 -0
- data/lib/slinky/compiled_file.rb +41 -23
- data/lib/slinky/compilers/coffee-compiler.rb +2 -3
- data/lib/slinky/compilers/haml-compiler.rb +10 -4
- data/lib/slinky/compilers/sass-compiler.rb +2 -3
- data/lib/slinky/compilers.rb +72 -0
- data/lib/slinky/manifest.rb +371 -0
- data/lib/slinky/runner.rb +56 -7
- data/lib/slinky/server.rb +46 -97
- data/lib/slinky.rb +13 -7
- data/slinky.gemspec +27 -8
- data/spec/slinky_spec.rb +314 -3
- data/spec/spec_helper.rb +113 -13
- metadata +76 -17
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/Gemfile
CHANGED
@@ -4,13 +4,18 @@ gem "eventmachine", ">= 0.12.0"
|
|
4
4
|
gem "eventmachine_httpserver", ">= 0.2.0"
|
5
5
|
gem "rainbow", ">= 1.1.1"
|
6
6
|
gem "haml", ">= 3.0.0"
|
7
|
-
|
7
|
+
gem "sass", ">= 3.1.1"
|
8
8
|
gem "coffee-script", ">= 2.2.0"
|
9
|
+
gem "mime-types", ">= 1.16"
|
10
|
+
gem "yui-compressor", ">= 0.9.6"
|
9
11
|
|
10
12
|
group :development do
|
11
|
-
gem "
|
13
|
+
gem "rspec", "~> 2.3.0"
|
12
14
|
gem "yard", "~> 0.6.0"
|
13
15
|
gem "bundler", "~> 1.0.0"
|
14
16
|
gem "jeweler", "~> 1.5.2"
|
15
|
-
gem
|
17
|
+
gem 'cover_me', '>= 1.0.0.rc6'
|
18
|
+
gem "fakefs"
|
19
|
+
gem "em-http-request"
|
20
|
+
# gem "em-synchrony", ">= 0"
|
16
21
|
end
|
data/README.md
CHANGED
@@ -1,9 +1,87 @@
|
|
1
1
|
#Slinky
|
2
2
|
|
3
|
-
Slinky
|
4
|
-
|
5
|
-
|
6
|
-
|
3
|
+
Slinky helps you write rich web applications using compiled web
|
4
|
+
languages like SASS, HAML and CoffeeScript. The slinky server
|
5
|
+
transparently compiles resources as they're requested, leaving you to
|
6
|
+
worry about your code, not how to compile it.
|
7
7
|
|
8
|
-
|
9
|
-
|
8
|
+
Once your ready for production, the slinky builder will compile all of
|
9
|
+
your sources and concatenate and minify your javascript and css,
|
10
|
+
leaving you a directory that's ready to be pushed to your servers.
|
11
|
+
|
12
|
+
## Quickstart
|
13
|
+
|
14
|
+
```
|
15
|
+
$ gem install slinky
|
16
|
+
$ cd ~/my/awesome/project
|
17
|
+
$ slinky start
|
18
|
+
[hardcore web development action]
|
19
|
+
$ slinky build
|
20
|
+
$ scp -r build/ myserver.com/var/www/project
|
21
|
+
````
|
22
|
+
## But tell me more!
|
23
|
+
|
24
|
+
Slinky currently supports three languages for compilation, SASS/SCSS,
|
25
|
+
HAML and CoffeeScript, but it's simple to add support for others (and
|
26
|
+
please submit a pull request when you do!). Slinky also has a few
|
27
|
+
tricks of its own for managing the complexity of modern web
|
28
|
+
development.
|
29
|
+
|
30
|
+
### Script & style management
|
31
|
+
|
32
|
+
Slinky can manage all of your javascript and css files if you want it
|
33
|
+
to, serving them up individually during development and concatenating
|
34
|
+
and minifying them for production. To support this, Slinky recognizes
|
35
|
+
`slinky_scripts` in your HTML/Haml files. For example, when Slinky
|
36
|
+
sees this:
|
37
|
+
|
38
|
+
```haml
|
39
|
+
!!!5
|
40
|
+
%html
|
41
|
+
%head
|
42
|
+
slinky_scripts
|
43
|
+
slinky_styles
|
44
|
+
%body
|
45
|
+
%h1 Hello, world!
|
46
|
+
```
|
47
|
+
|
48
|
+
it will compile the HAML to HTML and replace slinky_styles with the
|
49
|
+
appropriate HTML.
|
50
|
+
|
51
|
+
### Specifying order
|
52
|
+
|
53
|
+
But what if your scripts or styles depend on being included in the
|
54
|
+
page in a particular order? For this, we need the `slinky_require`
|
55
|
+
directive.
|
56
|
+
|
57
|
+
For example, consider the case of two coffeescript files, A.coffee and
|
58
|
+
B.coffee. A includes a class definition that B depends upon, so we
|
59
|
+
want to make sure that A comes before B in the concatenation order. We
|
60
|
+
can solve this simply using `slinky_require(script)`
|
61
|
+
|
62
|
+
File A.coffee:
|
63
|
+
|
64
|
+
```coffeescript
|
65
|
+
class A
|
66
|
+
hello: (thing) -> "Hello, " + thing
|
67
|
+
```
|
68
|
+
|
69
|
+
File B.coffee:
|
70
|
+
|
71
|
+
```coffeescript
|
72
|
+
slinky_require("A.coffee")
|
73
|
+
alert (new A).hello("world")
|
74
|
+
```
|
75
|
+
We can also do this in CSS/SASS/SCSS:
|
76
|
+
|
77
|
+
```sass
|
78
|
+
/* slinky_require("reset.css")
|
79
|
+
a
|
80
|
+
color: red
|
81
|
+
```
|
82
|
+
|
83
|
+
### And coming up next...
|
84
|
+
|
85
|
+
* Support for more languages
|
86
|
+
* Built in proxy-server to allow connections to web services
|
87
|
+
* More control over behavior
|
data/Rakefile
CHANGED
@@ -22,20 +22,32 @@ Jeweler::Tasks.new do |gem|
|
|
22
22
|
end
|
23
23
|
Jeweler::RubygemsDotOrgTasks.new
|
24
24
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
25
|
+
namespace :cover_me do
|
26
|
+
task :report do
|
27
|
+
require 'cover_me'
|
28
|
+
# CoverMe.config do |c|
|
29
|
+
# # where is your project's root:
|
30
|
+
# c.project.root = Dir.pwd
|
31
|
+
|
32
|
+
# # what files are you interested in coverage for:
|
33
|
+
# c.file_pattern = /(#{CoverMe.config.project.root}\/lib\/.+\.rb)/i
|
34
|
+
# end
|
35
|
+
CoverMe.complete!
|
36
|
+
end
|
30
37
|
end
|
31
38
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
39
|
+
# task :spec do
|
40
|
+
# Rake::Task['cover_me:report'].invoke
|
41
|
+
# end
|
42
|
+
|
43
|
+
require 'rspec/core'
|
44
|
+
require 'rspec/core/rake_task'
|
45
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
46
|
+
spec.pattern = FileList['spec/**/*_spec.rb']
|
37
47
|
end
|
38
48
|
|
49
|
+
|
50
|
+
|
39
51
|
task :default => :spec
|
40
52
|
|
41
53
|
require 'yard'
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.4.0
|
data/bin/slinky
CHANGED
data/lib/slinky/compiled_file.rb
CHANGED
@@ -2,62 +2,80 @@ module Slinky
|
|
2
2
|
# Stores information about compiled files, including location,
|
3
3
|
# source file and last modified time
|
4
4
|
class CompiledFile
|
5
|
-
|
6
|
-
|
5
|
+
attr_accessor :source, :print_name, :output_path
|
6
|
+
attr_reader :last_compiled, :output_ext
|
7
|
+
|
7
8
|
# Creates a new CompiledFile, compiling the provided source file
|
8
9
|
# with the provided compiler class.
|
9
10
|
def initialize source, compiler, output_ext
|
11
|
+
super source
|
10
12
|
@source = source
|
11
13
|
@compiler = compiler
|
12
14
|
@last_compiled = Time.new(0)
|
13
15
|
@output_ext = output_ext
|
14
16
|
end
|
15
17
|
|
18
|
+
def build path
|
19
|
+
compiler_compile path, EM::DefaultDeferrable
|
20
|
+
end
|
21
|
+
|
16
22
|
# Compiles the source file to a temporary location
|
17
23
|
def compile &cb
|
18
|
-
path = @
|
24
|
+
path = @output_path || tmp_path
|
19
25
|
@last_compiled = Time.now
|
20
26
|
if @compiler.respond_to? :compile
|
21
27
|
compiler_compile path, cb
|
22
28
|
else
|
23
|
-
|
29
|
+
compile_failed "invalid compiler"
|
30
|
+
cb.call @output_path, nil, nil, "invalid compiler"
|
31
|
+
# compiler_command path, cb
|
24
32
|
end
|
25
33
|
end
|
26
34
|
|
27
35
|
def compiler_compile path, cb
|
28
36
|
begin
|
29
|
-
out =
|
37
|
+
out = File.open @source do |f|
|
38
|
+
@compiler.compile f.read, @source
|
39
|
+
end
|
40
|
+
|
30
41
|
compile_succeeded
|
31
|
-
@path = path
|
32
42
|
File.open(path, "w+") do |f|
|
33
43
|
f.write out
|
34
44
|
end
|
35
|
-
rescue
|
45
|
+
rescue Exception
|
36
46
|
compile_failed $!
|
47
|
+
path = nil
|
37
48
|
end
|
38
|
-
cb.call
|
49
|
+
cb.call((path ? path.to_s : nil), nil, nil, $!)
|
39
50
|
end
|
40
51
|
|
41
|
-
def
|
42
|
-
|
52
|
+
# def compiler_command path, cb
|
53
|
+
# #NOTE: This currently won't strip out compiler directives, so
|
54
|
+
# #use at own risk. Ideally all compilers would have ruby version
|
55
|
+
# #so we don't have to use this
|
56
|
+
# command = @compiler.command @source, path
|
43
57
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
58
|
+
# EM.system3 command do |stdout, stderr, status|
|
59
|
+
# if status.exitstatus != 0
|
60
|
+
# compile_failed stderr.strip
|
61
|
+
# else
|
62
|
+
# compile_succeeded
|
63
|
+
# @output_path = path
|
64
|
+
# end
|
65
|
+
# cb.call(@output_path, status, stdout, stderr)
|
66
|
+
# end
|
67
|
+
# end
|
68
|
+
|
69
|
+
def name
|
70
|
+
@print_name || @source
|
53
71
|
end
|
54
72
|
|
55
73
|
def compile_succeeded
|
56
|
-
puts "Compiled #{
|
74
|
+
$stdout.puts "Compiled #{name}".foreground(:green)
|
57
75
|
end
|
58
76
|
|
59
77
|
def compile_failed e
|
60
|
-
$stderr.
|
78
|
+
$stderr.puts "Failed on #{name}: #{e}".foreground(:red)
|
61
79
|
end
|
62
80
|
|
63
81
|
# Calls the supplied callback with the path of the compiled file,
|
@@ -66,7 +84,7 @@ module Slinky
|
|
66
84
|
if needs_update?
|
67
85
|
compile &cb
|
68
86
|
else
|
69
|
-
cb.call @
|
87
|
+
cb.call @output_path, nil, nil, nil
|
70
88
|
end
|
71
89
|
end
|
72
90
|
|
@@ -74,7 +92,7 @@ module Slinky
|
|
74
92
|
# compiled.
|
75
93
|
def needs_update?
|
76
94
|
return true if @compiler.to_s.match "SassCompiler"
|
77
|
-
File.new(source).mtime > @last_compiled
|
95
|
+
File.new(source).mtime > @last_compiled rescue true
|
78
96
|
end
|
79
97
|
|
80
98
|
private
|
@@ -2,12 +2,11 @@ require 'coffee-script'
|
|
2
2
|
|
3
3
|
module Slinky
|
4
4
|
module CoffeeCompiler
|
5
|
-
|
5
|
+
Compilers.register_compiler self,
|
6
6
|
:inputs => ["coffee"],
|
7
7
|
:outputs => ["js"]
|
8
8
|
|
9
|
-
def CoffeeCompiler::compile file
|
10
|
-
s = File.read(file)
|
9
|
+
def CoffeeCompiler::compile s, file
|
11
10
|
CoffeeScript::compile(s)
|
12
11
|
end
|
13
12
|
end
|
@@ -2,14 +2,20 @@ require 'haml'
|
|
2
2
|
|
3
3
|
module Slinky
|
4
4
|
module HamlCompiler
|
5
|
-
|
5
|
+
Compilers.register_compiler self,
|
6
6
|
:inputs => ["haml"],
|
7
7
|
:outputs => ["html"]
|
8
8
|
|
9
|
-
def HamlCompiler::compile file
|
10
|
-
s = File.read(file)
|
9
|
+
def HamlCompiler::compile s, file
|
11
10
|
haml_engine = Haml::Engine.new(s)
|
12
11
|
haml_engine.render
|
13
|
-
end
|
12
|
+
end
|
13
|
+
|
14
|
+
# escape should return a string that can be inserted into the
|
15
|
+
# document in such a way that after compilation the string will be
|
16
|
+
# intact in the final output.
|
17
|
+
def HamlCompiler::escape s
|
18
|
+
s
|
19
|
+
end
|
14
20
|
end
|
15
21
|
end
|
@@ -2,12 +2,11 @@ require 'sass'
|
|
2
2
|
|
3
3
|
module Slinky
|
4
4
|
module SassCompiler
|
5
|
-
|
5
|
+
Compilers.register_compiler self,
|
6
6
|
:inputs => ["sass", "scss"],
|
7
7
|
:outputs => ["css"]
|
8
8
|
|
9
|
-
def SassCompiler::compile file
|
10
|
-
s = File.read(file)
|
9
|
+
def SassCompiler::compile s, file
|
11
10
|
sass_engine = Sass::Engine.new(s, :load_paths => [File.dirname(file)])
|
12
11
|
sass_engine.render
|
13
12
|
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module Slinky
|
2
|
+
EXTENSION_REGEX = /(.+)\.(\w+)/
|
3
|
+
|
4
|
+
class Compilers
|
5
|
+
@compilers = []
|
6
|
+
@compilers_by_ext = {}
|
7
|
+
@compilers_by_input = {}
|
8
|
+
|
9
|
+
class << self
|
10
|
+
def register_compiler klass, options
|
11
|
+
options[:klass] = klass
|
12
|
+
@compilers << options
|
13
|
+
options[:outputs].each do |output|
|
14
|
+
@compilers_by_ext[output] ||= []
|
15
|
+
@compilers_by_ext[output] << options
|
16
|
+
end
|
17
|
+
options[:inputs].each do |input|
|
18
|
+
@compilers_by_input[input] = options
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Produces a CompiledFile for an input file if the file needs to
|
23
|
+
# be compiled, or nil otherwise. Note that path is the path of
|
24
|
+
# the compiled file, so script.js not script.coffee.
|
25
|
+
def cfile_for_request path
|
26
|
+
_, file, extension = path.match(EXTENSION_REGEX).to_a
|
27
|
+
|
28
|
+
compilers = @compilers_by_ext
|
29
|
+
|
30
|
+
# if there's a file extension and we have a compiler that
|
31
|
+
# outputs that kind of file, look for an input with the same
|
32
|
+
# name and an extension in our list
|
33
|
+
if extension && extension != "" && compilers[extension]
|
34
|
+
files_by_ext = {}
|
35
|
+
# find possible source files
|
36
|
+
Dir.glob("#{file}.*").each do |f|
|
37
|
+
_, _, ext = f.match(EXTENSION_REGEX).to_a
|
38
|
+
files_by_ext[ext] = f
|
39
|
+
end
|
40
|
+
|
41
|
+
cfile = nil
|
42
|
+
# find a compiler that outputs the request kind of file and
|
43
|
+
# which has an input file type that exists on the file system
|
44
|
+
compilers[extension].each do |c|
|
45
|
+
c[:inputs].each do |i|
|
46
|
+
if files_by_ext[i]
|
47
|
+
cfile = CompiledFile.new files_by_ext[i], c[:klass], extension
|
48
|
+
break
|
49
|
+
end
|
50
|
+
end
|
51
|
+
break if cfile
|
52
|
+
end
|
53
|
+
cfile
|
54
|
+
else
|
55
|
+
nil
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Like cfile_for_request, but for the actual file to be compiled
|
60
|
+
# (so script.coffee, not script.js).
|
61
|
+
def cfile_for_file path
|
62
|
+
_, file, ext = path.match(EXTENSION_REGEX).to_a
|
63
|
+
|
64
|
+
if ext && ext != "" && compiler = @compilers_by_input[ext]
|
65
|
+
CompiledFile.new path, compiler[:klass], compiler[:outputs].first
|
66
|
+
else
|
67
|
+
nil
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|