suite 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +18 -0
- data/Gemfile +4 -0
- data/README.md +304 -0
- data/Rakefile +2 -0
- data/bin/suite +3 -0
- data/lib/suite/builder.rb +139 -0
- data/lib/suite/cli.rb +52 -0
- data/lib/suite/environment.rb +22 -0
- data/lib/suite/generators/project/application.js.erb +10 -0
- data/lib/suite/generators/project/application.scss.erb +5 -0
- data/lib/suite/generators/project/content.yml +16 -0
- data/lib/suite/generators/project/core.js.coffee +4 -0
- data/lib/suite/generators/project/gitignore +1 -0
- data/lib/suite/generators/project/homepage.html.haml +6 -0
- data/lib/suite/generators/project/info.html.haml +8 -0
- data/lib/suite/generators/project/suite.yml.erb +6 -0
- data/lib/suite/generators/project/template.html.haml.erb +44 -0
- data/lib/suite/generators/project/text.txt +1 -0
- data/lib/suite/generators/project.rb +69 -0
- data/lib/suite/helpers/image_view_helper.rb +14 -0
- data/lib/suite/helpers/javascript_view_helper.rb +23 -0
- data/lib/suite/helpers/stylesheet_view_helper.rb +13 -0
- data/lib/suite/helpers/view_helpers.rb +19 -0
- data/lib/suite/project/asset_registry.rb +62 -0
- data/lib/suite/project.rb +76 -0
- data/lib/suite/renderers/abstract.rb +21 -0
- data/lib/suite/renderers/exception.rb +17 -0
- data/lib/suite/renderers/haml.rb +12 -0
- data/lib/suite/renderers/html.rb +11 -0
- data/lib/suite/renderers/page.rb +59 -0
- data/lib/suite/renderers.rb +11 -0
- data/lib/suite/server/asset.rb +17 -0
- data/lib/suite/server/content.rb +18 -0
- data/lib/suite/server/error.rb +7 -0
- data/lib/suite/server/root_asset.rb +4 -0
- data/lib/suite/server.rb +16 -0
- data/lib/suite/version.rb +3 -0
- data/lib/suite.rb +33 -0
- data/suite.gemspec +30 -0
- metadata +196 -0
@@ -0,0 +1,69 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'thor/group'
|
3
|
+
module Suite
|
4
|
+
module Generators
|
5
|
+
class Project < Thor::Group
|
6
|
+
include Thor::Actions
|
7
|
+
argument :name, type: :string
|
8
|
+
|
9
|
+
def create_config
|
10
|
+
say "I’m going to ask you some questions to create your config file", :cyan
|
11
|
+
say "You can change these values in your config/suite.yml file", :cyan
|
12
|
+
@compress_javascript = yes? "Compress JavaScript? [Yn]"
|
13
|
+
@shorten_javascript_variables = yes? "Shorten JavaScript Variables? [Yn]"
|
14
|
+
@compress_stylesheet = yes? "Compress CSS? [Yn]"
|
15
|
+
@using_cdn = yes? "Will you serve assets from a CDN?"
|
16
|
+
if @using_cdn
|
17
|
+
@asset_host = ask("CDN Host and path [e.g. http://ak43nam.cloudfront.net/new_site]").gsub(/\/$/,'')
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def create_directory_structure
|
22
|
+
empty_directory
|
23
|
+
empty_directory "build"
|
24
|
+
empty_directory "config"
|
25
|
+
empty_directory "content"
|
26
|
+
empty_directory "content/layouts"
|
27
|
+
empty_directory "assets"
|
28
|
+
empty_directory "assets/images"
|
29
|
+
empty_directory "assets/javascripts"
|
30
|
+
empty_directory "assets/stylesheets"
|
31
|
+
end
|
32
|
+
|
33
|
+
def copy_files
|
34
|
+
template "application.js.erb", "#{name}/assets/javascripts/application.js"
|
35
|
+
template "core.js.coffee", "#{name}/assets/javascripts/core.js.coffee"
|
36
|
+
|
37
|
+
template "application.scss.erb", "#{name}/assets/stylesheets/#{name}.css.scss"
|
38
|
+
|
39
|
+
template "suite.yml.erb", "#{name}/config/suite.yml"
|
40
|
+
template "content.yml", "#{name}/config/content.yml"
|
41
|
+
|
42
|
+
template "template.html.haml.erb", "#{name}/content/layouts/application.html.haml"
|
43
|
+
template "homepage.html.haml", "#{name}/content/homepage.html.haml"
|
44
|
+
template "info.html.haml", "#{name}/content/info.html.haml"
|
45
|
+
end
|
46
|
+
|
47
|
+
def git
|
48
|
+
if yes? "Create git repo? [Yn]"
|
49
|
+
shell.mute do
|
50
|
+
inside name do
|
51
|
+
run 'git init'
|
52
|
+
end
|
53
|
+
template "gitignore", "#{name}/.gitignore"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def self.source_root
|
61
|
+
File.dirname(__FILE__) + "/project"
|
62
|
+
end
|
63
|
+
|
64
|
+
def empty_directory folder_name = nil
|
65
|
+
super "#{name}/#{folder_name}"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Suite::Helpers
|
2
|
+
module ImageViewHelper
|
3
|
+
def image_tag src, attributes = {}
|
4
|
+
attrs = []
|
5
|
+
attributes.each_pair do |k,v|
|
6
|
+
final_value = v.is_a?(Array) ? v.join(" ") : v
|
7
|
+
final_value = ERB::Util.html_escape(final_value)
|
8
|
+
attrs << %(#{k}="#{final_value}")
|
9
|
+
end
|
10
|
+
|
11
|
+
"<img src=\"#{Suite.project.asset_path}/images/#{ERB::Util.html_escape src}\" #{attrs.sort * ' '} />"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Suite::Helpers
|
2
|
+
module JavascriptViewHelper
|
3
|
+
def javascript_include_tag path
|
4
|
+
path = path + ".js" unless path =~ /\.js/
|
5
|
+
if Suite.env.development?
|
6
|
+
javascript_include_tags_for_asset(path)
|
7
|
+
else
|
8
|
+
javascript_script_tag "#{Suite.project.asset_path}/javascripts/#{Suite.project.asset_registry.add_asset :js, path}.js"
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def javascript_include_tags_for_asset path
|
13
|
+
assets = Suite.project.asset path
|
14
|
+
assets.to_a.map do |asset|
|
15
|
+
javascript_script_tag clean_asset_path asset.pathname.to_s
|
16
|
+
end.join("\n")
|
17
|
+
end
|
18
|
+
|
19
|
+
def javascript_script_tag path
|
20
|
+
"<script type=\"text/javascript\" src=\"#{path}\"></script>"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Suite::Helpers
|
2
|
+
module StylesheetViewHelper
|
3
|
+
|
4
|
+
def stylesheet_link_tag path, media = :all
|
5
|
+
path = path + ".css" unless path =~ /\.css/
|
6
|
+
if Suite.env.build?
|
7
|
+
path = Suite.project.asset_registry.add_asset(:css, path) + ".css"
|
8
|
+
end
|
9
|
+
"<link rel=\"stylesheet\" href=\"#{Suite.project.asset_path}/stylesheets/#{path}\" media=\"#{media}\" />"
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'suite/project/asset_registry'
|
2
|
+
require 'suite/helpers/image_view_helper'
|
3
|
+
require 'suite/helpers/javascript_view_helper'
|
4
|
+
require 'suite/helpers/stylesheet_view_helper'
|
5
|
+
|
6
|
+
module Suite
|
7
|
+
module Helpers
|
8
|
+
module ViewHelpers
|
9
|
+
include ImageViewHelper
|
10
|
+
include JavascriptViewHelper
|
11
|
+
include StylesheetViewHelper
|
12
|
+
|
13
|
+
def clean_asset_path path
|
14
|
+
path.sub(/\.coffee|\.scss|\.sass/,'').sub(Suite.project.path,'')
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'digest/md5'
|
2
|
+
module Suite
|
3
|
+
class AssetRegistry
|
4
|
+
|
5
|
+
attr_accessor :assets
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@assets = { js: {}, css: {} }
|
9
|
+
end
|
10
|
+
|
11
|
+
def add_asset type, path
|
12
|
+
@assets[type][path] = Suite::Asset.new(path, type) unless @assets[type][path]
|
13
|
+
@assets[type][path].identifier
|
14
|
+
end
|
15
|
+
|
16
|
+
def js
|
17
|
+
@assets[:js]
|
18
|
+
end
|
19
|
+
|
20
|
+
def css
|
21
|
+
@assets[:css]
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
class Asset
|
26
|
+
attr_accessor :path, :type
|
27
|
+
def initialize path, type
|
28
|
+
@path, @type = path, type
|
29
|
+
end
|
30
|
+
|
31
|
+
def identifier
|
32
|
+
Digest::MD5.hexdigest to_s
|
33
|
+
end
|
34
|
+
|
35
|
+
def build_file_name
|
36
|
+
"#{identifier}.#{type.downcase}"
|
37
|
+
end
|
38
|
+
|
39
|
+
def to_s
|
40
|
+
@_memoized_to_s ||= compress Suite.project.asset(@path).to_s
|
41
|
+
end
|
42
|
+
|
43
|
+
def compress content
|
44
|
+
case type
|
45
|
+
when :js
|
46
|
+
if Suite.project.config["compress_javascript"]
|
47
|
+
YUI::JavaScriptCompressor.new(:munge => Suite.project.config["shorten_javascript_variables"]).compress content
|
48
|
+
else
|
49
|
+
content
|
50
|
+
end
|
51
|
+
when :css
|
52
|
+
if Suite.project.config["compress_css"]
|
53
|
+
YUI::CssCompressor.new.compress content
|
54
|
+
else
|
55
|
+
content
|
56
|
+
end
|
57
|
+
else
|
58
|
+
raise "Could not find YUI compressor for '#{type}'"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'suite/renderers'
|
2
|
+
require 'sprockets'
|
3
|
+
module Suite
|
4
|
+
class Project
|
5
|
+
attr_accessor :path, :config, :content, :view
|
6
|
+
def initialize path, config, content, view
|
7
|
+
@path, @config, @content, @view = path, config, content, view
|
8
|
+
end
|
9
|
+
|
10
|
+
def config
|
11
|
+
@config || {}
|
12
|
+
end
|
13
|
+
|
14
|
+
# Retrieves the content hash for the current view
|
15
|
+
def content
|
16
|
+
@content[view.to_s] || raise("No view '#{view}' defined")
|
17
|
+
end
|
18
|
+
|
19
|
+
def pages
|
20
|
+
content["pages"]
|
21
|
+
end
|
22
|
+
|
23
|
+
def include? slugs
|
24
|
+
return !!content_at_slugs(slugs)
|
25
|
+
end
|
26
|
+
|
27
|
+
def page_at_slugs slugs
|
28
|
+
content_level = pages
|
29
|
+
slugs.each do |slug|
|
30
|
+
return false unless content_level = content_level[slug.to_s]
|
31
|
+
end
|
32
|
+
return {
|
33
|
+
layout: content_level["layout"] ? content_level["layout"] : content["layout"],
|
34
|
+
content: content_level["content"]
|
35
|
+
}
|
36
|
+
end
|
37
|
+
|
38
|
+
def file_types
|
39
|
+
config["file_types"] || [:html]
|
40
|
+
end
|
41
|
+
|
42
|
+
def asset path
|
43
|
+
sprocket_environment[path.sub(/javascripts\/|stylesheets\/|images\//,'')]
|
44
|
+
end
|
45
|
+
|
46
|
+
def source_asset_path
|
47
|
+
path + "/assets"
|
48
|
+
end
|
49
|
+
|
50
|
+
def asset_path
|
51
|
+
Suite.env.build? && config["asset_host"] ? config["asset_host"].gsub(/\/$/,'') : "/assets"
|
52
|
+
end
|
53
|
+
|
54
|
+
def asset_registry
|
55
|
+
@_memoized_asset_registry ||= Suite::AssetRegistry.new
|
56
|
+
end
|
57
|
+
|
58
|
+
def sprocket_environment
|
59
|
+
@_memorized_sprocket_environment ||= begin
|
60
|
+
environment = Sprockets::Environment.new
|
61
|
+
environment.append_path source_asset_path + '/images'
|
62
|
+
environment.append_path source_asset_path + '/javascripts'
|
63
|
+
environment.append_path source_asset_path + '/stylesheets'
|
64
|
+
environment
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# TODO: Puts a renderers config in the yaml to add new renderers
|
69
|
+
def renderers
|
70
|
+
@_memoized_render_types ||= begin
|
71
|
+
Suite::Renderers.default_renderers
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'suite/helpers/view_helpers'
|
2
|
+
|
3
|
+
module Suite::Renderers
|
4
|
+
class Abstract
|
5
|
+
include Suite::Helpers::ViewHelpers
|
6
|
+
|
7
|
+
def self.render_file path, &block
|
8
|
+
new(IO.read path).render &block
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.render content
|
12
|
+
new(content).render
|
13
|
+
end
|
14
|
+
|
15
|
+
attr_accessor :content
|
16
|
+
def initialize content
|
17
|
+
@content = content
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Suite
|
2
|
+
module Renderers
|
3
|
+
class Exception
|
4
|
+
attr_accessor :message
|
5
|
+
def initialize message
|
6
|
+
@message = message
|
7
|
+
if Suite.env.build?
|
8
|
+
raise message.gsub(/<([^>]*)>/,'"')
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_s
|
13
|
+
"<div style='position:fixed; top:0; left:0; width: 100%; font-family: sans-serif; text-align:center; font-size:24px; padding:25px; background-color:#FFF3C2;'>#{message}</div>"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'suite/renderers/haml'
|
2
|
+
require 'suite/renderers/html'
|
3
|
+
require 'suite/renderers/exception'
|
4
|
+
|
5
|
+
module Suite::Renderers
|
6
|
+
class Page
|
7
|
+
attr_accessor :layout, :partials, :contents
|
8
|
+
|
9
|
+
def initialize layout, partials
|
10
|
+
@layout, @partials, @contents = layout, partials, {}
|
11
|
+
end
|
12
|
+
|
13
|
+
def render
|
14
|
+
render_layout(partials.map do |partial|
|
15
|
+
render_partial partial
|
16
|
+
end.join("\n"))
|
17
|
+
end
|
18
|
+
|
19
|
+
def render_layout body
|
20
|
+
return body unless layout
|
21
|
+
Suite::Renderers::HAML.render_file(layout_path) do |location|
|
22
|
+
if location
|
23
|
+
@contents[location]
|
24
|
+
else
|
25
|
+
body
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def render_partial partial
|
31
|
+
root = Suite.project.path + "/content/" + partial
|
32
|
+
output = nil
|
33
|
+
Suite.project.file_types.each do |file_type|
|
34
|
+
Suite.project.renderers.each do |extension, renderer|
|
35
|
+
path = root + "." + file_type.to_s + "." + extension.to_s
|
36
|
+
if File.exists?(path)
|
37
|
+
output = renderer.render_file(path)
|
38
|
+
break
|
39
|
+
end
|
40
|
+
end
|
41
|
+
break if output
|
42
|
+
path = root + ".html"
|
43
|
+
if File.exists?(path)
|
44
|
+
output = Suite::Renderers::HTML.render_file(path, @contents)
|
45
|
+
break
|
46
|
+
end
|
47
|
+
end
|
48
|
+
output ? output : missing_partial(partial)
|
49
|
+
end
|
50
|
+
|
51
|
+
def layout_path
|
52
|
+
Suite.project.path + "/content/" + layout + ".html.haml"
|
53
|
+
end
|
54
|
+
|
55
|
+
def missing_partial partial
|
56
|
+
Suite::Renderers::Exception.new "Could not find partial <strong>#{partial}</strong>"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'suite/server/error'
|
2
|
+
require 'mime/types'
|
3
|
+
|
4
|
+
module Suite
|
5
|
+
class AssetServer < Goliath::API
|
6
|
+
include Suite::ServerError
|
7
|
+
def response(env)
|
8
|
+
asset = Suite.project.asset env.params[:asset].join("/")
|
9
|
+
return not_found unless asset
|
10
|
+
[
|
11
|
+
200,
|
12
|
+
{'content-type'=> MIME::Types.type_for(env.params[:asset].last).first.content_type },
|
13
|
+
asset.body
|
14
|
+
]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'suite/server/error'
|
2
|
+
require 'suite/renderers/page'
|
3
|
+
|
4
|
+
module Suite
|
5
|
+
class ContentServer < Goliath::API
|
6
|
+
include Suite::ServerError
|
7
|
+
def response env
|
8
|
+
return not_found unless page = Suite.project.page_at_slugs(slugs)
|
9
|
+
renderer = Suite::Renderers::Page.new page[:layout], page[:content]
|
10
|
+
[200,{},renderer.render]
|
11
|
+
end
|
12
|
+
|
13
|
+
# Returns the array of slugs, or index if no slug
|
14
|
+
def slugs
|
15
|
+
self.env.params[:content].map{ |slug| slug == "" ? "index" : slug }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/suite/server.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'goliath/api'
|
2
|
+
require 'goliath/runner'
|
3
|
+
require 'suite/server/asset'
|
4
|
+
require 'suite/server/root_asset'
|
5
|
+
require 'suite/server/content'
|
6
|
+
|
7
|
+
module Suite
|
8
|
+
class Server < Goliath::API
|
9
|
+
map "/assets/*asset", Suite::AssetServer
|
10
|
+
map "/*content", Suite::ContentServer
|
11
|
+
|
12
|
+
%w[favicon.ico].each do |resource|
|
13
|
+
map "/#{resource}", Suite::RootAssetServer
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/suite.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'suite/project'
|
2
|
+
require 'suite/environment'
|
3
|
+
require "suite/version"
|
4
|
+
require "suite/cli"
|
5
|
+
require 'yaml'
|
6
|
+
|
7
|
+
module Suite
|
8
|
+
class << self
|
9
|
+
def project= project
|
10
|
+
@@project = project
|
11
|
+
end
|
12
|
+
|
13
|
+
def project
|
14
|
+
@@project
|
15
|
+
end
|
16
|
+
|
17
|
+
def env= env
|
18
|
+
@@env = env
|
19
|
+
end
|
20
|
+
|
21
|
+
def env
|
22
|
+
@@env ||= Suite::Environment.new(ENV["SUITE_ENV"] || :development)
|
23
|
+
end
|
24
|
+
|
25
|
+
def use_project_at_path path, view = :desktop
|
26
|
+
self.project = Suite::Project.new \
|
27
|
+
path,
|
28
|
+
YAML.load(File.open(path + "/config/suite.yml")),
|
29
|
+
YAML.load(File.open(path + "/config/content.yml")),
|
30
|
+
view
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/suite.gemspec
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/suite/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Mal Curtis"]
|
6
|
+
gem.email = ["mal@mal.co.nz"]
|
7
|
+
gem.description = %q{All the stuff that make front end web development fun, in one simple toolkit}
|
8
|
+
gem.summary = gem.description
|
9
|
+
gem.homepage = ""
|
10
|
+
|
11
|
+
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
12
|
+
gem.files = `git ls-files`.split("\n")
|
13
|
+
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
14
|
+
gem.name = "suite"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = Suite::VERSION
|
17
|
+
|
18
|
+
|
19
|
+
gem.add_development_dependency "rspec", "~> 2.6"
|
20
|
+
gem.add_dependency "thor"
|
21
|
+
gem.add_dependency "goliath"
|
22
|
+
gem.add_dependency "mime-types"
|
23
|
+
gem.add_dependency "haml"
|
24
|
+
gem.add_dependency "sprockets"
|
25
|
+
gem.add_dependency "sprockets-sass"
|
26
|
+
gem.add_dependency "coffee-script"
|
27
|
+
gem.add_dependency "compass"
|
28
|
+
gem.add_dependency "yui-compressor"
|
29
|
+
|
30
|
+
end
|