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.
Files changed (72) hide show
  1. data/.gitignore +3 -0
  2. data/Gemfile +3 -0
  3. data/Gemfile.lock +67 -0
  4. data/README.md +50 -0
  5. data/Rakefile +16 -0
  6. data/VERSION +1 -0
  7. data/bin/tres +103 -0
  8. data/examples/anagen/anagen.coffee +61 -0
  9. data/examples/anagen/anagen.css +1413 -0
  10. data/examples/anagen/anagen.js +182 -0
  11. data/examples/anagen/anagen.scss +20 -0
  12. data/examples/anagen/backbone-min.js +38 -0
  13. data/examples/anagen/index.html +50 -0
  14. data/examples/anagen/jquery-1.8.0.min.js +2 -0
  15. data/examples/anagen/templates.coffee +6 -0
  16. data/examples/anagen/templates.js +13 -0
  17. data/examples/anagen/tres.js +375 -0
  18. data/examples/anagen/underscore-min.js +32 -0
  19. data/font/fontawesome-webfont.eot +0 -0
  20. data/font/fontawesome-webfont.svg +255 -0
  21. data/font/fontawesome-webfont.ttf +0 -0
  22. data/font/fontawesome-webfont.woff +0 -0
  23. data/javascripts/backbone-min.js +38 -0
  24. data/javascripts/jquery-1.8.0.min.js +2 -0
  25. data/javascripts/tres.coffee +210 -0
  26. data/javascripts/underscore-min.js +32 -0
  27. data/lib/ext/.filemethods.rb.swp +0 -0
  28. data/lib/ext/filemethods.rb +109 -0
  29. data/lib/ext/string.rb +33 -0
  30. data/lib/tres/app.rb +92 -0
  31. data/lib/tres/asset_packager.rb +45 -0
  32. data/lib/tres/errors.rb +6 -0
  33. data/lib/tres/logger.rb +9 -0
  34. data/lib/tres/rack_logger.rb +37 -0
  35. data/lib/tres/server.rb +50 -0
  36. data/lib/tres/template_compiler.rb +113 -0
  37. data/lib/tres.rb +64 -0
  38. data/sass/font-awesome.scss +329 -0
  39. data/sass/tres/base.scss +131 -0
  40. data/sass/tres/themes/default.scss +71 -0
  41. data/spec/app_spec.rb +44 -0
  42. data/spec/asset_packager_spec.rb +74 -0
  43. data/spec/filemethods_spec.rb +84 -0
  44. data/spec/fixtures/index.haml +41 -0
  45. data/spec/fixtures/index.html +50 -0
  46. data/spec/sample/assets/javascripts/all.coffee +12 -0
  47. data/spec/sample/assets/javascripts/anagen.coffee +7 -0
  48. data/spec/sample/assets/javascripts/backbone-min.js +38 -0
  49. data/spec/sample/assets/javascripts/jquery-1.8.0.min.js +2 -0
  50. data/spec/sample/assets/javascripts/underscore-min.js +32 -0
  51. data/spec/sample/assets/stylesheets/app.scss +7 -0
  52. data/spec/sample/assets/stylesheets/with_imports.scss +10 -0
  53. data/spec/sample/templates/article.html +0 -0
  54. data/spec/sample/templates/book.haml +4 -0
  55. data/spec/sample/templates/books/li.haml +2 -0
  56. data/spec/sample/templates/index.html +50 -0
  57. data/spec/server_spec.rb +35 -0
  58. data/spec/spec_helper.rb +69 -0
  59. data/spec/template_compiler_spec.rb +96 -0
  60. data/templates/all.coffee +21 -0
  61. data/templates/all.scss +11 -0
  62. data/templates/app.coffee +10 -0
  63. data/templates/collection_script.coffee.erb +6 -0
  64. data/templates/config.ru +3 -0
  65. data/templates/home.coffee +7 -0
  66. data/templates/home.haml +17 -0
  67. data/templates/index.html +15 -0
  68. data/templates/model_script.coffee.erb +3 -0
  69. data/templates/screen_script.coffee.erb +5 -0
  70. data/templates/templates.js +2 -0
  71. data/tres.gemspec +29 -0
  72. 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
@@ -0,0 +1,6 @@
1
+ module Tres
2
+ class CantParseJSONFile < RuntimeError; end
3
+ class NoSuchFile < RuntimeError; end
4
+ class TemplateExistsError < RuntimeError; end
5
+ class ScriptExistsError < RuntimeError; end
6
+ end
@@ -0,0 +1,9 @@
1
+ require 'logger'
2
+
3
+ module Tres
4
+ class Logger < ::Logger
5
+ def format_message severity, timestamp, program, message
6
+ ::Tres::OUTPUT_FORMAT % message + "\n"
7
+ end
8
+ end
9
+ end
@@ -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
@@ -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