suite 0.1.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 (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