tres 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.
- data/.gitignore +3 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +67 -0
- data/README.md +50 -0
- data/Rakefile +16 -0
- data/VERSION +1 -0
- data/bin/tres +103 -0
- data/examples/anagen/anagen.coffee +61 -0
- data/examples/anagen/anagen.css +1413 -0
- data/examples/anagen/anagen.js +182 -0
- data/examples/anagen/anagen.scss +20 -0
- data/examples/anagen/backbone-min.js +38 -0
- data/examples/anagen/index.html +50 -0
- data/examples/anagen/jquery-1.8.0.min.js +2 -0
- data/examples/anagen/templates.coffee +6 -0
- data/examples/anagen/templates.js +13 -0
- data/examples/anagen/tres.js +375 -0
- data/examples/anagen/underscore-min.js +32 -0
- data/font/fontawesome-webfont.eot +0 -0
- data/font/fontawesome-webfont.svg +255 -0
- data/font/fontawesome-webfont.ttf +0 -0
- data/font/fontawesome-webfont.woff +0 -0
- data/javascripts/backbone-min.js +38 -0
- data/javascripts/jquery-1.8.0.min.js +2 -0
- data/javascripts/tres.coffee +210 -0
- data/javascripts/underscore-min.js +32 -0
- data/lib/ext/.filemethods.rb.swp +0 -0
- data/lib/ext/filemethods.rb +109 -0
- data/lib/ext/string.rb +33 -0
- data/lib/tres/app.rb +92 -0
- data/lib/tres/asset_packager.rb +45 -0
- data/lib/tres/errors.rb +6 -0
- data/lib/tres/logger.rb +9 -0
- data/lib/tres/rack_logger.rb +37 -0
- data/lib/tres/server.rb +50 -0
- data/lib/tres/template_compiler.rb +113 -0
- data/lib/tres.rb +64 -0
- data/sass/font-awesome.scss +329 -0
- data/sass/tres/base.scss +131 -0
- data/sass/tres/themes/default.scss +71 -0
- data/spec/app_spec.rb +44 -0
- data/spec/asset_packager_spec.rb +74 -0
- data/spec/filemethods_spec.rb +84 -0
- data/spec/fixtures/index.haml +41 -0
- data/spec/fixtures/index.html +50 -0
- data/spec/sample/assets/javascripts/all.coffee +12 -0
- data/spec/sample/assets/javascripts/anagen.coffee +7 -0
- data/spec/sample/assets/javascripts/backbone-min.js +38 -0
- data/spec/sample/assets/javascripts/jquery-1.8.0.min.js +2 -0
- data/spec/sample/assets/javascripts/underscore-min.js +32 -0
- data/spec/sample/assets/stylesheets/app.scss +7 -0
- data/spec/sample/assets/stylesheets/with_imports.scss +10 -0
- data/spec/sample/templates/article.html +0 -0
- data/spec/sample/templates/book.haml +4 -0
- data/spec/sample/templates/books/li.haml +2 -0
- data/spec/sample/templates/index.html +50 -0
- data/spec/server_spec.rb +35 -0
- data/spec/spec_helper.rb +69 -0
- data/spec/template_compiler_spec.rb +96 -0
- data/templates/all.coffee +21 -0
- data/templates/all.scss +11 -0
- data/templates/app.coffee +10 -0
- data/templates/collection_script.coffee.erb +6 -0
- data/templates/config.ru +3 -0
- data/templates/home.coffee +7 -0
- data/templates/home.haml +17 -0
- data/templates/index.html +15 -0
- data/templates/model_script.coffee.erb +3 -0
- data/templates/screen_script.coffee.erb +5 -0
- data/templates/templates.js +2 -0
- data/tres.gemspec +29 -0
- metadata +257 -0
data/lib/ext/string.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
class String;
|
2
|
+
def /(s)
|
3
|
+
File.join(self, s)
|
4
|
+
end
|
5
|
+
|
6
|
+
def blank?
|
7
|
+
self == ""
|
8
|
+
end
|
9
|
+
|
10
|
+
# Taken from Rails
|
11
|
+
def classify
|
12
|
+
string = self.underscore
|
13
|
+
string = string.sub(/^[a-z\d]*/) { $&.capitalize }
|
14
|
+
string.gsub(/(?:_|(\/))([a-z\d]*)/i) { $2.capitalize }.gsub('/', '::')
|
15
|
+
end
|
16
|
+
|
17
|
+
def underscore
|
18
|
+
word = self.dup
|
19
|
+
word.gsub!(/([A-Z\d]+)([A-Z][a-z])/,'\1_\2')
|
20
|
+
word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
|
21
|
+
word.tr!("-", "_")
|
22
|
+
word.downcase!
|
23
|
+
word
|
24
|
+
end
|
25
|
+
|
26
|
+
def dasherize
|
27
|
+
self.underscore.gsub(/_/, '-')
|
28
|
+
end
|
29
|
+
|
30
|
+
def to_screen_name
|
31
|
+
self.classify.sub(/(screen)*$/i, 'Screen')
|
32
|
+
end
|
33
|
+
end
|
data/lib/tres/app.rb
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'tres/asset_packager'
|
2
|
+
require 'tres/template_compiler'
|
3
|
+
require 'listen'
|
4
|
+
require 'ostruct'
|
5
|
+
|
6
|
+
module Tres
|
7
|
+
class App
|
8
|
+
attr_reader :root, :asset_packager, :template_compiler, :listeners
|
9
|
+
|
10
|
+
include FileMethods and extend FileMethods
|
11
|
+
|
12
|
+
SKELETON = {
|
13
|
+
'config.ru' => '',
|
14
|
+
'index.html' => 'templates',
|
15
|
+
'home.haml' => 'templates',
|
16
|
+
'all.coffee' => 'assets'/'javascripts',
|
17
|
+
'app.coffee' => 'assets'/'javascripts',
|
18
|
+
'home.coffee' => 'assets'/'javascripts'/'screens',
|
19
|
+
'all.scss' => 'assets'/'stylesheets'
|
20
|
+
}
|
21
|
+
|
22
|
+
def initialize root, options = { :fresh => true }
|
23
|
+
@root = expand(root)
|
24
|
+
@logger = Tres::Logger.new(STDOUT)
|
25
|
+
@listeners = OpenStruct.new
|
26
|
+
if options[:fresh] == true
|
27
|
+
create_all_dirs
|
28
|
+
copy_templates_over
|
29
|
+
copy_fonts_over
|
30
|
+
end
|
31
|
+
make_asset_packager
|
32
|
+
make_template_compiler
|
33
|
+
make_templates_listener unless options[:deaf] == true
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.open root, options = {}
|
37
|
+
new root, options.merge(:fresh => false)
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
def create_all_dirs
|
42
|
+
new_dir @root
|
43
|
+
new_dir @root/'assets'
|
44
|
+
new_dir @root/'assets'/'stylesheets'
|
45
|
+
new_dir @root/'assets'/'javascripts'
|
46
|
+
new_dir @root/'assets'/'javascripts'/'screens'
|
47
|
+
new_dir @root/'assets'/'javascripts'/'models'
|
48
|
+
new_dir @root/'assets'/'javascripts'/'collections'
|
49
|
+
new_dir @root/'templates'
|
50
|
+
new_dir @root/'build'
|
51
|
+
end
|
52
|
+
|
53
|
+
def copy_templates_over
|
54
|
+
SKELETON.each do |file, destination|
|
55
|
+
copy Tres.templates_dir/file, @root/destination
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def copy_fonts_over
|
60
|
+
copy Tres.root/'font', @root/'build'
|
61
|
+
end
|
62
|
+
|
63
|
+
def make_asset_packager
|
64
|
+
@asset_packager = Tres::AssetPackager.new :root => @root, :logger => @logger
|
65
|
+
end
|
66
|
+
|
67
|
+
def make_template_compiler
|
68
|
+
@template_compiler = Tres::TemplateCompiler.new :root => @root
|
69
|
+
end
|
70
|
+
|
71
|
+
def make_templates_listener
|
72
|
+
@listeners.templates = Listen.to @root/'templates', :latency => 0.25, :force_polling => true
|
73
|
+
@listeners.templates.change do |modified, added, removed|
|
74
|
+
added_modified = (added + modified).map { |path| relativize(path, @root) }
|
75
|
+
removed = removed.map { |path| relativize(path, @root) }
|
76
|
+
|
77
|
+
unless added_modified.empty?
|
78
|
+
Tres.say_progress "Compiling #{added_modified.join(', ').colorize(:yellow)}" do
|
79
|
+
@template_compiler.compile_all
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
unless removed.empty?
|
84
|
+
Tres.say_progress "Removing #{removed.join(', ').colorize(:yellow)}" do
|
85
|
+
@template_compiler.remove_template removed
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
@listeners.templates.start false
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require 'sprockets'
|
3
|
+
require 'compass'
|
4
|
+
require 'coffee_script'
|
5
|
+
|
6
|
+
module Tres
|
7
|
+
class AssetPackager
|
8
|
+
attr_reader :sprockets
|
9
|
+
|
10
|
+
include FileMethods
|
11
|
+
|
12
|
+
def initialize options = {}
|
13
|
+
@root = options[:root]
|
14
|
+
@assets = @root/'assets'
|
15
|
+
@logger = options[:logger]
|
16
|
+
@sprockets = Sprockets::Environment.new Pathname(@assets) do |env|
|
17
|
+
env.logger = @logger
|
18
|
+
Compass.sass_engine_options[:load_paths].each do |path|
|
19
|
+
env.append_path path.to_s
|
20
|
+
end
|
21
|
+
env.append_path Tres.styles_dir
|
22
|
+
env.append_path Tres.scripts_dir
|
23
|
+
env.append_path @assets/'javascripts'
|
24
|
+
env.append_path @assets/'stylesheets'
|
25
|
+
env.append_path @assets
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def compile_to_build path
|
30
|
+
asset = @sprockets[path]
|
31
|
+
asset.write_to(@root/'build'/path) if asset
|
32
|
+
end
|
33
|
+
|
34
|
+
def new_script path, contents = ""
|
35
|
+
mkdir_p @assets/'javascripts'/dirname(path)
|
36
|
+
path = path + '.coffee' if extname(path).blank?
|
37
|
+
raise Tres::ScriptExistsError if file?(@assets/'javascripts'/path)
|
38
|
+
create_file @assets/'javascripts'/path, contents
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
def coffeescript?
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/lib/tres/errors.rb
ADDED
data/lib/tres/logger.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'rack'
|
2
|
+
require 'colorize'
|
3
|
+
|
4
|
+
module Tres
|
5
|
+
class RackLogger < ::Rack::CommonLogger
|
6
|
+
private
|
7
|
+
def log env, status, header, began_at
|
8
|
+
now = Time.now
|
9
|
+
length = extract_content_length(header)
|
10
|
+
|
11
|
+
logger = @logger || env['rack.errors']
|
12
|
+
logger.write Tres::OUTPUT_FORMAT % (
|
13
|
+
"[%s] %s %s\n" % [
|
14
|
+
colorized_status(status),
|
15
|
+
env["REQUEST_METHOD"],
|
16
|
+
env["PATH_INFO"]
|
17
|
+
]
|
18
|
+
)
|
19
|
+
end
|
20
|
+
|
21
|
+
def extract_content_length(headers)
|
22
|
+
value = headers['Content-Length'] or return '-'
|
23
|
+
value.to_s == '0' ? '-' : value
|
24
|
+
end
|
25
|
+
|
26
|
+
def colorized_status status
|
27
|
+
color = :green
|
28
|
+
case status.to_s[0]
|
29
|
+
when '3'
|
30
|
+
color = :yellow
|
31
|
+
when '4'
|
32
|
+
color = :red
|
33
|
+
end
|
34
|
+
status.to_s[0..3].colorize(color)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/tres/server.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'rack'
|
2
|
+
require File.dirname(__FILE__) + '/rack_logger'
|
3
|
+
|
4
|
+
module Tres
|
5
|
+
class Server
|
6
|
+
attr_accessor :app
|
7
|
+
|
8
|
+
def initialize app
|
9
|
+
@app = app
|
10
|
+
@static_server = Rack::File.new @app.root/'build'
|
11
|
+
end
|
12
|
+
|
13
|
+
def call env
|
14
|
+
response = serve_static env
|
15
|
+
response = serve_asset env if not_found?(response)
|
16
|
+
response = serve_index if not_found?(response)
|
17
|
+
response
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_rack_app
|
21
|
+
me = self
|
22
|
+
Rack::Builder.new do
|
23
|
+
use ::Tres::RackLogger
|
24
|
+
use Rack::Lint
|
25
|
+
run me
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
def serve_static env
|
31
|
+
@static_server.call env
|
32
|
+
end
|
33
|
+
|
34
|
+
def serve_asset env
|
35
|
+
@app.asset_packager.sprockets.call env
|
36
|
+
end
|
37
|
+
|
38
|
+
def serve_index
|
39
|
+
[ 200, { 'Content-Type' => 'text/html' }, File.open(@app.root/'build'/'index.html') ]
|
40
|
+
end
|
41
|
+
|
42
|
+
def not_found
|
43
|
+
[ 404, { 'Content-Type' => 'text/plain' }, ['Not found'] ]
|
44
|
+
end
|
45
|
+
|
46
|
+
def not_found? response
|
47
|
+
response[0] == 404
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
require 'tilt'
|
2
|
+
require 'haml'
|
3
|
+
require 'find'
|
4
|
+
require 'stringio'
|
5
|
+
|
6
|
+
module Tres
|
7
|
+
class TemplateCompiler
|
8
|
+
include FileMethods
|
9
|
+
|
10
|
+
EXTENSION_MAP = {
|
11
|
+
%w(.haml .erb) => '.html'
|
12
|
+
}
|
13
|
+
|
14
|
+
def initialize options = {}
|
15
|
+
@root = options[:root]
|
16
|
+
@templates = @root/'templates'
|
17
|
+
@assets = @root/'assets'
|
18
|
+
@build = @root/'build'
|
19
|
+
end
|
20
|
+
|
21
|
+
def compile_to_build path
|
22
|
+
mkdir_p @build
|
23
|
+
return copy(@templates/path, @build/path) if path == 'index.html'
|
24
|
+
mkdir_p @build/'templates'/dirname(path)
|
25
|
+
if static? path
|
26
|
+
copy @templates/path, @build/'templates'/path
|
27
|
+
else
|
28
|
+
template = Tilt.new @templates/path
|
29
|
+
destination = compiled_extension(@build/'templates'/path)
|
30
|
+
create_file destination, template.render
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def compile_to_templates_js path
|
35
|
+
contents = static?(path) ? read_file(@templates/path) : Tilt.new(@templates/path).render
|
36
|
+
unless file?(@assets/'javascripts'/'templates.js')
|
37
|
+
copy Tres.templates_dir/'templates.js', @assets/'javascripts'
|
38
|
+
end
|
39
|
+
remove_from_templates_js(path) if in_templates_js?(path)
|
40
|
+
append_to_file @assets/'javascripts'/'templates.js', jst_format(path, contents)
|
41
|
+
end
|
42
|
+
|
43
|
+
def new_template path, contents = ""
|
44
|
+
mkdir_p @templates/dirname(path)
|
45
|
+
path = path + '.haml' if extname(path).blank?
|
46
|
+
raise Tres::TemplateExistsError if file?(@templates/path)
|
47
|
+
create_file @templates/path, contents
|
48
|
+
end
|
49
|
+
|
50
|
+
def compile_all
|
51
|
+
if index = first_index_file
|
52
|
+
compile_to_build index
|
53
|
+
end
|
54
|
+
Find.find(@templates) do |path|
|
55
|
+
next if dir?(path) or path =~ /index/
|
56
|
+
compile_to_templates_js relativize(path, @templates)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def remove_template path
|
61
|
+
if path.is_a? Array
|
62
|
+
path.each do |file|
|
63
|
+
remove_template file
|
64
|
+
end
|
65
|
+
end
|
66
|
+
return false if path =~ /index/
|
67
|
+
remove_from_templates_js path
|
68
|
+
delete! @templates/path
|
69
|
+
delete! @build/path
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
def in_templates_js? path
|
74
|
+
lines = readlines @assets/'javascripts'/'templates.js'
|
75
|
+
!!lines.index { |line| line =~ /^JST\[\"#{path.sub(extname(path), '')}\"\]/ }
|
76
|
+
end
|
77
|
+
|
78
|
+
def remove_from_templates_js path
|
79
|
+
return false unless file?(@assets/'javascripts'/'templates.js')
|
80
|
+
lines = readlines @assets/'javascripts'/'templates.js'
|
81
|
+
lines.reject! { |line| line =~ /^JST\[\"#{path.sub(extname(path), '')}\"\]/ }
|
82
|
+
lines.reject! { |line| line == "\n" }
|
83
|
+
create_file @assets/'javascripts'/'templates.js', lines.join("\n")
|
84
|
+
end
|
85
|
+
|
86
|
+
def compiled_extension path
|
87
|
+
actual = extname(path)
|
88
|
+
wanted = EXTENSION_MAP.map { |exts, final| final if exts.include?(actual) }.first
|
89
|
+
dirname(path)/(basename(path, actual) + wanted)
|
90
|
+
end
|
91
|
+
|
92
|
+
def jst_format path, template
|
93
|
+
"JST[\"#{path.sub(extname(path), '')}\"] = \"#{escape_js(template)}\";\n"
|
94
|
+
end
|
95
|
+
|
96
|
+
def escape_js js
|
97
|
+
js.
|
98
|
+
gsub('\\', '\\\\\\').
|
99
|
+
gsub(/\r\n|\r|\n/, '\\n').
|
100
|
+
gsub(/['"]/, '\\\\\&').
|
101
|
+
gsub('</script>','</scr"+"ipt>')
|
102
|
+
end
|
103
|
+
|
104
|
+
def static? path
|
105
|
+
extname(path) == '.html'
|
106
|
+
end
|
107
|
+
|
108
|
+
def first_index_file
|
109
|
+
file = Dir.glob(@templates/'index.*').first
|
110
|
+
relativize(file, @templates) if file
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
data/lib/tres.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
$:.unshift File.dirname(__FILE__)
|
3
|
+
|
4
|
+
require 'rubygems'
|
5
|
+
require 'logger'
|
6
|
+
require 'json/pure'
|
7
|
+
require 'ext/string'
|
8
|
+
require 'ext/filemethods'
|
9
|
+
require 'tres/app'
|
10
|
+
require 'tres/asset_packager'
|
11
|
+
require 'tres/template_compiler'
|
12
|
+
require 'tres/server'
|
13
|
+
require 'tres/logger'
|
14
|
+
require 'tres/errors'
|
15
|
+
|
16
|
+
module Tres
|
17
|
+
OUTPUT_FORMAT = " → %s"
|
18
|
+
|
19
|
+
class << self
|
20
|
+
def quiet!
|
21
|
+
@quiet = true
|
22
|
+
end
|
23
|
+
|
24
|
+
def quiet?
|
25
|
+
!!@quiet
|
26
|
+
end
|
27
|
+
|
28
|
+
def verbose!
|
29
|
+
@quiet = false
|
30
|
+
end
|
31
|
+
|
32
|
+
def say something
|
33
|
+
STDOUT.puts(OUTPUT_FORMAT % something) unless quiet?
|
34
|
+
yield if block_given?
|
35
|
+
end
|
36
|
+
|
37
|
+
def say_progress something, done = '✔'.colorize(:green)
|
38
|
+
STDOUT.write(OUTPUT_FORMAT % something + " ") unless quiet?
|
39
|
+
yield if block_given?
|
40
|
+
STDOUT.puts done unless quiet?
|
41
|
+
end
|
42
|
+
|
43
|
+
def error message
|
44
|
+
STDERR.puts message unless quiet?
|
45
|
+
end
|
46
|
+
|
47
|
+
def root
|
48
|
+
@root ||= File.expand_path File.dirname(__FILE__)/'..'
|
49
|
+
end
|
50
|
+
|
51
|
+
def templates_dir
|
52
|
+
root/'templates'
|
53
|
+
end
|
54
|
+
|
55
|
+
def styles_dir
|
56
|
+
root/'sass'
|
57
|
+
end
|
58
|
+
|
59
|
+
def scripts_dir
|
60
|
+
root/'javascripts'
|
61
|
+
end
|
62
|
+
end
|
63
|
+
VERSION = File.read Tres.root/'VERSION'
|
64
|
+
end
|