machined 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. data/.gitignore +4 -0
  2. data/Gemfile +2 -0
  3. data/LICENSE +20 -0
  4. data/README.md +49 -0
  5. data/Rakefile +6 -0
  6. data/bin/machined +4 -0
  7. data/lib/machined.rb +23 -0
  8. data/lib/machined/cli.rb +99 -0
  9. data/lib/machined/context.rb +52 -0
  10. data/lib/machined/environment.rb +297 -0
  11. data/lib/machined/helpers/asset_tag_helpers.rb +135 -0
  12. data/lib/machined/helpers/locals_helpers.rb +57 -0
  13. data/lib/machined/helpers/output_helpers.rb +43 -0
  14. data/lib/machined/helpers/render_helpers.rb +138 -0
  15. data/lib/machined/processors/front_matter_processor.rb +35 -0
  16. data/lib/machined/processors/layout_processor.rb +71 -0
  17. data/lib/machined/server.rb +37 -0
  18. data/lib/machined/sprocket.rb +55 -0
  19. data/lib/machined/static_compiler.rb +71 -0
  20. data/lib/machined/templates/site/Gemfile.tt +10 -0
  21. data/lib/machined/templates/site/assets/images/.empty_directory +0 -0
  22. data/lib/machined/templates/site/assets/javascripts/main.js.coffee +0 -0
  23. data/lib/machined/templates/site/assets/stylesheets/main.css.scss +0 -0
  24. data/lib/machined/templates/site/config.ru +2 -0
  25. data/lib/machined/templates/site/machined.rb +17 -0
  26. data/lib/machined/templates/site/pages/index.html.erb +5 -0
  27. data/lib/machined/templates/site/public/.empty_directory +0 -0
  28. data/lib/machined/templates/site/views/layouts/main.html.erb +12 -0
  29. data/lib/machined/utils.rb +31 -0
  30. data/lib/machined/version.rb +3 -0
  31. data/machined.gemspec +39 -0
  32. data/spec/machined/cli_spec.rb +154 -0
  33. data/spec/machined/context_spec.rb +20 -0
  34. data/spec/machined/environment_spec.rb +202 -0
  35. data/spec/machined/helpers/asset_tag_helpers_spec.rb +95 -0
  36. data/spec/machined/helpers/locals_helper_spec.rb +37 -0
  37. data/spec/machined/helpers/output_helpers_spec.rb +81 -0
  38. data/spec/machined/helpers/render_helpers_spec.rb +53 -0
  39. data/spec/machined/processors/front_matter_processor_spec.rb +42 -0
  40. data/spec/machined/processors/layout_processor_spec.rb +32 -0
  41. data/spec/machined/server_spec.rb +77 -0
  42. data/spec/machined/sprocket_spec.rb +36 -0
  43. data/spec/machined/static_compiler_spec.rb +85 -0
  44. data/spec/machined/utils_spec.rb +31 -0
  45. data/spec/spec_helper.rb +16 -0
  46. data/spec/support/helpers.rb +59 -0
  47. data/spec/support/match_paths_matcher.rb +20 -0
  48. metadata +389 -0
@@ -0,0 +1,37 @@
1
+ require "rack"
2
+
3
+ module Machined
4
+ class Server
5
+ # A reference to the Machined environment which
6
+ # created this instance.
7
+ attr_reader :machined
8
+
9
+ # Creates a new Rack server that will serve
10
+ # up the processed files.
11
+ def initialize(machined)
12
+ @machined = machined
13
+ @files = Rack::File.new(machined.output_path)
14
+ remap
15
+ end
16
+
17
+ # Using the URLMap, determine which sprocket
18
+ # should handle the request and then...let it
19
+ # handle it.
20
+ def call(env)
21
+ response = @url_map.call(env)
22
+ response = @files.call(env) if response.first == 404
23
+ response
24
+ end
25
+
26
+ # Remaps the Machined environment's current
27
+ # sprockets using `Rack::URLMap`.
28
+ def remap
29
+ map = {}
30
+ machined.sprockets.each do |sprocket|
31
+ next unless sprocket.compile?
32
+ map[sprocket.config.url] = sprocket
33
+ end
34
+ @url_map = Rack::URLMap.new map
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,55 @@
1
+ require "ostruct"
2
+ require "sprockets"
3
+
4
+ module Machined
5
+ class Sprocket < Sprockets::Environment
6
+ # Default options for a Machined sprocket.
7
+ DEFAULT_OPTIONS = {
8
+ :root => ".",
9
+ :assets => false,
10
+ :compile => true
11
+ }.freeze
12
+
13
+ # A reference to the Machined environment which
14
+ # created this instance.
15
+ attr_reader :machined
16
+
17
+ # A reference to the configuration.
18
+ attr_reader :config
19
+
20
+ # Creates a new Machined sprocket. The API is
21
+ # a bit different than `Sprockets::Environment` to
22
+ # allow for per-Sprockets-environment configuration
23
+ # and to keep a reference to the Machined environment.
24
+ def initialize(machined, options = {})
25
+ @machined = machined
26
+ @config = OpenStruct.new DEFAULT_OPTIONS.dup.merge(options)
27
+
28
+ super config.root
29
+
30
+ @context_class = Class.new Context
31
+ use_all_templates unless config.assets
32
+ end
33
+
34
+ # Returns true, if this sprocket should be
35
+ # compiled. Nine times out of ten, you will want
36
+ # your sprocket compiled, but sometimes - like
37
+ # the default views sprocket - it is used as
38
+ # a uncompiled resource.
39
+ def compile?
40
+ config.compile && config.url
41
+ end
42
+
43
+ # Loops through the available Tilt templates
44
+ # and registers them as processor engines for
45
+ # Sprockets. By default, Sprockets cherry picks
46
+ # templates that work for web assets. We need to
47
+ # allow use of Haml, Markdown, etc.
48
+ def use_all_templates
49
+ Utils.available_templates.each do |ext, template|
50
+ next if engines(ext)
51
+ register_engine ext, template
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,71 @@
1
+ require "fileutils"
2
+
3
+ module Machined
4
+ class StaticCompiler
5
+ # A reference to the Machined environment which
6
+ # created this instance.
7
+ attr_reader :machined
8
+
9
+ # Creates a new instance, which will compile
10
+ # the assets to the given +output_path+.
11
+ def initialize(machined)
12
+ @machined = machined
13
+ end
14
+
15
+ # Loop through and compile each available
16
+ # asset to the appropriate output path.
17
+ def compile
18
+ compiled_assets = {}
19
+ machined.sprockets.each do |sprocket|
20
+ next unless sprocket.compile?
21
+ sprocket.each_logical_path do |logical_path|
22
+ url = File.join(sprocket.config.url, logical_path)
23
+ next unless compiled_assets[url].nil? && compile?(url)
24
+
25
+ if asset = sprocket.find_asset(logical_path)
26
+ compiled_assets[url] = write_asset(asset)
27
+ end
28
+ end
29
+ end
30
+ compiled_assets
31
+ end
32
+
33
+ # Determines if we should precompile the asset
34
+ # with the given url. By default, we skip over any
35
+ # files that begin with "_", like partials.
36
+ def compile?(url)
37
+ File.basename(url) !~ /^_/
38
+ end
39
+
40
+ # Writes the asset to its destination, also
41
+ # writing a gzipped version if necessary.
42
+ def write_asset(asset)
43
+ filename = path_for(asset)
44
+ FileUtils.mkdir_p File.dirname(filename)
45
+ asset.write_to filename
46
+ asset.write_to "#{filename}.gz" if gzip?(filename)
47
+ asset.digest
48
+ end
49
+
50
+ protected
51
+
52
+ # Gets the full output path for the given asset.
53
+ # If it's supposed to include a digest, it will return the
54
+ # digest_path.
55
+ def path_for(asset) # :nodoc:
56
+ path = digest?(asset) ? asset.digest_path : asset.logical_path
57
+ File.join(machined.output_path, asset.environment.config.url, path)
58
+ end
59
+
60
+ # Determines if we should use the digest_path for the given
61
+ # asset.
62
+ def digest?(asset) # :nodoc:
63
+ machined.config.digest_assets && asset.environment.config.assets
64
+ end
65
+
66
+ # Determines if we should gzip the asset.
67
+ def gzip?(filename) # :nodoc:
68
+ machined.config.gzip_assets && filename =~ /\.(css|js)$/
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,10 @@
1
+ source :rubygems
2
+
3
+ gem "machined", "<%= Machined::VERSION %>"
4
+
5
+ gem "sass", "~> 3.1"
6
+ gem "coffee-script", "~> 2.2"
7
+
8
+ group :production do
9
+ gem "uglifier", "~> 1.0"
10
+ end
@@ -0,0 +1,2 @@
1
+ require "machined"
2
+ run Machined::Environment.new
@@ -0,0 +1,17 @@
1
+ require "bundler"
2
+ Bundler.require :default, config.environment.to_sym
3
+
4
+ if config.environment == "production"
5
+ # Compress javascripts and stylesheets
6
+ config.compress = true
7
+
8
+ # Generate digests for assets URLs
9
+ # config.digest_assets = true
10
+
11
+ # Create gzipped versions of javascripts and stylesheets
12
+ # config.gzip_assets = true
13
+ end
14
+
15
+ helpers do
16
+ # Define helper methods here
17
+ end
@@ -0,0 +1,5 @@
1
+ ---
2
+ title: Home Page
3
+ ---
4
+ <h1><%= title %></h1>
5
+ <p>Find me in pages/index.erb</p>
@@ -0,0 +1,12 @@
1
+ <!doctype html>
2
+ <html>
3
+ <head lang="en">
4
+ <meta charset="utf-8">
5
+ <title><%= title %></title>
6
+ <%= stylesheet_link_tag "main" %>
7
+ <%= javascript_include_tag "main" %>
8
+ </head>
9
+ <body>
10
+ <%= yield %>
11
+ </body>
12
+ </html>
@@ -0,0 +1,31 @@
1
+ require "pathname"
2
+ require "sprockets"
3
+ require "tilt"
4
+
5
+ module Machined
6
+ module Utils
7
+ # Returns a hash of the Tilt templates
8
+ # that are registered and available to use, where
9
+ # the key is the extension the template's registered for.
10
+ def self.available_templates
11
+ @available_templates ||= {}.tap do |templates|
12
+ Tilt.mappings.each_key do |ext|
13
+ begin
14
+ templates[Sprockets::Utils.normalize_extension(ext)] = Tilt[ext]
15
+ rescue LoadError
16
+ # safely ignore...
17
+ end
18
+ end
19
+ end
20
+ end
21
+
22
+ # Returns an `Array` of the child directories that
23
+ # exist within the given +path+. If the path itself
24
+ # does not exist, an emtpy array is returned.
25
+ def self.existent_directories(path)
26
+ pathname = Pathname.new path
27
+ pathname.directory? or return []
28
+ pathname.children.select &:directory?
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,3 @@
1
+ module Machined
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,39 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "machined/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "machined"
7
+ s.version = Machined::VERSION
8
+ s.authors = ["Pete Browne"]
9
+ s.email = ["me@petebrowne.com"]
10
+ s.homepage = ""
11
+ s.summary = %q{A static site generator and Rack server built using Sprockets 2.0}
12
+ s.description = %q{Why another static site generator? Machined is for the developers who know and love the asset pipeline of Rails 3.1 and want to develop blazingly fast static websites. It's built from the ground up using Sprockets 2.0.}
13
+
14
+ s.rubyforge_project = "machined"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ s.add_dependency "sprockets", "~> 2.0"
22
+ s.add_dependency "sprockets-sass", "~> 0.3"
23
+ s.add_dependency "padrino-helpers", "~> 0.10"
24
+ s.add_dependency "activesupport", "~> 3.1"
25
+ s.add_dependency "i18n", "~> 0.6"
26
+ s.add_dependency "thor", "~> 0.14"
27
+ s.add_dependency "crush", "~> 0.3"
28
+ s.add_development_dependency "rspec", "~> 2.6"
29
+ s.add_development_dependency "rack-test", "~> 0.6"
30
+ s.add_development_dependency "test-construct", "~> 1.2"
31
+ s.add_development_dependency 'unindent', "~> 1.0"
32
+ s.add_development_dependency "haml"
33
+ s.add_development_dependency "sass"
34
+ s.add_development_dependency "slim"
35
+ s.add_development_dependency "erubis"
36
+ s.add_development_dependency "rdiscount"
37
+ s.add_development_dependency "uglifier"
38
+ s.add_development_dependency "rake"
39
+ end
@@ -0,0 +1,154 @@
1
+ require "spec_helper"
2
+
3
+ describe Machined::CLI do
4
+ describe "#compile" do
5
+ it "compiles the site" do
6
+ machined.should_receive(:compile)
7
+ Machined::Environment.should_receive(:new).with(:root => ".", :output_path => "public", :environment => "production", :config_path => "machined.rb").and_return(machined)
8
+ machined_cli "compile -e production"
9
+ end
10
+ end
11
+
12
+ describe "#new" do
13
+ it "creates a machined site directory" do
14
+ within_construct do |c|
15
+ machined_cli "new my_site"
16
+ File.directory?("my_site").should be_true
17
+ end
18
+ end
19
+
20
+ it "creates source directories" do
21
+ within_construct do |c|
22
+ machined_cli "new my_site"
23
+ File.directory?("my_site/pages").should be_true
24
+ File.directory?("my_site/views").should be_true
25
+ File.directory?("my_site/assets/images").should be_true
26
+ File.directory?("my_site/assets/javascripts").should be_true
27
+ File.directory?("my_site/assets/stylesheets").should be_true
28
+ end
29
+ end
30
+
31
+ it "creates an output path" do
32
+ within_construct do |c|
33
+ machined_cli "new my_site"
34
+ File.directory?("my_site/public").should be_true
35
+ end
36
+ end
37
+
38
+ it "creates an default index page" do
39
+ within_construct do |c|
40
+ machined_cli "new my_site"
41
+ File.read("my_site/pages/index.html.erb").should == <<-CONTENT.unindent
42
+ ---
43
+ title: Home Page
44
+ ---
45
+ <h1><%= title %></h1>
46
+ <p>Find me in pages/index.erb</p>
47
+ CONTENT
48
+ end
49
+ end
50
+
51
+ it 'creates a default layout' do
52
+ within_construct do |c|
53
+ machined_cli "new my_site"
54
+ File.read("my_site/views/layouts/main.html.erb").should == <<-CONTENT.unindent
55
+ <!doctype html>
56
+ <html>
57
+ <head lang="en">
58
+ <meta charset="utf-8">
59
+ <title><%= title %></title>
60
+ <%= stylesheet_link_tag "main" %>
61
+ <%= javascript_include_tag "main" %>
62
+ </head>
63
+ <body>
64
+ <%= yield %>
65
+ </body>
66
+ </html>
67
+ CONTENT
68
+ end
69
+ end
70
+
71
+ it "creates a default javascript file" do
72
+ within_construct do |c|
73
+ machined_cli "new my_site"
74
+ File.exist?("my_site/assets/javascripts/main.js.coffee").should be_true
75
+ end
76
+ end
77
+
78
+ it "creates a default stylesheet file" do
79
+ within_construct do |c|
80
+ machined_cli "new my_site"
81
+ File.exist?("my_site/assets/stylesheets/main.css.scss").should be_true
82
+ end
83
+ end
84
+
85
+ it "creates a default Gemfile" do
86
+ within_construct do |c|
87
+ machined_cli "new my_site"
88
+ File.read("my_site/Gemfile").should == <<-CONTENT.unindent
89
+ source :rubygems
90
+
91
+ gem "machined", "#{Machined::VERSION}"
92
+
93
+ gem "sass", "~> 3.1"
94
+ gem "coffee-script", "~> 2.2"
95
+
96
+ group :production do
97
+ gem "uglifier", "~> 1.0"
98
+ end
99
+ CONTENT
100
+ end
101
+ end
102
+
103
+ it "creates a default config file" do
104
+ within_construct do |c|
105
+ machined_cli "new my_site"
106
+ File.read("my_site/machined.rb").should == <<-CONTENT.unindent
107
+ require "bundler"
108
+ Bundler.require :default, config.environment.to_sym
109
+
110
+ if config.environment == "production"
111
+ # Compress javascripts and stylesheets
112
+ config.compress = true
113
+
114
+ # Generate digests for assets URLs
115
+ # config.digest_assets = true
116
+
117
+ # Create gzipped versions of javascripts and stylesheets
118
+ # config.gzip_assets = true
119
+ end
120
+
121
+ helpers do
122
+ # Define helper methods here
123
+ end
124
+ CONTENT
125
+ end
126
+ end
127
+
128
+ it "creates a default rackup file" do
129
+ within_construct do |c|
130
+ machined_cli "new my_site"
131
+ File.read("my_site/config.ru").should == <<-CONTENT.unindent
132
+ require "machined"
133
+ run Machined::Environment.new
134
+ CONTENT
135
+ end
136
+ end
137
+ end
138
+
139
+ describe "#server" do
140
+ it "should start a Rack server" do
141
+ app = machined
142
+ Machined::Environment.should_receive(:new).with(:root => ".", :output_path => "site", :environment => "production", :config_path => "machined.rb").and_return(app)
143
+ Rack::Server.should_receive(:start).with(hash_including(:app => app, :environment => "production", :Port => 5000))
144
+ machined_cli "server -o site -e production -p 5000"
145
+ end
146
+ end
147
+
148
+ describe "#version" do
149
+ it "prints out the current version number" do
150
+ output = machined_cli "version"
151
+ output.strip.should == Machined::VERSION
152
+ end
153
+ end
154
+ end