massimo 0.4.6 → 0.5.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.
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