slinky 0.2.1 → 0.4.0
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.
- 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
|