contao 0.3.1

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/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