guard-templates 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,13 @@
1
+ require 'execjs'
2
+
3
+ module Guard
4
+ class Templates < Guard
5
+ module Compilers
6
+ def self.compile_jade(str)
7
+ jade = File.read(File.join(Pathname.new(__FILE__).dirname.to_s, '../engines/jade.js'))
8
+ ExecJS.compile("window = {}; #{jade}").eval("window.jade.compile(#{str.dump}, {client:true, compileDebug:false}).toString()")
9
+
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,11 @@
1
+ # Example guardfile for guard-templates
2
+ #
3
+ # by default, output to a single .js file in public/javascript/templates.js
4
+ # if you'd like to compile each template to an individual .js file,
5
+ # change :output to a directory path instead
6
+ #
7
+ # see https://github.com/thegreatape/guard-templates for more options
8
+ guard 'templates', :output => 'public/javascript/templates.js', :namespace => 'MyApp' do
9
+ # change this regex to match your source files
10
+ watch(/app\/javascripts\/templates\/(.*)\.jade$/)
11
+ end
@@ -0,0 +1,115 @@
1
+ require "guard"
2
+ require "guard/guard"
3
+ require 'guard/compilers'
4
+
5
+ module Guard
6
+ class Templates < Guard
7
+
8
+ DEFAULTS = {
9
+ :namespace => 'this'
10
+ }
11
+
12
+ # Initialize a Guard.
13
+ # @param [Array<Guard::Watcher>] watchers the Guard file watchers
14
+ # @param [Hash] options the custom Guard options
15
+ def initialize(watchers = [], options = {})
16
+ @watchers = watchers
17
+ @options = DEFAULTS.clone.merge(options)
18
+ @single_file_mode = @options[:output].match(/\.js$/)
19
+ super(watchers, @options)
20
+ end
21
+
22
+ # Call once when Guard starts. Please override initialize method to init stuff.
23
+ # @raise [:task_has_failed] when start has failed
24
+ def start
25
+ run_all
26
+ end
27
+
28
+ # Called when `stop|quit|exit|s|q|e + enter` is pressed (when Guard quits).
29
+ # @raise [:task_has_failed] when stop has failed
30
+ def stop
31
+ end
32
+
33
+ # Called when `reload|r|z + enter` is pressed.
34
+ # This method should be mainly used for "reload" (really!) actions like reloading passenger/spork/bundler/...
35
+ # @raise [:task_has_failed] when reload has failed
36
+ def reload
37
+ end
38
+
39
+ # Called when just `enter` is pressed
40
+ # This method should be principally used for long action like running all specs/tests/...
41
+ # @raise [:task_has_failed] when run_all has failed
42
+ def run_all
43
+ run_on_change(Watcher.match_files(self, Dir.glob('**/*')))
44
+ end
45
+
46
+ # Called on file(s) modifications that the Guard watches.
47
+ # @param [Array<String>] paths the changes files or paths
48
+ # @raise [:task_has_failed] when run_on_change has failed
49
+ def run_on_change(paths)
50
+ templates = {}
51
+ paths.each do |path|
52
+ @watchers.each do |watcher|
53
+ if target = get_target(path, watcher)
54
+ contents = File.read(path)
55
+ if @single_file_mode
56
+ templates[target] = contents
57
+ else
58
+ dir = Pathname.new(target[:path]).dirname.to_s
59
+ FileUtils.mkdir_p(dir) if !File.exist?(dir)
60
+ File.open(target[:path], 'w') do |f|
61
+ f.write("#{@options[:namespace]}['#{target[:name]}'] = #{compile(contents, target[:type])}")
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
67
+ if @single_file_mode
68
+ File.open(@options[:output], 'w') do |f|
69
+ js = templates.map{|target,content| "#{target[:name].dump}: #{compile(content, target[:type])}" }.join(",\n")
70
+ f.write "#{@options[:namespace]}.templates = {#{js}}"
71
+ end
72
+ end
73
+ end
74
+
75
+ # Called on file(s) deletions that the Guard watches.
76
+ # @param [Array<String>] paths the deleted files or paths
77
+ # @raise [:task_has_failed] when run_on_change has failed
78
+ def run_on_deletion(paths)
79
+ matched = false
80
+ paths.each do |path|
81
+ @watchers.each do |watcher|
82
+ if target = get_target(path, watcher)
83
+ matched = true
84
+ FileUtils.rm target[:path] unless @single_file_mode
85
+ end
86
+ end
87
+ end
88
+
89
+ # this is slow, but works. would be better to do a eval + series of deletes,
90
+ # but how to re-serialize the js object?
91
+ run_all if @single_file_mode && matched
92
+ end
93
+
94
+ private
95
+ def get_target(path, watcher)
96
+ if match = path.match(watcher.pattern)
97
+ subpath = match[1]
98
+ jspath = File.join(@options[:output], "#{subpath}.js")
99
+
100
+ {:name => subpath,
101
+ :type => File.extname(path).gsub(/^\./,''),
102
+ :path => jspath}
103
+ end
104
+ end
105
+
106
+ def compile(str, type)
107
+ if Compilers.methods.include?("compile_#{type}")
108
+ Compilers.send("compile_#{type}", str)
109
+ else
110
+ str.dump
111
+ end
112
+ end
113
+
114
+ end
115
+ end
@@ -0,0 +1,202 @@
1
+ require "rubygems"
2
+ require "bundler/setup"
3
+ require 'execjs'
4
+ require 'fakefs/safe'
5
+
6
+ require 'guard/templates'
7
+ require 'guard/watcher'
8
+
9
+ # ExecJS needs real filesystem access to find the js engine
10
+ def eval_js(str, context='')
11
+ FakeFS.deactivate!
12
+ result = ExecJS.compile(context).eval(str)
13
+ FakeFS.activate!
14
+ result
15
+ end
16
+
17
+ module ExecJS
18
+ class << self
19
+ alias :old_compile :compile
20
+ def compile(source, *args)
21
+ FakeFS.deactivate!
22
+ result = self.old_compile(source, *args)
23
+ FakeFS.activate!
24
+ result
25
+ end
26
+ end
27
+ end
28
+
29
+ ExecJS.runtime.class.const_get('Context').class_eval do
30
+ alias :old_eval :eval
31
+ def eval(source, options={})
32
+ FakeFS.deactivate!
33
+ result = old_eval(source, options)
34
+ FakeFS.activate!
35
+ result
36
+ end
37
+ end
38
+
39
+
40
+ describe Guard::Templates do
41
+ before(:each) do
42
+ # have to write js libs to FakeFS post-fake-activation
43
+ js_src = File.join(Pathname.new(__FILE__).dirname.to_s, '../lib/engines/')
44
+ libs = Dir.glob(File.join(js_src, "*.js")).map do |path|
45
+ [File.basename(path), File.read(path)]
46
+ end
47
+ FakeFS.activate!
48
+ FileUtils.mkdir_p 'lib/engines'
49
+ FileUtils.mkdir_p 'javascripts/templates'
50
+ FileUtils.mkdir_p 'public/templates'
51
+ libs.each do |name, content|
52
+ File.open("lib/engines/#{name}", 'w') {|f| f.write(content)}
53
+ end
54
+ end
55
+
56
+ after(:each) do
57
+ FileUtils.rm_rf 'javascripts'
58
+ FileUtils.rm_rf 'public'
59
+ FakeFS.deactivate!
60
+ end
61
+
62
+ describe "#run_on_change" do
63
+ before(:each) do
64
+ @content = "<div>\n {{foo}}\n</div>"
65
+ @template = 'javascripts/templates/home.handlebars'
66
+ File.open(@template, 'w') {|f| f.write(@content)}
67
+ end
68
+
69
+ it "should create a new file with the stringifed template" do
70
+ @guard_templates = Guard::Templates.new([Guard::Watcher.new(/javascripts\/templates\/(.*)\.handlebars/)],
71
+ :output => 'public/templates')
72
+ @guard_templates.run_on_change [@template]
73
+ File.exists?('public/templates/home.js').should == true
74
+ File.read('public/templates/home.js').should == "this['home'] = #{@content.dump}"
75
+ end
76
+
77
+ it "should write the template with a namespace" do
78
+ @guard_templates = Guard::Templates.new([Guard::Watcher.new(/javascripts\/templates\/(.*)\.handlebars/)],
79
+ :output => 'public/templates',
80
+ :namespace => 'App.Jawesome')
81
+ @guard_templates.run_on_change [@template]
82
+ File.read('public/templates/home.js').should == "App.Jawesome['home'] = #{@content.dump}"
83
+ end
84
+
85
+ it "should write the templates to a single file when output has a .js extension" do
86
+ wombat_template = 'javascripts/templates/wombat.handlebars'
87
+ wombat_content = '<span>{{wombat}}</span>'
88
+ File.open(wombat_template, 'w') {|f| f.write(wombat_content)}
89
+ @guard_templates = Guard::Templates.new([Guard::Watcher.new(/javascripts\/templates\/(.*)\.handlebars/)],
90
+ :output => 'public/templates/templates.js')
91
+ @guard_templates.run_on_change [@template, wombat_template]
92
+ File.exists?('public/templates/home.js').should == false
93
+ File.exists?('public/templates/wombat.js').should == false
94
+ File.exists?('public/templates/templates.js').should == true
95
+ templates = eval_js File.read('public/templates/templates.js')
96
+ templates.keys.should =~ ['home', 'wombat']
97
+ templates['wombat'].should == wombat_content
98
+ templates['home'].should == @content
99
+ end
100
+
101
+ it "should compile jade to native functions" do
102
+ jade_template = 'javascripts/templates/jade-test.jade'
103
+ jade_content = '.greetings O HAI'
104
+ File.open(jade_template, 'w') {|f| f.write(jade_content)}
105
+ @guard_templates = Guard::Templates.new([Guard::Watcher.new(/javascripts\/templates\/(.*)\.jade/)],
106
+ :namespace => 'app',
107
+ :output => 'public/templates')
108
+ @guard_templates.run_on_change [jade_template]
109
+ File.exists?('public/templates/jade-test.js').should == true
110
+ compiled = File.read('public/templates/jade-test.js')
111
+ jade = File.read('lib/engines/jade-runtime.js')
112
+ context = %{var window = {}, app = {};
113
+ #{jade}
114
+ #{compiled} }
115
+ fn = "app['jade-test']()"
116
+ eval_js(fn, context).should == '<div class="greetings">O HAI</div>'
117
+ end
118
+
119
+ it "should compile jade to native functions in single-file mode" do
120
+ jade_template = 'javascripts/templates/jade-test.jade'
121
+ jade_content = '.greetings O HAI'
122
+ File.open(jade_template, 'w') {|f| f.write(jade_content)}
123
+ @guard_templates = Guard::Templates.new([Guard::Watcher.new(/javascripts\/templates\/(.*)\.jade/)],
124
+ :namespace => 'app',
125
+ :output => 'public/templates/all.js')
126
+ @guard_templates.run_on_change [jade_template]
127
+ File.exists?('public/templates/all.js').should == true
128
+ File.exists?('public/templates/jade-test.js').should == false
129
+ compiled = File.read('public/templates/all.js')
130
+ jade = File.read('lib/engines/jade-runtime.js')
131
+ context = %{var window = {}, app = {};
132
+ #{jade}
133
+ #{compiled} }
134
+ fn = "app.templates['jade-test']()"
135
+ eval_js(fn, context).should == '<div class="greetings">O HAI</div>'
136
+ end
137
+ end
138
+
139
+ describe "#run_on_deletion" do
140
+ it "should delete output files with source files are deleted" do
141
+ src_path = 'javascripts/templates/home.handlebars'
142
+ dst_path = 'public/templates/home.js'
143
+ File.open(src_path, 'w') {|f| f.write('<div>{foo}</div>')}
144
+ File.open(dst_path, 'w') {|f| f.write('var home = "<div>{foo}</div>"')}
145
+ @guard_templates = Guard::Templates.new([Guard::Watcher.new(/javascripts\/templates\/(.*)\.handlebars/)],
146
+ :output => 'public/templates')
147
+ FileUtils.rm(src_path)
148
+ @guard_templates.run_on_deletion(src_path)
149
+ File.exists?(dst_path).should == false
150
+ end
151
+
152
+ it "should delete multiple files correctly" do
153
+ srcs = ['foo', 'bar', 'goats']
154
+ srcs.each do |s|
155
+ File.open("javascripts/templates/#{s}.handlebars", 'w') {|f| f.write('<div>{foo}</div>')}
156
+ File.open("public/templates/#{s}.js", 'w') {|f| f.write("this['#{s}'] = '<div>{foo}</div>'")}
157
+ end
158
+ File.open("javascripts/templates/dont_delete_me_bro.handlebars", 'w') {|f| f.write('<div>{foo}</div>')}
159
+ @guard_templates = Guard::Templates.new([Guard::Watcher.new(/javascripts\/templates\/(.*)\.handlebars/)],
160
+ :output => 'public/templates')
161
+
162
+ srcs.each{|s| FileUtils.rm "javascripts/templates/#{s}.handlebars"}
163
+ @guard_templates.run_on_deletion(srcs.map{|s| "javascripts/templates/#{s}.handlebars"})
164
+ srcs.each{|s| File.exists?("public/templates/#{s}.js").should == false}
165
+ end
166
+
167
+ it "should delete entries in single file mode" do
168
+ src_path = 'javascripts/templates/home.handlebars'
169
+ File.open(src_path, 'w') {|f| f.write("<div>{foo}</div>")}
170
+ File.open('javascripts/templates/foo.handlebars', 'w') {|f| f.write('foo')}
171
+
172
+ dst_path = 'public/templates.js'
173
+ File.open(dst_path, 'w') {|f| f.write("this['templates'] = {home: 'home', foo: 'bar'}")}
174
+ @guard_templates = Guard::Templates.new([Guard::Watcher.new(/javascripts\/templates\/(.*)\.handlebars/)],
175
+ :output => 'public/templates.js')
176
+ eval_js(File.read(dst_path)).keys.should =~ ['home', 'foo']
177
+
178
+ FileUtils.rm(src_path)
179
+ @guard_templates.run_on_deletion([src_path])
180
+ templates = eval_js(File.read(dst_path))
181
+ templates.keys.should == ['foo']
182
+ end
183
+ end
184
+
185
+ describe '#run_all' do
186
+ it "should compile everything" do
187
+ paths = ['foo', 'bar/baz', 'duck/duck/go']
188
+ paths.each do |p|
189
+ target = "javascripts/templates/#{p}.handlebars"
190
+ FileUtils.mkdir_p Pathname.new(target).dirname.to_s
191
+ File.open(target, 'w') {|f| f.write(p)}
192
+ end
193
+ @guard_templates = Guard::Templates.new([Guard::Watcher.new(/javascripts\/templates\/(.*)\.handlebars/)],
194
+ :output => 'public/templates/')
195
+
196
+ @guard_templates.run_all
197
+ paths.each do |p|
198
+ File.exists?("public/templates/#{p}.js").should == true
199
+ end
200
+ end
201
+ end
202
+ end
metadata ADDED
@@ -0,0 +1,159 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: guard-templates
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Thomas Mayfield
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2012-04-01 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: guard
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ hash: 3
29
+ segments:
30
+ - 0
31
+ version: "0"
32
+ type: :runtime
33
+ version_requirements: *id001
34
+ - !ruby/object:Gem::Dependency
35
+ name: execjs
36
+ prerelease: false
37
+ requirement: &id002 !ruby/object:Gem::Requirement
38
+ none: false
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ hash: 3
43
+ segments:
44
+ - 0
45
+ version: "0"
46
+ type: :runtime
47
+ version_requirements: *id002
48
+ - !ruby/object:Gem::Dependency
49
+ name: json
50
+ prerelease: false
51
+ requirement: &id003 !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ hash: 3
57
+ segments:
58
+ - 0
59
+ version: "0"
60
+ type: :runtime
61
+ version_requirements: *id003
62
+ - !ruby/object:Gem::Dependency
63
+ name: bundler
64
+ prerelease: false
65
+ requirement: &id004 !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ hash: 3
71
+ segments:
72
+ - 0
73
+ version: "0"
74
+ type: :development
75
+ version_requirements: *id004
76
+ - !ruby/object:Gem::Dependency
77
+ name: rspec
78
+ prerelease: false
79
+ requirement: &id005 !ruby/object:Gem::Requirement
80
+ none: false
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ hash: 3
85
+ segments:
86
+ - 0
87
+ version: "0"
88
+ type: :development
89
+ version_requirements: *id005
90
+ - !ruby/object:Gem::Dependency
91
+ name: fakefs
92
+ prerelease: false
93
+ requirement: &id006 !ruby/object:Gem::Requirement
94
+ none: false
95
+ requirements:
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ hash: 3
99
+ segments:
100
+ - 0
101
+ version: "0"
102
+ type: :development
103
+ version_requirements: *id006
104
+ description: Guard plugin for smart, automatic compilation of your Javascript template files into usable Javascript
105
+ email:
106
+ - Thomas.Mayfield@gmail.com
107
+ executables: []
108
+
109
+ extensions: []
110
+
111
+ extra_rdoc_files: []
112
+
113
+ files:
114
+ - .gitignore
115
+ - Gemfile
116
+ - README.md
117
+ - Rakefile
118
+ - guard-templates.gemspec
119
+ - lib/engines/jade-runtime.js
120
+ - lib/engines/jade.js
121
+ - lib/guard/compilers.rb
122
+ - lib/guard/templates.rb
123
+ - lib/guard/templates/templates/Guardfile
124
+ - spec/guard-templates-spec.rb
125
+ homepage: ""
126
+ licenses: []
127
+
128
+ post_install_message:
129
+ rdoc_options: []
130
+
131
+ require_paths:
132
+ - lib
133
+ required_ruby_version: !ruby/object:Gem::Requirement
134
+ none: false
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ hash: 3
139
+ segments:
140
+ - 0
141
+ version: "0"
142
+ required_rubygems_version: !ruby/object:Gem::Requirement
143
+ none: false
144
+ requirements:
145
+ - - ">="
146
+ - !ruby/object:Gem::Version
147
+ hash: 3
148
+ segments:
149
+ - 0
150
+ version: "0"
151
+ requirements: []
152
+
153
+ rubyforge_project: guard-templates
154
+ rubygems_version: 1.8.17
155
+ signing_key:
156
+ specification_version: 3
157
+ summary: Javascript template compilation via Guard
158
+ test_files:
159
+ - spec/guard-templates-spec.rb