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,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source :rubygems
2
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Peter Browne
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,49 @@
1
+ Machined
2
+ ========
3
+
4
+ ### Why another [static](https://github.com/mojombo/jekyll) [site](https://github.com/cloudhead/toto) [generator](https://github.com/petebrowne/massimo)?
5
+
6
+ Machined is for the developers who know and love [the asset pipeline](http://edgeguides.rubyonrails.org/asset_pipeline.html) of Rails 3.1 and want to develop blazingly fast static websites. It's built from the ground up using [Sprockets 2.0](https://github.com/search?q=sprockets).
7
+
8
+ Installation
9
+ ------------
10
+
11
+ ``` bash
12
+ $ gem install machined
13
+ ```
14
+
15
+ Getting Started
16
+ ---------------
17
+
18
+ ``` bash
19
+ $ machined new example.com
20
+ ```
21
+
22
+ This creates a directory with the default Machined project structure. More on that later. Let's start up the Machined server:
23
+
24
+ ``` bash
25
+ $ cd example.com
26
+ $ machined server
27
+ ```
28
+
29
+ Now that the server is running, edit your pages, assets, etc. and view the results. Most static site servers need to recompile the _entire_ site each time a request is made. Machined (well really Sprockets) is smart enough to compile only what you request, so developing is super fast.
30
+
31
+ Deploying a Static Website
32
+ --------------------------
33
+
34
+ Once you've created your site, it's time to go live. On your production box, you just need to compile the site and let Apache, Nginx, or whatever handle the serving. It'll be fast - damn fast.
35
+
36
+ ``` bash
37
+ $ cd example.com
38
+ $ machined compile --environment production
39
+ ```
40
+
41
+ Diving In
42
+ ---------
43
+
44
+ TODO...
45
+
46
+ Copyright
47
+ ---------
48
+
49
+ Copyright (c) 2011 [Peter Browne](http://petebrowne.com). See LICENSE for details.
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require "rspec/core/rake_task"
4
+ RSpec::Core::RakeTask.new
5
+
6
+ task :default => :spec
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "machined"
4
+ Machined::CLI.start
@@ -0,0 +1,23 @@
1
+ require "machined/version"
2
+
3
+ module Machined
4
+ autoload :CLI, "machined/cli"
5
+ autoload :Context, "machined/context"
6
+ autoload :Environment, "machined/environment"
7
+ autoload :Server, "machined/server"
8
+ autoload :Sprocket, "machined/sprocket"
9
+ autoload :StaticCompiler, "machined/static_compiler"
10
+ autoload :Utils, "machined/utils"
11
+
12
+ module Helpers
13
+ autoload :AssetTagHelpers, "machined/helpers/asset_tag_helpers"
14
+ autoload :LocalsHelpers, "machined/helpers/locals_helpers"
15
+ autoload :OutputHelpers, "machined/helpers/output_helpers"
16
+ autoload :RenderHelpers, "machined/helpers/render_helpers"
17
+ end
18
+
19
+ module Processors
20
+ autoload :FrontMatterProcessor, "machined/processors/front_matter_processor"
21
+ autoload :LayoutProcessor, "machined/processors/layout_processor"
22
+ end
23
+ end
@@ -0,0 +1,99 @@
1
+ require "active_support/core_ext/hash/keys"
2
+ require "active_support/core_ext/hash/slice"
3
+ require "thor"
4
+
5
+ module Machined
6
+ class CLI < Thor
7
+ include Thor::Actions
8
+
9
+ default_task :help
10
+ source_root File.expand_path("../templates", __FILE__)
11
+
12
+ class_option "root", :aliases => "-r",
13
+ :desc => "Path to the root directory of the project",
14
+ :default => "."
15
+ class_option "config_path", :aliases => "-c",
16
+ :desc => "Path to the config file",
17
+ :default => "machined.rb"
18
+ class_option "output_path", :aliases => "-o",
19
+ :desc => "Path to the output directory of the project",
20
+ :default => "public"
21
+ class_option "environment", :aliases => "-e",
22
+ :desc => "Sets the environment",
23
+ :default => "development"
24
+
25
+ desc "compile", "Compiles the site from the source files"
26
+ def compile
27
+ machined.compile
28
+ end
29
+ map %w(c build b) => :compile
30
+
31
+ desc "new SITE_NAME", "Generates a new site with the give name"
32
+ def new(site_name)
33
+ directory "site", site_name
34
+ end
35
+ map %w(n generate g) => :new
36
+
37
+ desc "server", "Runs a local Rack based web server"
38
+ method_option :port, :aliases => "-p",
39
+ :desc => "Serve at the given port",
40
+ :type => :numeric, :default => 3000
41
+ method_option :host, :aliases => "-h",
42
+ :desc => "Listen on the given given host",
43
+ :default => "0.0.0.0"
44
+ method_option :server, :aliases => "-s",
45
+ :desc => "Serve with the given handler"
46
+ method_option :daemonize, :aliases => "-D",
47
+ :desc => "Run daemonized in the background",
48
+ :type => :boolean
49
+ method_option :pid, :aliases => "-P",
50
+ :desc => "File to store PID"
51
+ def server
52
+ require "rack"
53
+ Rack::Server.start rack_options
54
+ end
55
+ map %w(s rackup r) => :server
56
+
57
+ desc "version", "Prints out the version"
58
+ def version
59
+ say VERSION
60
+ end
61
+ map %w(v -v --version) => :version
62
+
63
+ protected
64
+
65
+ def machined
66
+ @machined ||= Environment.new machined_options
67
+ end
68
+
69
+ # Returns the current environment, using the "RACK_ENV" variable
70
+ # if set.
71
+ def environment # :nodoc
72
+ ENV["RACK_ENV"] || options["environment"]
73
+ end
74
+
75
+ # Returns the options needed for setting up
76
+ # Machined environment.
77
+ def machined_options # :nodoc:
78
+ symbolized_options(:root, :config_path, :output_path).tap do |machined_options|
79
+ machined_options[:environment] = environment
80
+ end
81
+ end
82
+
83
+ # Returns the options needed for setting up the Rack server.
84
+ def rack_options # :nodoc:
85
+ symbolized_options(:port, :host, :server, :daemonize, :pid).tap do |rack_options|
86
+ rack_options[:environment] = environment
87
+ rack_options[:Port] = rack_options.delete :port
88
+ rack_options[:Host] = rack_options.delete :host
89
+ rack_options[:app] = machined
90
+ end
91
+ end
92
+
93
+ # Returns a mutable options hash with symbolized keys.
94
+ # Optionally, returns only the keys given.
95
+ def symbolized_options(*keys) # :nodoc:
96
+ {}.merge(options).symbolize_keys.slice(*keys)
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,52 @@
1
+ require "padrino-helpers"
2
+ require "rack"
3
+ require "sprockets"
4
+
5
+ module Machined
6
+ class Context < Sprockets::Context
7
+ include Helpers::OutputHelpers
8
+ include Padrino::Helpers::OutputHelpers
9
+ include Padrino::Helpers::TagHelpers
10
+ include Padrino::Helpers::AssetTagHelpers
11
+ include Padrino::Helpers::FormHelpers
12
+ include Padrino::Helpers::FormatHelpers
13
+ include Padrino::Helpers::NumberHelpers
14
+ include Padrino::Helpers::TranslationHelpers
15
+ include Helpers::AssetTagHelpers
16
+ include Helpers::LocalsHelpers
17
+ include Helpers::RenderHelpers
18
+
19
+ # Override initialize to add helpers
20
+ # from the Machined environment.
21
+ def initialize(*args) # :nodoc:
22
+ super
23
+ add_machined_helpers
24
+ end
25
+
26
+ # Returns the main Machined environment instance.
27
+ def machined
28
+ environment.machined
29
+ end
30
+
31
+ # Returns the configuration of the Machined environment.
32
+ def config
33
+ machined.config
34
+ end
35
+
36
+ protected
37
+
38
+ # Loops through the helpers added to the Machined
39
+ # environment and adds them to the Context. Supports
40
+ # blocks and Modules.
41
+ def add_machined_helpers # :nodoc:
42
+ machined.context_helpers.each do |helper|
43
+ case helper
44
+ when Proc
45
+ instance_eval &helper
46
+ when Module
47
+ extend helper
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,297 @@
1
+ require "ostruct"
2
+ require "pathname"
3
+ require "active_support/core_ext/hash/reverse_merge"
4
+ require "crush"
5
+ require "tilt"
6
+
7
+ module Machined
8
+ class Environment
9
+ # Default options for a Machined environment.
10
+ DEFAULT_OPTIONS = {
11
+ # Global configuration
12
+ :root => ".",
13
+ :config_path => "machined.rb",
14
+ :output_path => "public",
15
+ :environment => "development",
16
+ :digest_assets => false,
17
+ :gzip_assets => false,
18
+ :layout => "main",
19
+
20
+ # Sprocket paths and URLs
21
+ :assets_path => "assets",
22
+ :assets_paths => %w(lib/assets vendor/assets),
23
+ :assets_url => "/assets",
24
+ :pages_path => "pages",
25
+ :pages_url => "/",
26
+ :views_path => "views",
27
+
28
+ # Compression configuration
29
+ :compress => false,
30
+ :compress_css => false,
31
+ :compress_js => false,
32
+ :css_compressor => nil,
33
+ :js_compressor => nil
34
+ }.freeze
35
+
36
+ # A hash of Javascript compressors. When `config.js_compressor`
37
+ # is set using a symbol, such as `:uglifier`, this is where
38
+ # we check which engine to use.
39
+ JS_COMPRESSORS = {
40
+ :jsmin => Crush::JSMin,
41
+ :packr => Crush::Packr,
42
+ :yui => Crush::YUI::JavaScriptCompressor,
43
+ :closure => Crush::Closure::Compiler,
44
+ :uglifier => Crush::Uglifier
45
+ }
46
+
47
+ # A hash of CSS compressors. When `config.css_compressor`
48
+ # is set using a symbol, such as `:sass`, this is where
49
+ # we check which engine to use.
50
+ CSS_COMPRESSORS = {
51
+ :cssmin => Crush::CSSMin,
52
+ :rainpress => Crush::Rainpress,
53
+ :yui => Crush::YUI::CssCompressor,
54
+ :sass => Crush::Sass::Engine
55
+ }
56
+
57
+ # The global configuration for the Machined
58
+ # environment.
59
+ attr_reader :config
60
+
61
+ # An array of the helpers added to the Context
62
+ # through the `#helpers` method.
63
+ attr_reader :context_helpers
64
+
65
+ # When the Machined environment is compiling static files,
66
+ # this will reference the `Machined::StaticCompiler` which handles
67
+ # looping through the available files and generating them.
68
+ attr_reader :static_compiler
69
+
70
+ # A reference to the root directory the Machined
71
+ # environment is run from.
72
+ attr_reader :root
73
+
74
+ # A reference to the directory the Machined environment
75
+ # compiles its files to.
76
+ attr_reader :output_path
77
+
78
+ # An `Array` of the Sprockets environments (actually `Machined::Sprocket`
79
+ # instances) that are the core of a Machined environment.
80
+ attr_reader :sprockets
81
+
82
+ # When the Machined environment is used as a Rack server, this
83
+ # will reference the actual `Machined::Server` instance that handles
84
+ # the requests.
85
+ attr_reader :server
86
+
87
+ # Creates a new Machined environment. It sets up three default
88
+ # sprockets:
89
+ #
90
+ # * An assets sprocket similar to what you would use
91
+ # in a Rails 3.1 app.
92
+ # * A pages sprocket which handles static HTML pages.
93
+ # * A views sprocket, which is not compiled, which is where
94
+ # layouts and partials go.
95
+ #
96
+ def initialize(options = {})
97
+ @config = OpenStruct.new DEFAULT_OPTIONS.dup.merge(options)
98
+ @root = Pathname.new(config.root).expand_path
99
+ @sprockets = []
100
+ @context_helpers = []
101
+
102
+ # Create and append the default `assets` sprocket.
103
+ # This sprocket mimics the asset pipeline in Rails 3.1.
104
+ append_sprocket :assets, :assets => true
105
+
106
+ # Create and append the default `pages` sprocket.
107
+ # This sprocket is responsible for processing HTML pages,
108
+ # and includes processors for wrapping pages in layouts and
109
+ # reading YAML front matter.
110
+ append_sprocket :pages do |pages|
111
+ pages.register_mime_type "text/html", ".html"
112
+ pages.register_preprocessor "text/html", Processors::FrontMatterProcessor
113
+ pages.register_postprocessor "text/html", Processors::LayoutProcessor
114
+ end
115
+
116
+ # Create and append the default `views` sprocket.
117
+ # The files in this sprocket are not compiled. They are
118
+ # meant to be resources for the other sprockets. For instance,
119
+ # the layouts for the `pages` sprocket will be located here.
120
+ append_sprocket :views, :compile => false do |views|
121
+ views.register_mime_type "text/html", ".html"
122
+ end
123
+
124
+ # If there's a config file, execute with the scope of the
125
+ # newly created Machined environment. The default sprockets are
126
+ # available at this point, but not fully configured. This is so
127
+ # you can actually configure the sprockets with this file.
128
+ config_file = root.join(config.config_path)
129
+ instance_eval config_file.read if config_file.exist?
130
+
131
+ # Handle anymore configuration related setup.
132
+ @output_path = root.join(config.output_path)
133
+
134
+ # Append the paths for each sprocket. The default `assets` sprocket
135
+ # is special, because we actually append the directories within
136
+ # the given paths (like the Rails 3.1 asset pipeline).
137
+ append_path pages, config.pages_path
138
+ append_paths pages, config.pages_paths
139
+ append_path views, config.views_path
140
+ append_paths views, config.views_paths
141
+ append_paths assets, Utils.existent_directories(root.join(config.assets_path))
142
+ config.assets_paths.each do |asset_path|
143
+ append_paths assets, Utils.existent_directories(root.join(asset_path))
144
+ end
145
+
146
+ # Set the URLs for the compilable default sprockets.
147
+ assets.config.url = config.assets_url
148
+ pages.config.url = config.pages_url
149
+
150
+ # Now setup assets compression.
151
+ if config.compress
152
+ config.compress_js = true
153
+ config.compress_css = true
154
+ else
155
+ config.compress_js = true if config.js_compressor
156
+ config.compress_css = true if config.css_compressor
157
+ end
158
+ assets.js_compressor = js_compressor if config.compress_js
159
+ assets.css_compressor = css_compressor if config.compress_css
160
+ end
161
+
162
+ # Handles Rack requests by passing the +env+ to an instance
163
+ # of `Machined::Server`.
164
+ def call(env)
165
+ @server ||= Server.new self
166
+ server.call(env)
167
+ end
168
+
169
+ # Loops through the available static files and generates them in
170
+ # the output path.
171
+ def compile
172
+ @static_compiler ||= StaticCompiler.new self
173
+ static_compiler.compile
174
+ end
175
+
176
+ # Creates a Machined sprocket with the given +name+ and +options+
177
+ # and appends it to the #sprockets list. This will also create
178
+ # an accessor with the given name that references the created sprocket.
179
+ #
180
+ # machined.append_sprocket :updates, :url => "/news" do |updates|
181
+ # updates.append_path "updates"
182
+ # end
183
+ #
184
+ # machined.updates # => #<Machined::Sprocket...>
185
+ # machined.updates.config.url # => "/news"
186
+ # machined.updates.paths # => [ ".../updates" ]
187
+ #
188
+ def append_sprocket(name, options = {}, &block)
189
+ create_sprocket(name, options, &block).tap do |sprocket|
190
+ sprockets.push(sprocket).uniq!
191
+ server and server.remap
192
+ end
193
+ end
194
+
195
+ # Creates a Machined sprocket with the given +name+ and +options+
196
+ # and prepends it to the #sprockets list.
197
+ def prepend_sprocket(name, options = {}, &block)
198
+ create_sprocket(name, options, &block).tap do |sprocket|
199
+ sprockets.unshift(sprocket).uniq!
200
+ server and server.remap
201
+ end
202
+ end
203
+
204
+ # Adds helpers that can be used within asset files.
205
+ # There's two supported ways to add helpers, the first of
206
+ # which is similar to how the `#helpers` DSL works in Sinatra:
207
+ #
208
+ # helpers do
209
+ # def current_time
210
+ # Time.now
211
+ # end
212
+ # end
213
+ #
214
+ # The other way is to pass modules directly:
215
+ #
216
+ # module CycleHelper
217
+ # def cycle(*args)
218
+ # # ...
219
+ # end
220
+ # end
221
+ #
222
+ # helpers CycleHelper
223
+ #
224
+ def helpers(*modules, &block)
225
+ @context_helpers << block if block_given?
226
+ @context_helpers.push *modules
227
+ end
228
+
229
+ unless method_defined?(:define_singleton_method)
230
+ # Add define_singleton_method for Ruby 1.8.7
231
+ # This is used to define the sprocket accessor methods.
232
+ def define_singleton_method(symbol, method = nil, &block) # :nodoc:
233
+ singleton_class = class << self; self; end
234
+ singleton_class.__send__ :define_method, symbol, method || block
235
+ end
236
+ end
237
+
238
+ protected
239
+
240
+ # Factory method for creating a `Machined::Sprocket` instance.
241
+ # This is used in both `#append_sprocket` and `#prepend_sprocket`.
242
+ def create_sprocket(name, options = {}, &block) # :nodoc:
243
+ options.reverse_merge! :root => root
244
+ Sprocket.new(self, options).tap do |sprocket|
245
+ define_singleton_method(name) { sprocket }
246
+ yield sprocket if block_given?
247
+ end
248
+ end
249
+
250
+ # Appends the given +path+ to the given +sprocket+
251
+ # This makes sure the path is relative to the `root` path
252
+ # or an absolute path pointing somewhere else. It also
253
+ # checks if it exists before appending it.
254
+ def append_path(sprocket, path) # :nodoc:
255
+ path = root.join(path)
256
+ sprocket.append_path(path) if path.exist?
257
+ end
258
+
259
+ # Appends the given `Array` of +paths+ to the given +sprocket+.
260
+ def append_paths(sprocket, paths) # :nodoc:
261
+ paths or return
262
+ paths.each do |path|
263
+ append_path sprocket, path
264
+ end
265
+ end
266
+
267
+ # Returns the Javascript compression engine, based on
268
+ # what's set in `config.js_compressor`. If `config.js_compressor`
269
+ # is nil, let Tilt + Crush decide which one to use.
270
+ def js_compressor # :nodoc:
271
+ case config.js_compressor
272
+ when Crush::Engine
273
+ config.js_compressor
274
+ when Symbol, String
275
+ JS_COMPRESSORS[config.js_compressor.to_sym]
276
+ else
277
+ Crush.register_js
278
+ Tilt["js"]
279
+ end
280
+ end
281
+
282
+ # Returns the CSS compression engine, based on
283
+ # what's set in `config.css_compressor`. If `config.css_compressor`
284
+ # is nil, let Tilt + Crush decide which one to use.
285
+ def css_compressor # :nodoc:
286
+ case config.css_compressor
287
+ when Crush::Engine
288
+ config.css_compressor
289
+ when Symbol, String
290
+ CSS_COMPRESSORS[config.css_compressor.to_sym]
291
+ else
292
+ Crush.register_css
293
+ Tilt["css"]
294
+ end
295
+ end
296
+ end
297
+ end