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 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
- #gem "sass", ">= 4.0.0"
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 "bacon", ">= 0"
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 "rcov", ">= 0"
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 is a static file server designed to make development of static
4
- web sites and applications simpler. Simply run slinky in the home
5
- directory of your app and it will serve while compiling things like
6
- SASS, HAML and CoffeeScript on the fly.
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
- To get it, just `gem install slinky`. Note that this is unstable, not well
9
- tested, and hardly feature-complete. It may, however, be useful.
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
- require 'rake/testtask'
26
- Rake::TestTask.new(:spec) do |spec|
27
- spec.libs << 'lib' << 'spec'
28
- spec.pattern = 'spec/**/*_spec.rb'
29
- spec.verbose = true
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
- require 'rcov/rcovtask'
33
- Rcov::RcovTask.new do |spec|
34
- spec.libs << 'spec'
35
- spec.pattern = 'spec/**/*_spec.rb'
36
- spec.verbose = true
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.2.1
1
+ 0.4.0
data/bin/slinky CHANGED
@@ -5,4 +5,4 @@ root = File.expand_path(File.dirname(__FILE__))
5
5
  #require "#{root}/../lib/slinky"
6
6
  require 'slinky'
7
7
 
8
- Slinky::Runner.run
8
+ Slinky::Runner.new(ARGV).run
@@ -0,0 +1,8 @@
1
+ module Slinky
2
+ class Builder
3
+ def self.build dir, build_dir
4
+ manifest = Manifest.new(dir, :build_to => build_dir, :devel => false)
5
+ manifest.build
6
+ end
7
+ end
8
+ end
@@ -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
- attr_reader :source, :last_compiled
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 = @path || tmp_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
- compiler_command path, cb
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 = @compiler.compile @source
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 @path, nil, nil, $!
49
+ cb.call((path ? path.to_s : nil), nil, nil, $!)
39
50
  end
40
51
 
41
- def command path, cb
42
- command = @compiler.command @source, path
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
- EM.system3 command do |stdout, stderr, status|
45
- if status.exitstatus != 0
46
- compile_failed stderr.strip
47
- else
48
- compile_succeeded
49
- @path = path
50
- end
51
- cb.call(@path, status, stdout, stderr)
52
- end
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 #{@source}".foreground(:green)
74
+ $stdout.puts "Compiled #{name}".foreground(:green)
57
75
  end
58
76
 
59
77
  def compile_failed e
60
- $stderr.write "Failed on #{@source}: #{e}\n".foreground(:red)
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 @path, nil, nil, nil
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
- Server.register_compiler self,
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
- Server.register_compiler self,
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
- Server.register_compiler self,
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