suite 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. data/.gitignore +18 -0
  2. data/Gemfile +4 -0
  3. data/README.md +304 -0
  4. data/Rakefile +2 -0
  5. data/bin/suite +3 -0
  6. data/lib/suite/builder.rb +139 -0
  7. data/lib/suite/cli.rb +52 -0
  8. data/lib/suite/environment.rb +22 -0
  9. data/lib/suite/generators/project/application.js.erb +10 -0
  10. data/lib/suite/generators/project/application.scss.erb +5 -0
  11. data/lib/suite/generators/project/content.yml +16 -0
  12. data/lib/suite/generators/project/core.js.coffee +4 -0
  13. data/lib/suite/generators/project/gitignore +1 -0
  14. data/lib/suite/generators/project/homepage.html.haml +6 -0
  15. data/lib/suite/generators/project/info.html.haml +8 -0
  16. data/lib/suite/generators/project/suite.yml.erb +6 -0
  17. data/lib/suite/generators/project/template.html.haml.erb +44 -0
  18. data/lib/suite/generators/project/text.txt +1 -0
  19. data/lib/suite/generators/project.rb +69 -0
  20. data/lib/suite/helpers/image_view_helper.rb +14 -0
  21. data/lib/suite/helpers/javascript_view_helper.rb +23 -0
  22. data/lib/suite/helpers/stylesheet_view_helper.rb +13 -0
  23. data/lib/suite/helpers/view_helpers.rb +19 -0
  24. data/lib/suite/project/asset_registry.rb +62 -0
  25. data/lib/suite/project.rb +76 -0
  26. data/lib/suite/renderers/abstract.rb +21 -0
  27. data/lib/suite/renderers/exception.rb +17 -0
  28. data/lib/suite/renderers/haml.rb +12 -0
  29. data/lib/suite/renderers/html.rb +11 -0
  30. data/lib/suite/renderers/page.rb +59 -0
  31. data/lib/suite/renderers.rb +11 -0
  32. data/lib/suite/server/asset.rb +17 -0
  33. data/lib/suite/server/content.rb +18 -0
  34. data/lib/suite/server/error.rb +7 -0
  35. data/lib/suite/server/root_asset.rb +4 -0
  36. data/lib/suite/server.rb +16 -0
  37. data/lib/suite/version.rb +3 -0
  38. data/lib/suite.rb +33 -0
  39. data/suite.gemspec +30 -0
  40. 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,12 @@
1
+ require 'suite/renderers/abstract'
2
+ require 'haml'
3
+
4
+ module Suite::Renderers
5
+ class HAML < Abstract
6
+
7
+ def render &block
8
+ Haml::Engine.new(@content).render(self, &block)
9
+ end
10
+
11
+ end
12
+ end
@@ -0,0 +1,11 @@
1
+ require 'suite/renderers/abstract'
2
+
3
+ module Suite::Renderers
4
+ class HTML < Abstract
5
+
6
+ def render
7
+ @content
8
+ end
9
+
10
+ end
11
+ 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,11 @@
1
+ module Suite
2
+ module Renderers
3
+ class << self
4
+ def default_renderers
5
+ {
6
+ haml: Suite::Renderers::HAML
7
+ }
8
+ end
9
+ end
10
+ end
11
+ 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
@@ -0,0 +1,7 @@
1
+ module Suite
2
+ module ServerError
3
+ def not_found
4
+ [404, {}, "Resource not found"]
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,4 @@
1
+ module Suite
2
+ class RootAssetServer < Goliath::API
3
+ end
4
+ end
@@ -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
@@ -0,0 +1,3 @@
1
+ module Suite
2
+ VERSION = "0.1.0"
3
+ 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