chriseppstein-compass 0.3.9 → 0.4.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.
@@ -1,48 +1,27 @@
1
1
  require File.join(File.dirname(__FILE__), 'project_base')
2
+ require File.join(Compass.lib_directory, 'compass', 'compiler')
2
3
 
3
4
  module Compass
4
5
  module Commands
5
6
  class UpdateProject < ProjectBase
6
7
 
7
- Base::ACTIONS << :compile
8
- Base::ACTIONS << :overwrite
8
+ def initialize(working_path, options)
9
+ super
10
+ assert_project_directory_exists!
11
+ end
9
12
 
10
13
  def perform
11
14
  read_project_configuration
12
- Dir.glob(separate("#{project_src_directory}/**/[^_]*.sass")).each do |sass_file|
13
- stylesheet_name = sass_file[("#{project_src_directory}/".length)..-6]
14
- compile "#{project_src_subdirectory}/#{stylesheet_name}.sass", "#{project_css_subdirectory}/#{stylesheet_name}.css", options
15
- end
16
- end
17
-
18
- # Compile one Sass file
19
- def compile(sass_filename, css_filename, options)
20
- sass_filename = projectize(sass_filename)
21
- css_filename = projectize(css_filename)
22
- if !File.directory?(File.dirname(css_filename))
23
- directory basename(File.dirname(css_filename)), options.merge(:force => true) unless options[:dry_run]
24
- end
25
- print_action :compile, basename(sass_filename)
26
- if File.exists?(css_filename)
27
- print_action :overwrite, basename(css_filename)
28
- else
29
- print_action :create, basename(css_filename)
30
- end
31
- unless options[:dry_run]
32
- engine = ::Sass::Engine.new(open(sass_filename).read,
33
- :filename => sass_filename,
34
- :line_comments => options[:environment] == :development,
35
- :style => output_style,
36
- :css_filename => css_filename,
37
- :load_paths => sass_load_paths)
38
- output = open(css_filename,'w')
39
- output.write(engine.render)
40
- output.close
41
- end
15
+ default_options = { :style => default_output_style }
16
+ compilation_options = default_options.merge(options).merge(:load_paths => sass_load_paths)
17
+ Compass::Compiler.new(working_path,
18
+ projectize(project_src_subdirectory),
19
+ projectize(project_css_subdirectory),
20
+ compilation_options).run
42
21
  end
43
22
 
44
- def output_style
45
- @output_style ||= options[:style] || if options[:environment] == :development
23
+ def default_output_style
24
+ if options[:environment] == :development
46
25
  :expanded
47
26
  else
48
27
  :compact
@@ -51,17 +30,17 @@ module Compass
51
30
 
52
31
  # where to load sass files from
53
32
  def sass_load_paths
54
- @sass_load_paths ||= [project_src_directory] + Compass::Frameworks::ALL.map{|f| f.stylesheets_directory}
33
+ [project_src_directory] + Compass::Frameworks::ALL.map{|f| f.stylesheets_directory}
55
34
  end
56
35
 
57
36
  # The subdirectory where the sass source is kept.
58
37
  def project_src_subdirectory
59
- @project_src_subdirectory ||= options[:src_dir] || "src"
38
+ Compass.configuration.sass_dir ||= options[:src_dir] || "src"
60
39
  end
61
40
 
62
41
  # The subdirectory where the css output is kept.
63
42
  def project_css_subdirectory
64
- @project_css_subdirectory ||= options[:css_dir] || "stylesheets"
43
+ Compass.configuration.css_dir ||= options[:css_dir] || "stylesheets"
65
44
  end
66
45
 
67
46
  # The directory where the project source files are located.
@@ -69,6 +48,14 @@ module Compass
69
48
  @project_src_directory ||= separate("#{project_directory}/#{project_src_subdirectory}")
70
49
  end
71
50
 
51
+ def assert_project_directory_exists!
52
+ if File.exists?(project_directory) && !File.directory?(project_directory)
53
+ raise Compass::FilesystemConflict.new("#{project_directory} is not a directory.")
54
+ elsif !File.directory?(project_directory)
55
+ raise Compass::Error.new("#{project_directory} does not exist.")
56
+ end
57
+ end
58
+
72
59
  end
73
60
  end
74
61
  end
@@ -8,7 +8,9 @@ require File.join(File.dirname(__FILE__), 'update_project')
8
8
  module Compass
9
9
  module Commands
10
10
  class WatchProject < UpdateProject
11
+
11
12
  attr_accessor :last_update_time
13
+
12
14
  def perform
13
15
  puts ">>> Compiling all stylesheets."
14
16
  super
@@ -34,9 +36,11 @@ module Compass
34
36
  end
35
37
  end
36
38
  end
39
+
37
40
  def most_recent_update_time
38
41
  Dir.glob(separate("#{project_src_directory}/**/*.sass")).map {|sass_file| File.stat(sass_file).mtime}.max
39
42
  end
43
+
40
44
  def should_update?
41
45
  t = most_recent_update_time
42
46
  if t > last_update_time
@@ -0,0 +1,40 @@
1
+ module Compass
2
+ class Compiler
3
+
4
+ include Actions
5
+
6
+ attr_accessor :working_path, :from, :to, :options
7
+
8
+ def initialize(working_path, from, to, options)
9
+ self.working_path = working_path
10
+ self.from, self.to = from, to
11
+ self.logger = options.delete(:logger)
12
+ self.options = options
13
+ end
14
+
15
+ def sass_files
16
+ @sass_files || Dir.glob(separate("#{from}/**/[^_]*.sass"))
17
+ end
18
+
19
+ def stylesheet_name(sass_file)
20
+ sass_file[("#{from}/".length)..-6]
21
+ end
22
+
23
+ def css_files
24
+ @css_files || sass_files.map{|sass_file| "#{to}/#{stylesheet_name(sass_file)}.css"}
25
+ end
26
+
27
+ def target_directories
28
+ css_files.map{|css_file| File.dirname(css_file)}.uniq.sort.sort_by{|d| d.length }
29
+ end
30
+
31
+ def run
32
+ target_directories.each do |dir|
33
+ directory dir
34
+ end
35
+ sass_files.zip(css_files).each do |sass_filename, css_filename|
36
+ compile sass_filename, css_filename, options
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,52 @@
1
+ require 'singleton'
2
+
3
+ module Compass
4
+ class Configuration
5
+ include Singleton
6
+ attr_accessor :project_path, :css_dir, :sass_dir, :images_dir, :javascripts_dir
7
+
8
+ # parses a manifest file which is a ruby script
9
+ # evaluated in a Manifest instance context
10
+ def parse(config_file)
11
+ open(config_file) do |f|
12
+ eval(f.read, instance_binding, config_file)
13
+ end
14
+ end
15
+
16
+ def reset!
17
+ [:project_path, :css_dir, :sass_dir, :images_dir, :javascripts_dir].each do |attr|
18
+ send("#{attr}=", nil)
19
+ end
20
+ end
21
+
22
+ def instance_binding
23
+ binding
24
+ end
25
+ end
26
+
27
+ module ConfigHelpers
28
+ def configuration
29
+ if block_given?
30
+ yield Configuration.instance
31
+ end
32
+ Configuration.instance
33
+ end
34
+
35
+ def sass_plugin_configuration
36
+ proj_sass_path = File.join(configuration.project_path, configuration.sass_dir)
37
+ proj_css_path = File.join(configuration.project_path, configuration.css_dir)
38
+ locations = {proj_sass_path => proj_css_path}
39
+ Compass::Frameworks::ALL.each do |framework|
40
+ locations[framework.stylesheets_directory] = proj_css_path
41
+ end
42
+ {:template_location => locations}
43
+ end
44
+
45
+ def configure_sass_plugin!
46
+ Sass::Plugin.options.merge!(sass_plugin_configuration)
47
+ end
48
+ end
49
+
50
+ extend ConfigHelpers
51
+
52
+ end
@@ -1,21 +1,8 @@
1
1
  class String
2
+
2
3
  # see if string has any content
3
4
  def blank?; self.length.zero?; end
4
5
 
5
- # strip space after :, remove newlines, replace multiple spaces with only one space, remove comments
6
- def strip_space!
7
- replace self.gsub(/:\s*/, ':').gsub(/\n/, '').gsub(/\s+/, ' ').gsub(/(\/\*).*?(\*\/)/, '')
8
- end
9
-
10
- # remove newlines, insert space after comma, replace two spaces with one space after comma
11
- def strip_selector_space!
12
- replace self.gsub(/(\n)/, '').gsub(',', ', ').gsub(', ', ', ')
13
- end
14
-
15
- # remove leading whitespace, remove end whitespace
16
- def strip_side_space!
17
- replace self.gsub(/^\s+/, '').gsub(/\s+$/, $/)
18
- end
19
6
  end
20
7
 
21
8
  class NilClass
@@ -23,17 +10,3 @@ class NilClass
23
10
  true
24
11
  end
25
12
  end
26
-
27
- class File
28
- # string output from file
29
- def self.path_to_string(path)
30
- File.new(path).read
31
- end
32
-
33
- # saves a string to a specified file path
34
- def self.string_to_file(string, path)
35
- directory = File.dirname(path)
36
- FileUtils.mkdir_p directory unless File.directory?(directory)
37
- File.open(path, 'w') { |f| f << string }
38
- end
39
- end
@@ -0,0 +1,7 @@
1
+ module Compass
2
+ class Error < StandardError
3
+ end
4
+
5
+ class FilesystemConflict < Error
6
+ end
7
+ end
data/lib/compass/exec.rb CHANGED
@@ -1,15 +1,13 @@
1
1
  require 'optparse'
2
2
  require 'rubygems'
3
3
  require 'haml'
4
+ require File.join(Compass.lib_directory, 'compass', 'logger')
5
+ require File.join(Compass.lib_directory, 'compass', 'configuration')
6
+ require File.join(Compass.lib_directory, 'compass', 'errors')
7
+ require File.join(Compass.lib_directory, 'compass', 'actions')
4
8
 
5
9
  module Compass
6
10
  module Exec
7
- class ExecError < StandardError
8
- end
9
-
10
- class DirectoryExistsError < ExecError
11
- end
12
-
13
11
 
14
12
  def report_error(e, options)
15
13
  $stderr.puts "#{e.class} on line #{get_line e} of #{get_file e}: #{e.message}"
@@ -48,7 +46,7 @@ module Compass
48
46
  perform!
49
47
  rescue Exception => e
50
48
  raise e if e.is_a? SystemExit
51
- if e.is_a?(ExecError) || e.is_a?(OptionParser::ParseError)
49
+ if e.is_a?(::Compass::Error) || e.is_a?(OptionParser::ParseError)
52
50
  $stderr.puts e.message
53
51
  else
54
52
  ::Compass::Exec.report_error(e, @options)
@@ -77,6 +75,7 @@ module Compass
77
75
  self.options[:command] ||= self.options[:project_name] ? :create_project : :update_project
78
76
  self.options[:environment] ||= :production
79
77
  self.options[:framework] ||= :compass
78
+ self.options[:project_type] ||= :stand_alone
80
79
  end
81
80
 
82
81
  def trim_trailing_separator(path)
@@ -131,8 +130,8 @@ END
131
130
  require library
132
131
  end
133
132
 
134
- opts.on('--rails', "Install compass into your Ruby on Rails project found in the current directory.") do
135
- self.options[:command] = :install_rails
133
+ opts.on('--rails', "Sets the project type to a rails project.") do
134
+ self.options[:project_type] = :rails
136
135
  end
137
136
 
138
137
  opts.on('-q', '--quiet', :NONE, 'Quiet mode.') do
@@ -0,0 +1,135 @@
1
+ module Compass
2
+ module Installers
3
+
4
+ class Base
5
+
6
+ include Actions
7
+
8
+ attr_accessor :template_path, :target_path, :working_path
9
+ attr_accessor :options
10
+ attr_accessor :manifest
11
+ attr_accessor :css_dir, :sass_dir, :images_dir, :javascripts_dir
12
+
13
+ def initialize(template_path, target_path, options = {})
14
+ @template_path = template_path
15
+ @target_path = target_path
16
+ @working_path = Dir.getwd
17
+ @options = options
18
+ @manifest = Manifest.new(manifest_file)
19
+ self.logger = options[:logger]
20
+ configure
21
+ end
22
+
23
+ def manifest_file
24
+ @manifest_file ||= File.join(template_path, "manifest.rb")
25
+ end
26
+
27
+ # Initializes the project to work with compass
28
+ def init
29
+ end
30
+
31
+ # Runs the installer.
32
+ # Every installer must conform to the installation strategy of prepare, install, and then finalize.
33
+ # A default implementation is provided for each step.
34
+ def run(options = {})
35
+ prepare
36
+ install
37
+ finalize unless options[:skip_finalization]
38
+ end
39
+
40
+ # The default configure method -- it sets up directories from the options
41
+ # and corresponding default_* methods for those not found in the options hash.
42
+ # It can be overridden it or augmented for reading config files,
43
+ # prompting the user for more information, etc.
44
+ def configure
45
+ unless @configured
46
+ [:css_dir, :sass_dir, :images_dir, :javascripts_dir].each do |opt|
47
+ configure_option_with_default opt
48
+ end
49
+ end
50
+ ensure
51
+ @configured = true
52
+ end
53
+
54
+ # The default prepare method -- it is a no-op.
55
+ # Generally you would create required directories, etc.
56
+ def prepare
57
+ end
58
+
59
+ def configure_option_with_default(opt)
60
+ value = options[opt]
61
+ value ||= begin
62
+ default_method = "default_#{opt}".to_sym
63
+ send(default_method) if respond_to?(default_method)
64
+ end
65
+ send("#{opt}=", value)
66
+ end
67
+
68
+ # The default install method. Calls install_<type> methods in the order specified by the manifest.
69
+ def install
70
+ manifest.each do |entry|
71
+ send("install_#{entry.type}", entry.from, entry.to, entry.options)
72
+ end
73
+ end
74
+
75
+ # The default finalize method -- it is a no-op.
76
+ # This could print out a message or something.
77
+ def finalize
78
+ end
79
+
80
+ def compilation_required?
81
+ false
82
+ end
83
+
84
+ def self.installer(type, &locator)
85
+ locator ||= lambda{|to| to}
86
+ loc_method = "install_location_for_#{type}".to_sym
87
+ define_method loc_method, locator
88
+ define_method "install_#{type}" do |from, to, options|
89
+ copy templatize(from), targetize(send(loc_method, to))
90
+ end
91
+ end
92
+
93
+ installer :stylesheet do |to|
94
+ "#{sass_dir}/#{to}"
95
+ end
96
+
97
+ installer :image do |to|
98
+ "#{images_dir}/#{to}"
99
+ end
100
+
101
+ installer :script do |to|
102
+ "#{javascripts_dir}/#{to}"
103
+ end
104
+
105
+ installer :file
106
+
107
+ # returns an absolute path given a path relative to the current installation target.
108
+ # Paths can use unix style "/" and will be corrected for the current platform.
109
+ def targetize(path)
110
+ strip_trailing_separator File.join(target_path, separate(path))
111
+ end
112
+
113
+ # returns an absolute path given a path relative to the current template.
114
+ # Paths can use unix style "/" and will be corrected for the current platform.
115
+ def templatize(path)
116
+ strip_trailing_separator File.join(template_path, separate(path))
117
+ end
118
+
119
+ def stylesheet_links
120
+ html = "<head>\n"
121
+ manifest.each_stylesheet do |stylesheet|
122
+ media = if stylesheet.options[:media]
123
+ %Q{ media="#{stylesheet.options[:media]}"}
124
+ end
125
+ ss_line = %Q{ <link href="/stylesheets/#{stylesheet.to.sub(/\.sass$/,'.css')}"#{media} rel="stylesheet" type="text/css" />}
126
+ if stylesheet.options[:ie]
127
+ ss_line = " <!--[if IE]>\n #{ss_line}\n <![endif]-->"
128
+ end
129
+ html << ss_line + "\n"
130
+ end
131
+ html << "</head>"
132
+ end
133
+ end
134
+ end
135
+ end
@@ -0,0 +1,57 @@
1
+ module Compass
2
+ module Installers
3
+
4
+ class Manifest
5
+
6
+ # A Manifest entry
7
+ class Entry < Struct.new(:type, :from, :options)
8
+ def to
9
+ options[:to] || from
10
+ end
11
+ end
12
+
13
+ def initialize(manifest_file = nil)
14
+ @entries = []
15
+ parse(manifest_file) if manifest_file
16
+ end
17
+
18
+ def self.type(t)
19
+ eval <<-END
20
+ def #{t}(from, options = {})
21
+ @entries << Entry.new(:#{t}, from, options)
22
+ end
23
+ def has_#{t}?
24
+ @entries.detect {|e| e.type == :#{t}}
25
+ end
26
+ def each_#{t}
27
+ @entries.select {|e| e.type == :#{t}}.each {|e| yield e}
28
+ end
29
+ END
30
+ end
31
+
32
+ type :stylesheet
33
+ type :image
34
+ type :javascript
35
+ type :file
36
+
37
+ # Enumerates over the manifest files
38
+ def each
39
+ @entries.each {|e| yield e}
40
+ end
41
+
42
+
43
+ protected
44
+ # parses a manifest file which is a ruby script
45
+ # evaluated in a Manifest instance context
46
+ def parse(manifest_file)
47
+ open(manifest_file) do |f|
48
+ eval(f.read, instance_binding, manifest_file)
49
+ end
50
+ end
51
+ def instance_binding
52
+ binding
53
+ end
54
+ end
55
+
56
+ end
57
+ end
@@ -0,0 +1,117 @@
1
+ module Compass
2
+ module Installers
3
+
4
+ class RailsInstaller < Base
5
+
6
+ def configure
7
+ configuration_file = targetize('config/initializers/compass.rb')
8
+ if File.exists?(configuration_file)
9
+ open(configuration_file) do |config|
10
+ eval(config.read, nil, configuration_file)
11
+ end
12
+ end
13
+ end
14
+
15
+ def init
16
+ set_sass_dir unless sass_dir
17
+ set_css_dir unless css_dir
18
+ directory targetize(css_dir)
19
+ directory targetize(sass_dir)
20
+ write_file targetize('config/initializers/compass.rb'), initializer_contents
21
+ end
22
+
23
+ def prepare
24
+ end
25
+
26
+ def finalize(options = {})
27
+ if options[:create]
28
+ puts <<-NEXTSTEPS
29
+
30
+ Congratulations! Your rails project has been configured to use Compass.
31
+ Sass will automatically compile your stylesheets during the next
32
+ page request and keep them up to date when they change.
33
+ Make sure you restart your server!
34
+
35
+ Next add these lines to the head of your layouts:
36
+
37
+ NEXTSTEPS
38
+ end
39
+ puts stylesheet_links
40
+ puts "\n(You are using haml, aren't you?)"
41
+ end
42
+
43
+ def sass_dir
44
+ Compass.configuration.sass_dir
45
+ end
46
+
47
+ def css_dir
48
+ Compass.configuration.css_dir
49
+ end
50
+
51
+ def images_dir
52
+ separate "public/images"
53
+ end
54
+
55
+ def javascripts_dir
56
+ separate "public/javascripts"
57
+ end
58
+
59
+ def set_sass_dir
60
+ recommended_location = separate('app/stylesheets')
61
+ default_location = separate('public/stylesheets/sass')
62
+ print %Q{Compass recommends that you keep your stylesheets in #{recommended_location}
63
+ instead of the Sass default location of #{default_location}.
64
+ Is this OK? (Y/n) }
65
+ answer = gets.downcase[0]
66
+ Compass.configuration.sass_dir = answer == ?n ? default_location : recommended_location
67
+ end
68
+
69
+ def set_css_dir
70
+ recommended_location = separate("public/stylesheets/compiled")
71
+ default_location = separate("public/stylesheets")
72
+ puts
73
+ print %Q{Compass recommends that you keep your compiled css in #{recommended_location}/
74
+ instead the Sass default of #{default_location}/.
75
+ However, if you're exclusively using Sass, then #{default_location}/ is recommended.
76
+ Emit compiled stylesheets to #{recommended_location}/? (Y/n) }
77
+ answer = gets.downcase[0]
78
+ Compass.configuration.css_dir = answer == ?n ? default_location : recommended_location
79
+ end
80
+
81
+ def initializer_contents
82
+ %Q{require 'compass'
83
+ # If you have any compass plugins, require them here.
84
+ Compass.configuration do |config|
85
+ config.project_path = RAILS_ROOT
86
+ config.sass_dir = "#{sass_dir}"
87
+ config.css_dir = "#{css_dir}"
88
+ end
89
+ Compass.configure_sass_plugin!
90
+ }
91
+ end
92
+
93
+ def stylesheet_prefix
94
+ if css_dir.length >= 19
95
+ "#{css_dir[19..-1]}/"
96
+ else
97
+ nil
98
+ end
99
+ end
100
+
101
+ def stylesheet_links
102
+ html = "%head\n"
103
+ manifest.each_stylesheet do |stylesheet|
104
+ ss_line = " = stylesheet_link_tag '#{stylesheet_prefix}#{stylesheet.to.sub(/\.sass$/,'.css')}'"
105
+ if stylesheet.options[:media]
106
+ ss_line += ", :media => '#{stylesheet.options[:media]}'"
107
+ end
108
+ if stylesheet.options[:ie]
109
+ ss_line = " /[if IE]\n " + ss_line
110
+ end
111
+ html << ss_line + "\n"
112
+ end
113
+ html
114
+ end
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,76 @@
1
+ module Compass
2
+ module Installers
3
+
4
+ class StandAloneInstaller < Base
5
+
6
+ def configure
7
+ if File.exists?(config_file)
8
+ Compass.configuration.parse(config_file)
9
+ elsif File.exists?(old_config_file)
10
+ Compass.configuration.parse(old_config_file)
11
+ end
12
+ super
13
+ end
14
+
15
+ def init
16
+ directory targetize("")
17
+ directory targetize(css_dir)
18
+ directory targetize(sass_dir)
19
+ end
20
+
21
+ def prepare
22
+ directory targetize(images_dir) if manifest.has_image?
23
+ directory targetize(javascripts_dir) if manifest.has_javascript?
24
+ end
25
+
26
+ def default_css_dir
27
+ Compass.configuration.css_dir || "stylesheets"
28
+ end
29
+
30
+ def default_sass_dir
31
+ Compass.configuration.sass_dir ||"src"
32
+ end
33
+
34
+ def default_images_dir
35
+ Compass.configuration.images_dir || "images"
36
+ end
37
+
38
+ def default_javascripts_dir
39
+ Compass.configuration.javascripts_dir || "javascripts"
40
+ end
41
+
42
+ # Read the configuration file for this project
43
+ def config_file
44
+ @config_file ||= targetize('config.rb')
45
+ end
46
+
47
+ def old_config_file
48
+ @old_config_file ||= targetize('src/config.rb')
49
+ end
50
+
51
+ def finalize(options = {})
52
+ if options[:create]
53
+ puts <<-NEXTSTEPS
54
+
55
+ Congratulations! Your compass project has been created.
56
+ You must recompile your sass stylesheets when they change.
57
+ This can be done in one of the following ways:
58
+ 1. From within your project directory run:
59
+ compass
60
+ 2. From any directory run:
61
+ compass -u path/to/project
62
+ 3. To monitor your project for changes and automatically recompile:
63
+ compass --watch [path/to/project]
64
+
65
+ NEXTSTEPS
66
+ end
67
+ puts "To import your new stylesheets add the following lines of HTML (or equivalent) to your webpage:"
68
+ puts stylesheet_links
69
+ end
70
+
71
+ def compilation_required?
72
+ true
73
+ end
74
+ end
75
+ end
76
+ end