contao 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
data/lib/contao.rb ADDED
@@ -0,0 +1,66 @@
1
+ require 'contao/version'
2
+ require 'singleton'
3
+ require 'ostruct'
4
+
5
+ module TechnoGate
6
+ module Contao
7
+
8
+ class RootNotSet < RuntimeError; end
9
+
10
+ # Get the currently running environment
11
+ #
12
+ # @return [Symbol] Currently running environment
13
+ def self.env
14
+ @@env
15
+ end
16
+
17
+ # Set the environment
18
+ #
19
+ # @param [Symbol] Environment
20
+ def self.env=(env)
21
+ @@env = env
22
+ end
23
+
24
+ # Get the currently running rootironment
25
+ #
26
+ # @return [Symbol] Currently running rootironment
27
+ def self.root
28
+ @@root
29
+ end
30
+
31
+ # Set the rootironment
32
+ #
33
+ # @param [Symbol] rootironment
34
+ def self.root=(root)
35
+ @@root = Pathname.new(root).expand_path
36
+ end
37
+
38
+ # Expandify a path
39
+ #
40
+ # @param [String] Path
41
+ # @return [Pathname] Path converted to absolute path
42
+ # raises RootNotSet
43
+ def self.expandify(path)
44
+ raise RootNotSet unless root
45
+
46
+ if path.to_s.start_with? "/"
47
+ Pathname(path).expand_path
48
+ else
49
+ root.join(path)
50
+ end
51
+ end
52
+ end
53
+ end
54
+
55
+ # Contao
56
+ require 'contao/application'
57
+ require 'contao/notifier'
58
+ require 'contao/coffeescript_compiler'
59
+ require 'contao/javascript_compiler'
60
+ require 'contao/stylesheet_compiler'
61
+
62
+ # Guard
63
+ require 'guard/assets'
64
+
65
+ # Monkey patches
66
+ require 'monkey_patches'
@@ -0,0 +1,98 @@
1
+ require 'guard'
2
+ require 'guard/guard'
3
+
4
+ module Guard
5
+ class Assets < ::Guard::Guard
6
+ # Initialize a Guard.
7
+ # @param [Array<Guard::Watcher>] watchers the Guard file watchers
8
+ # @param [Hash] options the custom Guard options
9
+ def initialize(watchers = [], options = {})
10
+ super
11
+
12
+ @coffeescript_compiler = ::TechnoGate::Contao::CoffeescriptCompiler.new
13
+ @javascript_compiler = ::TechnoGate::Contao::JavascriptCompiler.new
14
+ @stylesheet_compiler = ::TechnoGate::Contao::StylesheetCompiler.new
15
+ end
16
+
17
+ # Call once when Guard starts. Please override initialize method to init stuff.
18
+ # @raise [:task_has_failed] when start has failed
19
+ def start
20
+ run_all
21
+ end
22
+
23
+ # Called when just `enter` is pressed
24
+ # This method should be principally used for long action like running all specs/tests/...
25
+ # @raise [:task_has_failed] when run_all has failed
26
+ def run_all
27
+ @stylesheet_compiler.clean
28
+ @coffeescript_compiler.clean
29
+ @javascript_compiler.clean
30
+ compile_stylesheet
31
+ compile_coffeescript
32
+ compile_javascript
33
+ end
34
+
35
+ # Called on file(s) modifications that the Guard watches.
36
+ # @param [Array<String>] paths the changes files or paths
37
+ # @raise [:task_has_failed] when run_on_change has failed
38
+ def run_on_change(paths)
39
+ compile(paths)
40
+ end
41
+
42
+ # Called on file(s) deletions that the Guard watches.
43
+ # @param [Array<String>] paths the deleted files or paths
44
+ # @raise [:task_has_failed] when run_on_change has failed
45
+ def run_on_deletion(paths)
46
+ compile(paths)
47
+ end
48
+
49
+ protected
50
+ def compile(paths)
51
+ coffeescript = javascript = stylesheet = false
52
+
53
+ paths.each do |path|
54
+ coffeescript = true if !coffeescript && is_coffeescript?(path)
55
+ javascript = true if !javascript && is_javascript?(path)
56
+ stylesheet = true if !stylesheet && is_stylesheet?(path)
57
+ end
58
+
59
+ compile_stylesheet if stylesheet
60
+ compile_coffeescript if coffeescript
61
+ compile_javascript if coffeescript || javascript
62
+ end
63
+
64
+ def compile_stylesheet
65
+ @stylesheet_compiler.compile
66
+ end
67
+
68
+ def compile_coffeescript
69
+ @coffeescript_compiler.compile
70
+ end
71
+
72
+ def compile_javascript
73
+ @javascript_compiler.compile
74
+ end
75
+
76
+ def is_coffeescript?(path)
77
+ file_in_path?(path, TechnoGate::Contao::Application.config.javascripts_path) && File.extname(path) == '.coffee'
78
+ end
79
+
80
+ def is_javascript?(path)
81
+ file_in_path?(path, TechnoGate::Contao::Application.config.javascripts_path) && File.extname(path) == '.js'
82
+ end
83
+
84
+ def is_stylesheet?(path)
85
+ file_in_path?(path, TechnoGate::Contao::Application.config.stylesheets_path)
86
+ end
87
+
88
+ def file_in_path?(file, paths)
89
+ paths = [paths] if paths.is_a?(String)
90
+
91
+ paths.each do |path|
92
+ return true if file.start_with?(path)
93
+ end
94
+
95
+ false
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,81 @@
1
+ require 'compass'
2
+
3
+ module Compass::SassExtensions::Functions::Urls
4
+ module ImageUrl
5
+ def image_url(path, only_path = Sass::Script::Bool.new(false), cache_buster = Sass::Script::Bool.new(true))
6
+ path = path.value # get to the string value of the literal.
7
+
8
+ if path =~ %r{^#{Regexp.escape(Compass.configuration.http_images_path)}/(.*)}
9
+ # Treat root relative urls (without a protocol) like normal if they start with
10
+ # the images path.
11
+ path = $1
12
+ elsif absolute_path?(path)
13
+ # Short curcuit if they have provided an absolute url.
14
+ return Sass::Script::String.new("url(#{path})")
15
+ end
16
+
17
+ # Compute the path to the image, either root relative or stylesheet relative
18
+ # or nil if the http_images_path is not set in the configuration.
19
+ http_images_path = if relative?
20
+ compute_relative_path(Compass.configuration.images_path)
21
+ elsif Compass.configuration.http_images_path
22
+ Compass.configuration.http_images_path
23
+ else
24
+ Compass.configuration.http_root_relative(Compass.configuration.images_dir)
25
+ end
26
+
27
+ # Compute the real path to the image on the file stystem if the images_dir is set.
28
+ real_path = if Compass.configuration.images_dir
29
+ File.join(Compass.configuration.project_path, Compass.configuration.images_dir, path)
30
+ end
31
+
32
+ # Generate the digested image (if production)
33
+ if ::TechnoGate::Contao.env == :production
34
+ digested_path = ::TechnoGate::Contao::Compiler.new.send(:create_digest_for_file, real_path)
35
+ real_path = File.join(Compass.configuration.project_path, Compass.configuration.css_dir, File.dirname(path), File.basename(digested_path))
36
+ FileUtils.mkdir_p File.dirname(real_path)
37
+ FileUtils.mv digested_path, real_path
38
+ else
39
+ non_digested_path = File.join(Compass.configuration.project_path, Compass.configuration.css_dir, path)
40
+ FileUtils.mkdir_p File.dirname(non_digested_path)
41
+ FileUtils.cp real_path, non_digested_path
42
+
43
+ real_path = non_digested_path
44
+ end
45
+
46
+ # prepend the path to the image if there's one
47
+ if http_images_path
48
+ http_images_path = "#{http_images_path}/" unless http_images_path[-1..-1] == "/"
49
+ path = "#{http_images_path}#{path}"
50
+ end
51
+
52
+ # Generate the digested image (if production)
53
+ if ::TechnoGate::Contao.env == :production
54
+ path = path.gsub(File.basename(path), File.basename(digested_path))
55
+ end
56
+
57
+ # Compute the asset host unless in relative mode.
58
+ asset_host = if !relative? && Compass.configuration.asset_host
59
+ Compass.configuration.asset_host.call(path)
60
+ end
61
+
62
+ # Compute and append the cache buster if there is one.
63
+ if cache_buster.to_bool
64
+ if cache_buster.is_a?(Sass::Script::String)
65
+ path += "?#{cache_buster.value}"
66
+ else
67
+ path = cache_busted_path(path, real_path)
68
+ end
69
+ end
70
+
71
+ # prepend the asset host if there is one.
72
+ path = "#{asset_host}#{'/' unless path[0..0] == "/"}#{path}" if asset_host
73
+
74
+ if only_path.to_bool
75
+ Sass::Script::String.new(clean_path(path))
76
+ else
77
+ clean_url(path)
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1 @@
1
+ require 'monkey_patches/compass/urls'
@@ -0,0 +1,92 @@
1
+ require 'spec_helper'
2
+
3
+ module TechnoGate
4
+ module Contao
5
+ describe Application do
6
+ subject { Contao::Application.instance }
7
+ let(:klass) { Contao::Application }
8
+
9
+ it_should_behave_like "Singleton"
10
+
11
+ it "should be an open struct" do
12
+ subject.class.superclass.should == OpenStruct
13
+ end
14
+
15
+ it "should have a config as a superclass" do
16
+ subject.config.class.should == OpenStruct
17
+ end
18
+
19
+ describe "config" do
20
+ it "should set a configuration variable using a block" do
21
+ Contao::Application.configure do
22
+ config.foo = :bar
23
+ end
24
+
25
+ Contao::Application.instance.config.foo.should == :bar
26
+ end
27
+
28
+ it "should be accessible form the class level" do
29
+ Contao::Application.configure do
30
+ config.foo = :bar
31
+ end
32
+
33
+ Contao::Application.config.foo.should == :bar
34
+ end
35
+
36
+ describe '#linkify', :fakefs do
37
+ before :each do
38
+ stub_filesystem!
39
+ end
40
+
41
+ it "should call #exhaustive_list_of_files_to_link" do
42
+ subject.should_receive(:exhaustive_list_of_files_to_link).with(
43
+ Contao.expandify(Application.config.contao_path),
44
+ Contao.expandify(Application.config.contao_public_path)
45
+ ).once.and_return []
46
+
47
+ subject.linkify
48
+ end
49
+
50
+ it "should actually link the files" do
51
+ subject.linkify
52
+
53
+ File.exists?('/root/public/non_existing_folder').should be_true
54
+ File.exists?('/root/public/system/modules/some_extension').should be_true
55
+ end
56
+
57
+ it "should be accessible at class level" do
58
+ subject.should_receive(:linkify).once
59
+
60
+ Application.linkify
61
+ end
62
+ end
63
+
64
+ describe '#exhaustive_list_of_files_to_link', :fakefs do
65
+ before :each do
66
+ stub_filesystem!
67
+ end
68
+
69
+ it "should return the correct list of files" do
70
+ list = subject.send :exhaustive_list_of_files_to_link, '/root/contao', '/root/public'
71
+ list.should == [
72
+ ['/root/contao/non_existing_folder', '/root/public/non_existing_folder'],
73
+ ['/root/contao/system/modules/some_extension', '/root/public/system/modules/some_extension']
74
+ ]
75
+ end
76
+ end
77
+
78
+ describe '#name' do
79
+ it {should respond_to :name}
80
+
81
+ it "should return the correct name" do
82
+ subject.name.should == 'root'
83
+ end
84
+
85
+ it "should be accessible at class level" do
86
+ Application.name.should == 'root'
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,78 @@
1
+ require 'spec_helper'
2
+
3
+ module CoffeeScript
4
+ def self.compile(contents, *args)
5
+ contents
6
+ end
7
+ end
8
+
9
+ module TechnoGate
10
+ module Contao
11
+ describe CoffeescriptCompiler do
12
+ it_should_behave_like "Compiler"
13
+
14
+ describe '#compile_assets', :fakefs do
15
+ before :each do
16
+ stub_filesystem!
17
+
18
+ [
19
+ '/root/app/assets/javascripts/simple_coffeescript_file.js.coffee',
20
+ '/root/app/assets/javascripts/simple_javascript_file.js',
21
+ '/root/app/assets/javascripts/nested/script.js.coffee',
22
+ '/root/lib/assets/javascripts/simple_coffeescript_file.js.coffee',
23
+ '/root/vendor/assets/javascripts/simple_coffeescript_file.js.coffee',
24
+ ].each do |file|
25
+ FileUtils.mkdir_p File.dirname(file)
26
+ File.open(file, 'w') do |f|
27
+ f.write file.
28
+ gsub('/root/app/assets/javascripts/', '').
29
+ gsub('/root/lib/assets/javascripts/', '').
30
+ gsub('/root/vendor/assets/javascripts/', '')
31
+ end
32
+ end
33
+ end
34
+
35
+ it "should compile coffeescripts" do
36
+ subject.send :compile_assets
37
+
38
+ File.exists?('/root/tmp/compiled_javascript/app_assets_javascripts/simple_coffeescript_file.js').should be_true
39
+ File.exists?('/root/tmp/compiled_javascript/lib_assets_javascripts/simple_coffeescript_file.js').should be_true
40
+ File.exists?('/root/tmp/compiled_javascript/vendor_assets_javascripts/simple_coffeescript_file.js').should be_true
41
+ File.exists?('/root/tmp/compiled_javascript/app_assets_javascripts/simple_javascript_file').should be_false
42
+ File.exists?('/root/tmp/compiled_javascript/app_assets_javascripts/nested/script.js').should be_true
43
+ end
44
+ end
45
+
46
+ describe "#compute_destination_filename" do
47
+ it "should be able to compute given a relative file" do
48
+ subject.send(:compute_destination_filename, "app/js", "app/js/file.js.coffee").should ==
49
+ "/root/tmp/compiled_javascript/app_js/file.js"
50
+ end
51
+
52
+ it "should be able to compute given an absolute file" do
53
+ subject.send(:compute_destination_filename, "app/js", "/root/app/js/file.js.coffee").should ==
54
+ "/root/tmp/compiled_javascript/app_js/file.js"
55
+ end
56
+
57
+ it "should add automaticallty .js extension" do
58
+ subject.send(:compute_destination_filename, "app/js", "app/js/file.coffee").should ==
59
+ "/root/tmp/compiled_javascript/app_js/file.js"
60
+ end
61
+ end
62
+
63
+ describe "#clean", :fakefs do
64
+ before :each do
65
+ stub_filesystem!
66
+
67
+ FileUtils.mkdir_p '/root/tmp/compiled_javascript'
68
+ end
69
+
70
+ it "should remove the temporary javascript compiled files" do
71
+ subject.clean
72
+
73
+ File.exists?('/root/tmp/compiled_javascript').should be_false
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,91 @@
1
+ require 'spec_helper'
2
+
3
+ module TechnoGate
4
+ module Contao
5
+ describe JavascriptCompiler do
6
+ it_should_behave_like "Compiler"
7
+
8
+ describe "#compile_assets", :fakefs do
9
+ before :each do
10
+ Uglifier.any_instance.stub(:compile)
11
+
12
+ stub_filesystem!
13
+
14
+ @file_path = "/root/app/assets/javascripts/file.js"
15
+ @app_js_path = "/root/public/resources/application.js"
16
+
17
+ File.open(@file_path, 'w') do |file|
18
+ file.write("not compiled js")
19
+ end
20
+ end
21
+
22
+ it "should compile javascripts into #{@app_js_path}" do
23
+ subject.send :compile_assets
24
+ File.exists?(@app_js_path).should be_true
25
+ end
26
+
27
+ it "should add the contents of file.js to app.js un-minified if env is development" do
28
+ subject.send :compile_assets
29
+ File.read(@app_js_path).should ==
30
+ "// #{@file_path}\n#{File.read(@file_path)}\n"
31
+ end
32
+
33
+ it "should add the contents of file.js to app.js minified if env is production" do
34
+ TechnoGate::Contao.env = :production
35
+ Uglifier.any_instance.should_receive(:compile).once.and_return("compiled js")
36
+
37
+ subject.send :compile_assets
38
+ File.read(@app_js_path).should == "compiled js"
39
+ end
40
+ end
41
+
42
+ describe "#javascripts_path" do
43
+ it "should return the configured path as well as the temporary javascript path" do
44
+ subject.send(:javascripts_path).should ==
45
+ [
46
+ 'tmp/compiled_javascript/vendor_assets_javascripts',
47
+ 'vendor/assets/javascripts',
48
+ 'tmp/compiled_javascript/lib_assets_javascripts',
49
+ 'lib/assets/javascripts',
50
+ 'tmp/compiled_javascript/app_assets_javascripts',
51
+ 'app/assets/javascripts',
52
+ ]
53
+ end
54
+ end
55
+
56
+ describe "#create_hashed_assets", :fakefs do
57
+ before :each do
58
+ stub_filesystem!
59
+
60
+ @app_js_path = "/root/public/resources/application.js"
61
+
62
+ File.open(@app_js_path, 'w') do |file|
63
+ file.write('compiled js')
64
+ end
65
+ end
66
+
67
+ it "should call :create_digest_for_file" do
68
+ subject.should_receive(:create_digest_for_file).with(Pathname.new(@app_js_path)).once
69
+
70
+ subject.send :create_hashed_assets
71
+ end
72
+ end
73
+
74
+ describe "#generate_manifest" do
75
+ it "should call generate_manifest_for" do
76
+ subject.should_receive(:generate_manifest_for).with("javascripts", "js").once
77
+
78
+ subject.send :generate_manifest
79
+ end
80
+ end
81
+
82
+ describe "#notify" do
83
+ it "should call Notifier.notify with the appropriate message" do
84
+ Notifier.should_receive(:notify).with("Javascript compiler finished successfully.", title: "JavascriptCompiler").once
85
+
86
+ subject.send :notify
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,50 @@
1
+ require 'spec_helper'
2
+
3
+ module TechnoGate
4
+ module Contao
5
+ describe Notifier do
6
+ subject { Notifier.instance }
7
+ let(:klass) { Notifier }
8
+
9
+ it_should_behave_like "Singleton"
10
+
11
+ describe '#notify' do
12
+ before :each do
13
+ @message = "Hello"
14
+ @output = "Contao>> #{@message}"
15
+ @colored_output = "\e[0;34mContao>>\e[0m \e[0;32m#{@message}\e[0m"
16
+ @options = {title: "Hello, World!"}
17
+
18
+ ::Guard::UI.stub(:color_enabled?).and_return(false)
19
+ end
20
+
21
+ it {should respond_to :notify}
22
+
23
+ it "should call guard ui" do
24
+ ::Guard::UI.should_receive(:info).with(@output, {})
25
+
26
+ subject.notify(@message)
27
+ end
28
+
29
+ it "should send whatever options passed to the info method" do
30
+ ::Guard::UI.should_receive(:info).with(@output, @options)
31
+
32
+ subject.notify(@message, @options)
33
+ end
34
+
35
+ it "should use colors if enabled" do
36
+ ::Guard::UI.should_receive(:color_enabled?).once.and_return(true)
37
+ ::Guard::UI.should_receive(:info).with(@colored_output, @options)
38
+
39
+ subject.notify(@message, @options)
40
+ end
41
+
42
+ it "should be accessible at class level" do
43
+ klass.any_instance.should_receive(:notify).with(@message, @options)
44
+
45
+ klass.notify(@message, @options)
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,117 @@
1
+ require 'spec_helper'
2
+
3
+ module TechnoGate
4
+ module Contao
5
+ describe StylesheetCompiler do
6
+ it_should_behave_like "Compiler"
7
+
8
+ describe "#compile_assets" do
9
+ before :each do
10
+ @updater = mock('updater')
11
+ @updater.stub(:execute)
12
+
13
+ Compass::Commands::UpdateProject.stub(:new).with(
14
+ Contao.root,
15
+ configuration_file: Contao.root.join('config', 'compass.rb')
16
+ ).and_return(@updater)
17
+ end
18
+
19
+ it "should create a new updater" do
20
+ Compass::Commands::UpdateProject.should_receive(:new).with(
21
+ Contao.root,
22
+ configuration_file: Contao.root.join('config', 'compass.rb')
23
+ ).once.and_return(@updater)
24
+
25
+ subject.send :compile_assets
26
+ end
27
+
28
+ it "should cache the updater" do
29
+ Compass::Commands::UpdateProject.should_receive(:new).with(
30
+ Contao.root,
31
+ configuration_file: Contao.root.join('config', 'compass.rb')
32
+ ).once.and_return(@updater)
33
+
34
+ subject.send :compile_assets
35
+ subject.send :compile_assets
36
+ end
37
+
38
+ it "should call execute on the updater" do
39
+ @updater.should_receive(:execute).once
40
+
41
+ subject.send :compile_assets
42
+ end
43
+ end
44
+
45
+ describe "#generate_manifest" do
46
+ it "should call generate_manifest_for" do
47
+ subject.should_receive(:generate_manifest_for).with("stylesheets", "css").once
48
+
49
+ subject.send :generate_manifest
50
+ end
51
+ end
52
+
53
+ describe "#clean assets" do
54
+ before :each do
55
+ @cleaner = mock('cleaner')
56
+ @cleaner.stub(:execute)
57
+
58
+ Compass::Commands::CleanProject.stub(:new).with(
59
+ Contao.root,
60
+ configuration_file: Contao.root.join('config', 'compass.rb')
61
+ ).and_return(@cleaner)
62
+ end
63
+
64
+ it "should create a new cleaner" do
65
+ Compass::Commands::CleanProject.should_receive(:new).with(
66
+ Contao.root,
67
+ configuration_file: Contao.root.join('config', 'compass.rb')
68
+ ).once.and_return(@cleaner)
69
+
70
+ subject.clean
71
+ end
72
+
73
+ it "should cache the cleaner" do
74
+ Compass::Commands::CleanProject.should_receive(:new).with(
75
+ Contao.root,
76
+ configuration_file: Contao.root.join('config', 'compass.rb')
77
+ ).once.and_return(@cleaner)
78
+
79
+ subject.clean
80
+ subject.clean
81
+ end
82
+
83
+ it "should call execute on the cleaner" do
84
+ @cleaner.should_receive(:execute).once
85
+
86
+ subject.send :clean
87
+ end
88
+ end
89
+
90
+ describe 'create_hashed_assets', :fakefs do
91
+ before :each do
92
+ stub_filesystem!
93
+
94
+ @app_css_path = "/root/public/resources/application.css"
95
+
96
+ File.open(@app_css_path, 'w') do |file|
97
+ file.write('compiled css')
98
+ end
99
+ end
100
+
101
+ it "should call :create_digest_for_file" do
102
+ subject.should_receive(:create_digest_for_file).with(Pathname.new(@app_css_path)).once
103
+
104
+ subject.send :create_hashed_assets
105
+ end
106
+ end
107
+
108
+ describe "#notify" do
109
+ it "should call Notifier.notify with the appropriate message" do
110
+ Notifier.should_receive(:notify).with("Stylesheet compiler finished successfully.", title: "StylesheetCompiler").once
111
+
112
+ subject.send :notify
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end