massimo 0.4.6 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. data/VERSION +1 -1
  2. data/bin/massimo +5 -3
  3. data/lib/massimo/cli.rb +101 -0
  4. data/lib/massimo/config.rb +56 -0
  5. data/lib/massimo/helpers.rb +12 -19
  6. data/lib/massimo/javascript.rb +18 -19
  7. data/lib/massimo/page.rb +63 -70
  8. data/lib/massimo/resource.rb +92 -0
  9. data/lib/massimo/server.rb +27 -0
  10. data/lib/massimo/site.rb +64 -101
  11. data/lib/massimo/stylesheet.rb +13 -35
  12. data/lib/massimo/view.rb +5 -32
  13. data/lib/massimo/watcher.rb +52 -0
  14. data/lib/massimo.rb +22 -31
  15. metadata +172 -117
  16. data/.document +0 -5
  17. data/.gitignore +0 -25
  18. data/Gemfile +0 -13
  19. data/Rakefile +0 -62
  20. data/lib/massimo/command.rb +0 -243
  21. data/lib/massimo/resource/base.rb +0 -74
  22. data/lib/massimo/resource/collection.rb +0 -56
  23. data/lib/massimo/resource/processing.rb +0 -67
  24. data/lib/massimo/templates.rb +0 -22
  25. data/massimo.gemspec +0 -135
  26. data/test/assertions.rb +0 -8
  27. data/test/helper.rb +0 -64
  28. data/test/source/config.yml +0 -4
  29. data/test/source/helpers/test_helper.rb +0 -5
  30. data/test/source/javascripts/_plugin.js +0 -1
  31. data/test/source/javascripts/application.js +0 -3
  32. data/test/source/javascripts/lib.js +0 -1
  33. data/test/source/lib/site.rb +0 -5
  34. data/test/source/pages/_skipped_page.haml +0 -0
  35. data/test/source/pages/about_us.erb +0 -5
  36. data/test/source/pages/erb.erb +0 -5
  37. data/test/source/pages/erb_with_layout.erb +0 -4
  38. data/test/source/pages/feed.haml +0 -6
  39. data/test/source/pages/haml.haml +0 -5
  40. data/test/source/pages/html.html +0 -4
  41. data/test/source/pages/index.erb +0 -4
  42. data/test/source/pages/markdown.markdown +0 -5
  43. data/test/source/pages/posts/first-post.haml +0 -1
  44. data/test/source/pages/with_extension.haml +0 -4
  45. data/test/source/pages/with_meta_data.haml +0 -7
  46. data/test/source/pages/with_title.haml +0 -4
  47. data/test/source/pages/with_url.haml +0 -4
  48. data/test/source/pages/without_extension.haml +0 -0
  49. data/test/source/pages/without_meta_data.haml +0 -1
  50. data/test/source/pages/without_title.haml +0 -1
  51. data/test/source/pages/without_url.haml +0 -1
  52. data/test/source/stylesheets/_base.sass +0 -2
  53. data/test/source/stylesheets/application.sass +0 -4
  54. data/test/source/stylesheets/basic.css +0 -3
  55. data/test/source/stylesheets/less_file.less +0 -5
  56. data/test/source/views/layouts/application.haml +0 -2
  57. data/test/source/views/with_helper.haml +0 -1
  58. data/test/source/views/with_locals.haml +0 -1
  59. data/test/source/views/without_locals.haml +0 -1
  60. data/test/test_helpers.rb +0 -25
  61. data/test/test_javascript.rb +0 -30
  62. data/test/test_page.rb +0 -142
  63. data/test/test_resource.rb +0 -70
  64. data/test/test_site.rb +0 -125
  65. data/test/test_stylesheet.rb +0 -40
  66. data/test/test_view.rb +0 -50
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.4.6
1
+ 0.5.0
data/bin/massimo CHANGED
@@ -1,6 +1,8 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require File.expand_path("../../lib/massimo", __FILE__)
4
- require File.expand_path("../../lib/massimo/command", __FILE__)
3
+ $:.unshift File.expand_path('../../lib', __FILE__)
5
4
 
6
- exit Massimo::Command.new(ARGV).run!
5
+ require 'massimo'
6
+ require 'massimo/cli'
7
+
8
+ Massimo::CLI.start
@@ -0,0 +1,101 @@
1
+ require 'optparse'
2
+ require 'ostruct'
3
+ require 'thor'
4
+
5
+ module Massimo
6
+ class CLI < Thor
7
+ include Thor::Actions
8
+
9
+ default_task :build
10
+ class_option 'config', :desc => 'Path to the config file', :aliases => '-c'
11
+ class_option 'source_path', :desc => 'Path to the source dir', :aliases => '-s'
12
+ class_option 'output_path', :desc => 'Path to the output dir', :aliases => '-o'
13
+
14
+ desc 'build', 'Builds the site'
15
+ def build
16
+ site.process
17
+ say 'massimo has built your site'
18
+ end
19
+ map 'b' => :build
20
+
21
+ desc 'watch', 'Watches your files for changes and rebuilds'
22
+ def watch
23
+ begin
24
+ say 'massimo is watching your files for changes'
25
+ Massimo::Watcher.start(site)
26
+ rescue Interrupt
27
+ exit
28
+ end
29
+ end
30
+ map 'w' => :watch
31
+
32
+ desc 'server [PORT]', 'Runs a local web server and processes the site on save'
33
+ def server(port = 3000)
34
+ say "massimo is serving your site at http://localhost:#{port}"
35
+ Massimo::Server.start(site, port.to_i)
36
+ end
37
+ map 's' => :server
38
+
39
+ desc 'generate SITE_OR_RESOURCE [FILE]', 'Generates a new site. Optionally generates a resource file.'
40
+ method_option 'page_ext',
41
+ :desc => 'The extension used for generated Pages and Views',
42
+ :default => 'haml',
43
+ :aliases => '--page'
44
+ method_option 'javascript_ext',
45
+ :desc => 'The extension used for generated Javascripts',
46
+ :default => 'js',
47
+ :aliases => '--js'
48
+ method_option 'stylesheet_ext',
49
+ :desc => 'The extension used for generated Stylesheets',
50
+ :default => 'sass',
51
+ :aliases => '--css'
52
+ def generate(site_or_resource, file = nil)
53
+ require 'active_support/inflector'
54
+
55
+ if file
56
+ create_file File.join(site.config.path_for(resource.pluralize), file)
57
+ else
58
+ empty_directory site_or_resource
59
+ inside site_or_resource do
60
+ site.resources.each do |resource|
61
+ empty_directory(resource.path)
62
+ end
63
+ create_file File.join(Massimo::Page.path, "index.#{options[:page_ext]}")
64
+ create_file File.join(Massimo::Javascript.path, "main.#{options[:javascript_ext]}")
65
+ create_file File.join(Massimo::Stylesheet.path, "main.#{options[:stylesheet_ext]}")
66
+ create_file File.join(Massimo::View.path, "layouts/main.#{options[:page_ext]}")
67
+ empty_directory site.config.output_path
68
+ end
69
+ end
70
+ end
71
+ map 'g' => :generate
72
+
73
+ desc 'version', 'Displays current version'
74
+ def version
75
+ say Massimo::VERSION
76
+ end
77
+ map %w( -v --version ) => :version
78
+
79
+ protected
80
+
81
+ def site
82
+ @site ||= begin
83
+ site = Massimo::Site.new config_file(:yml)
84
+ if config_rb = config_file(:rb)
85
+ site.instance_eval File.read(config_rb)
86
+ end
87
+ site.config.source_path = options[:source_path] if options[:source_path]
88
+ site.config.output_path = options[:output_path] if options[:output_path]
89
+ site
90
+ end
91
+ end
92
+
93
+ def config_file(ext)
94
+ if options[:config] && File.extname(options[:config]) == ".#{ext}"
95
+ options[:config]
96
+ elsif File.exist?("config.#{ext}")
97
+ "config.#{ext}"
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,56 @@
1
+ require 'active_support/core_ext/hash/keys'
2
+ require 'ostruct'
3
+ require 'yaml'
4
+
5
+ module Massimo
6
+ class Config < OpenStruct
7
+ DEFAULT_OPTIONS = {
8
+ :source_path => '.',
9
+ :output_path => 'public',
10
+ :resources_path => '.',
11
+ :resources_url => '/',
12
+ :javascripts_url => '/javascripts',
13
+ :stylesheets_url => '/stylesheets',
14
+ :resources_url => '/'
15
+ }.freeze
16
+
17
+ def initialize(options = nil)
18
+ hash = DEFAULT_OPTIONS.dup
19
+
20
+ options = YAML.load_file(options) if options.is_a?(String)
21
+ hash.merge!(options.symbolize_keys) if options.is_a?(Hash)
22
+
23
+ super hash
24
+ end
25
+
26
+ def source_path
27
+ File.expand_path(super)
28
+ end
29
+
30
+ def output_path
31
+ File.expand_path(super)
32
+ end
33
+
34
+ def path_for(resource_name)
35
+ path_method = "#{resource_name}_path"
36
+ if resource_path = (respond_to?(path_method) and send(path_method))
37
+ File.expand_path(resource_path)
38
+ else
39
+ File.join(source_path, resource_name.to_s)
40
+ end
41
+ end
42
+
43
+ def url_for(resource_name)
44
+ url_method = "#{resource_name}_url"
45
+ if resource_url = (respond_to?(url_method) and send(url_method))
46
+ resource_url
47
+ else
48
+ resources_url
49
+ end
50
+ end
51
+
52
+ def files_in(resource_name, extension = '*')
53
+ Dir.glob(File.join(path_for(resource_name), "**/*.#{extension}"))
54
+ end
55
+ end
56
+ end
@@ -1,28 +1,21 @@
1
- begin require "sinatra_more"; rescue LoadError; end
1
+ require 'rack/utils' # needed for sinatra_more...
2
+ require 'sinatra_more/markup_plugin'
2
3
 
3
4
  module Massimo
4
- class Helpers
5
- if defined? SinatraMore
6
- include SinatraMore::OutputHelpers
7
- include SinatraMore::TagHelpers
8
- include SinatraMore::AssetTagHelpers
9
- include SinatraMore::FormHelpers
10
- include SinatraMore::FormatHelpers
11
- end
12
-
13
- #
14
- def initialize(modules = nil)
15
- extend(*modules) unless modules.nil? || modules.empty?
16
- end
5
+ module Helpers
6
+ include SinatraMore::OutputHelpers
7
+ include SinatraMore::TagHelpers
8
+ include SinatraMore::AssetTagHelpers
9
+ include SinatraMore::FormHelpers
10
+ include SinatraMore::FormatHelpers
17
11
 
18
- # Gets the site instance
19
12
  def site
20
- Massimo::Site()
13
+ Massimo.site
21
14
  end
22
15
 
23
- #
24
- def render(name, locals = {}, &block)
25
- site.render_view(name, locals, &block)
16
+ def render(view_name, locals = {})
17
+ view = Massimo::View.find(view_name)
18
+ view && view.render(locals)
26
19
  end
27
20
  end
28
21
  end
@@ -1,24 +1,23 @@
1
- require "jsmin"
2
- require "sprockets"
3
- require "massimo/resource/base"
4
-
5
1
  module Massimo
6
- class Javascript < Massimo::Resource::Base
7
- processable!
8
-
9
- # Concat the Javascript using Sprockets, then minify using JSmin
2
+ class Javascript < Massimo::Resource
10
3
  def render
11
- secretary = Sprockets::Secretary.new(
12
- :assert_root => site.output_dir,
13
- :source_files => [ @source_path.to_s ]
14
- )
15
- # install assets if necessary
16
- secretary.install_assets
17
-
18
- # Concatenate the scripts and minify if necessary
19
- output = secretary.concatenation.to_s
20
- output = JSMin.minify(output) if site.production? or site.options[:minify]
21
- output
4
+ case source_path.extname.to_s
5
+ when '.coffee'
6
+ require 'coffee-script' unless defined?(CoffeeScript)
7
+ CoffeeScript.compile(content)
8
+ else
9
+ require 'sprockets' unless defined?(Sprockets)
10
+ secretary = Sprockets::Secretary.new(
11
+ :assert_root => Massimo.config.output_path,
12
+ :source_files => [ source_path.to_s ]
13
+ )
14
+ secretary.install_assets
15
+ secretary.concatenation.to_s
16
+ end
17
+ end
18
+
19
+ def extension
20
+ @extension ||= '.js'
22
21
  end
23
22
  end
24
23
  end
data/lib/massimo/page.rb CHANGED
@@ -1,97 +1,90 @@
1
- require "active_support/inflector"
2
- require "pathname"
3
- require "yaml"
4
- require "massimo/view"
1
+ require 'active_support/core_ext/hash/keys'
2
+ require 'active_support/inflector'
3
+ require 'tilt'
4
+ require 'yaml'
5
+
6
+ Tilt.register 'html', Tilt::StringTemplate
5
7
 
6
8
  module Massimo
7
- class Page < Massimo::View
8
- META_SEP = %r/\A---\s*(?:\r\n|\n)?\z/ # :nodoc:
9
+ class Page < Resource
10
+ def render
11
+ template = Tilt.new(source_path.basename.to_s, @line || 1) { content }
12
+ meta_data = @meta_data.merge(self.class.resource_name.singularize.to_sym => self)
13
+ output = template.render(Massimo.site.template_scope, meta_data)
14
+ if found_layout = Massimo::View.find("layouts/#{layout}")
15
+ output = found_layout.render(:page => self) { output }
16
+ end
17
+ output
18
+ end
9
19
 
10
- processable!
20
+ def title
21
+ @meta_data[:title] ||= source_path.basename.to_s.chomp(source_path.extname.to_s).titleize
22
+ end
11
23
 
12
- # Creates a new page associated with the given file path.
13
- def initialize(source_path)
14
- super(source_path, {
15
- :title => File.basename(source_path).gsub(File.extname(source_path), "").titleize,
16
- :extension => ".html",
17
- :url => source_path.gsub(self.class.dir, "").gsub(File.extname(source_path), "").dasherize,
18
- :layout => "application"
19
- })
24
+ def extension
25
+ @meta_data[:extension] ||= '.html'
20
26
  end
21
27
 
22
- # Override render to wrap the result in the layout
23
- def render(use_layout = true)
24
- output = super()
25
- if use_layout and found_layout = find_layout
26
- output = found_layout.render(:page => self) { output }
28
+ def url
29
+ @meta_data[:url] ||= begin
30
+ url = super
31
+ url.chomp!('index.html')
32
+ url.sub!(/\.html$/, '/')
33
+ url
27
34
  end
28
- output
29
35
  end
30
36
 
31
- # Override to_s so that the layout can include the page with `<%= page %>`
32
- def to_s
33
- render(false)
37
+ def layout
38
+ @meta_data[:layout] = 'main' if @meta_data[:layout].nil?
39
+ @meta_data[:layout]
40
+ end
41
+
42
+ def output_path
43
+ @output_path ||= begin
44
+ output_path = super.to_s
45
+ output_path << "index.html" if output_path =~ /\/$/
46
+ Pathname.new output_path
47
+ end
34
48
  end
35
49
 
36
50
  protected
37
51
 
38
- # Reads the source page file, and populates the `@meta_data` and
39
- # `@body` attributes.
40
- def read_source!
41
- # read the source file and setup some values for the loop
42
- source = super()
52
+ def read_source
43
53
  @line = nil
54
+ @content = ''
44
55
  front_matter = false
45
- meta_data = ""
46
- body = ""
56
+ meta_data = ''
47
57
 
48
- # Loop through source to get meta data
49
- # and the correct line number for the body
50
- source.each_with_index do |line, line_num|
51
- if line =~ META_SEP
52
- front_matter = !front_matter
53
- else
54
- if front_matter
55
- meta_data << line
58
+ source_path.open do |file|
59
+ file.each do |line|
60
+ if line =~ /\A---\s*\Z/
61
+ front_matter = !front_matter
56
62
  else
57
- @line ||= line_num
58
- body << line
63
+ if front_matter
64
+ meta_data << line
65
+ else
66
+ @line ||= file.lineno
67
+ @content << line
68
+ end
59
69
  end
60
70
  end
61
71
  end
62
72
 
63
- # finally get the meta_data as a hash and set the body
64
- meta_data = YAML.load(meta_data)
65
- @meta_data.merge!(meta_data.symbolize_keys) if meta_data
66
- @body = body
73
+ @meta_data = (YAML.load(meta_data) || {}).symbolize_keys
67
74
  end
68
75
 
69
- # Determine the output file path
70
- def output_path
71
- @output_path ||= begin
72
- path = site.output_dir(url)
73
- path << if index? or not html?
74
- extension unless path.match(/#{extension}$/)
75
- else
76
- "/index.html" unless path.match(/\/index\.html$/)
77
- end
78
- Pathname.new(path)
76
+ def method_missing(method, *args, &block)
77
+ if args.length == 0
78
+ read_source
79
+ method_name = method.to_s
80
+ if method_name.chomp! '?'
81
+ !!@meta_data[method_name.to_sym]
82
+ else
83
+ @meta_data[method_name.to_sym]
84
+ end
85
+ else
86
+ super
79
87
  end
80
88
  end
81
-
82
- # Determines if this is an index page
83
- def index?
84
- @source_path.basename.to_s =~ /^index/
85
- end
86
-
87
- # Determines if this an HTML page.
88
- def html?
89
- extension =~ /(html|php)$/
90
- end
91
-
92
- # Finds the Layout View if it exists
93
- def find_layout
94
- site.find_view("layouts/#{layout}") unless layout == false
95
- end
96
89
  end
97
90
  end
@@ -0,0 +1,92 @@
1
+ require 'active_support/inflector'
2
+ require 'fileutils'
3
+ require 'pathname'
4
+
5
+ module Massimo
6
+ class Resource
7
+ class << self
8
+ def resource_name
9
+ self.name.underscore.sub(/.*\//, '').pluralize
10
+ end
11
+
12
+ def path
13
+ Massimo.config.path_for resource_name
14
+ end
15
+
16
+ def url
17
+ Massimo.config.url_for resource_name
18
+ end
19
+
20
+ def find(name)
21
+ resource_path = Dir.glob(File.join(path, "#{name}.*")).first
22
+ resource_path && self.new(resource_path)
23
+ end
24
+
25
+ def all
26
+ files = Massimo.config.files_in resource_name
27
+ files.reject! { |file| File.basename(file) =~ /^_/ }
28
+ files.map { |file| self.new(file) }
29
+ end
30
+
31
+ def processable?
32
+ true
33
+ end
34
+
35
+ protected
36
+
37
+ def unprocessable
38
+ def self.processable?; false; end
39
+ define_method(:process) { false }
40
+ end
41
+ end
42
+
43
+ attr_reader :source_path, :content
44
+
45
+ def initialize(source)
46
+ @source_path = source.is_a?(Pathname) ? source.expand_path : Pathname.new(source).expand_path
47
+ read_source
48
+ end
49
+
50
+ def filename
51
+ @filename ||= source_path.basename.to_s
52
+ end
53
+
54
+ def extension
55
+ @extension ||= source_path.extname
56
+ end
57
+
58
+ def url
59
+ @url ||= begin
60
+ url = source_path.to_s.sub(/^#{Regexp.escape(self.class.path)}/, '')
61
+ url = url.sub(/\.[^\.]+$/, extension)
62
+ url = File.join(self.class.url, url) unless url.include? self.class.url
63
+ url = url.dasherize
64
+ url
65
+ end
66
+ end
67
+
68
+ # The path to the output file.
69
+ def output_path
70
+ @output_path ||= Pathname.new File.join(Massimo.config.output_path, url)
71
+ end
72
+
73
+ # Runs the content through any necessary filters, templates, etc.
74
+ def render
75
+ content
76
+ end
77
+
78
+ # Writes the rendered content to the output file.
79
+ def process
80
+ FileUtils.mkdir_p(output_path.dirname)
81
+ output_path.open('w') do |f|
82
+ f.write render
83
+ end
84
+ end
85
+
86
+ protected
87
+
88
+ def read_source
89
+ @content = source_path.read
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,27 @@
1
+ require 'rack'
2
+
3
+ module Massimo
4
+ class Server
5
+ class << self
6
+ def start(site, port = 3000)
7
+ app = Rack::Builder.new do
8
+ use Rack::ShowExceptions
9
+ run Massimo::Server.new(site)
10
+ end
11
+ Rack::Handler::WEBrick.run(app, :Port => port)
12
+ end
13
+ end
14
+
15
+ def initialize(site)
16
+ @site = site
17
+ @file_server = Rack::File.new(site.config.output_path)
18
+ @watcher = Massimo::Watcher.new(site)
19
+ end
20
+
21
+ def call(env)
22
+ @watcher.process
23
+ env['PATH_INFO'] << 'index.html' if env['PATH_INFO'] =~ /\/$/
24
+ @file_server.call(env)
25
+ end
26
+ end
27
+ end