class Sassificator

TODO:

- mediaquery inside of a mediaquery - done partically (only for one tree level deep)
- images formating pattern setting
- add convertor: all #_colors_ to rgb
- MAJOR: formatting is done only for output sass_string, but not for sass_obj. Move formatting options applyment to object creation
- write tests
- had issue with [] brackets - test
- had issue with asset-url asigning - test -             background: asset-url('#{$brand}offersButton_on.png',image',image) 100% 50% no-repeat;
                                                         background: asset-url('#{$brand}//offersButton_on.png) 100% 50% no-repeat;
- issue with assigning same roles one after onother

Attributes

colors_to_vars[RW]

Public Class Methods

new( param = {}) click to toggle source

@param [Boolean] alphabethize_output true : Alphatehise the rules in output string @param [Boolean] colors_to_vars true : sets all color to sass variables to the top of output string @param [Boolean] fromat_image_declarations true : formats images declarations to asset-url (for now at least - TODO: will be unified for any format) @param [Boolean] download_images true : downloads images to specified @output_path @param [String] image_assets_path Image asstes path in application @param [String] output_path Output path must be specified if download_images is set to true

# File lib/sassificator.rb, line 23
def initialize( param = {})
  @alphabethize_output =  (param[:alphabethize_output] != false) != false
  @colors_to_vars =  (param[:colors_to_vars] != false) != false
  @fromat_image_declarations = (param[:fromat_image_declarations] != false) != false
  @download_images =  (param[:download_images] != false) != false

  @image_assets_path = param[:image_assets_path] ? param[:image_assets_path] : ''
  @output_path = param[:output_path] ? param[:output_path] : "#{ENV['HOME']}/Desktop/sassificator_output/"
end

Public Instance Methods

get_sass_str_and_sass_obj(input_css_str) click to toggle source

Returns a hash containing sass_object and sass_formated string REQUIRES A PROPERLY FROMATED INPUT CSS

@param [String] css A PROPERLY FROMATED INPUT CSS STRING | for now it’s NOT working with media_query’s inside of media_querys

# File lib/sassificator.rb, line 40
def get_sass_str_and_sass_obj(input_css_str)
  selectors_hash = css_to_hash input_css_str      # 1. convert plain css to hash obj
  css_stack = objectize_css(selectors_hash)       # 2. convert recieved hash to sass obj with relatons
  sassed_css_string = sass_obj_to_str(css_stack)  # 3. get formatted string out sass_obj

  Hash[:sass_obj => css_stack, :sass_string => sassed_css_string]
end

Private Instance Methods

css_to_hash(input_css) click to toggle source
# File lib/sassificator.rb, line 72
def css_to_hash (input_css)
  input_css = prepare_input_css(input_css)
  selectors_arr = remove_white_spaces_and_new_lines(input_css).gsub(/@media/,"\n@media").gsub(/(?<={{1}).+}(?=[\s\t\n]{0,}}{1})/,'').gsub(/(?<={{1}).+}(?=[\s\t\n]{0,}}{1})/,'').scan(/[^{^}]+(?=\{)/).map {|line| line.sub(/^\s+/,'').sub(/\s+$/,'')}
  rules_arr = remove_white_spaces_and_new_lines(input_css).gsub(/@media/,"\n@media").scan(/(((?<={{1}).+}(?=[\s\t\n]{0,}}{1})|(?<=\{)[^}]+\}{0,}[\\t\\n\s]{0,}(?=\}))|((?<=\{)[^}]+\}{0,}[\\t\\n\s]{0,}(?=\}))|(?<={{1}).+}(?=[\s\t\n]{0,}}{1}))/).map {|item| item.compact.uniq.join} #super-mega reg-exp that scans for normal rules as well as inlined media-query rule + make a single_string_items out of matched array groups
  return_hash = {}
  selectors_arr.each_with_index do |selector, index|
    unless return_hash[selector]
      return_hash[selector] = rules_arr[index.to_i]
    else
      return_hash[selector] = return_hash[selector] + ' ' + rules_arr[index.to_i]
    end
  end

  return_hash.each do |key,val|
    unless val.scan(/[^{^}]+(?=\{)/).size.zero?
      return_hash[key] = css_to_hash val
    end
  end

  return_hash
end
format_color(sassed_str) click to toggle source
# File lib/sassificator.rb, line 215
def format_color(sassed_str)
  #TODO: resolve the match for colors in format #sdsd
  formated_rule = sassed_str
  color_hash = {}
  sassed_str.scan(/rgba\([0-9\,\s\.]+\)|rgb\([0-9\,\s\.]+\)|#[0-9A-Za-z]+(?=;)/).each do|m|

    unless color_hash[m]
      color_hash[m] = '$color_'+color_hash.size.to_s
      formated_rule = formated_rule.gsub( Regexp.new(m.gsub(/\(/,'\(').gsub(/\)/,'\)').gsub(/\./,'\.')), color_hash[m] )
    end
  end

  color_hash.invert.to_a.reverse_each {|m|
    formated_rule = m.join(":")+";\n" + formated_rule
  }

  formated_rule
end
format_images(sassed_str) click to toggle source
# File lib/sassificator.rb, line 172
def format_images (sassed_str)

  require 'net/http'

  formated_rule = sassed_str

  #TODO - move this to optional feature (file downloading)
  #TODO: optimise this - connect to global path
  path = @output_path
  Dir.mkdir(path) unless Dir.exist?(path)
  FileUtils.rm_rf(Dir.glob("#{@output_path}/*"))

  sassed_str.scan(/(url\((((http[s]{0,1}:\/\/)([a-z0-9].+\.[a-z]+).+)(\/.+)))\)/).each do |match|
    #TODO: optimize this - move mathched to variables for clarification of match
    formated_rule = formated_rule.sub(Regexp.new(match[0].gsub(/\(/,'\(').gsub(match[5],'')),'asset-url(\''+@image_assets_path)
    .sub( Regexp.new(match[5]),match[5].sub(/\//,'')+'\'')
    
    #TODO - move this to optional feature (file downloading)
    Net::HTTP.start(match[4]) do |http|
      
      #TODO - test get_url
      # forms relative url
      get_url = match[1].sub(Regexp.new("(http:\/\/||https:\/\/){1}#{match[4]}"),'')
     begin
       http.read_timeout = 20
       resp = http.get(get_url)

       open(path+match[5], 'wb') do |file|
         begin
          file.write(resp.body)
         ensure
          file.close()
         end
       end
       rescue
         p "Fail on getting image #{match[1]}"
       end
     end
  end

  formated_rule
end
format_mixin() click to toggle source
# File lib/sassificator.rb, line 234
def format_mixin
  #TODO: combine similar rules blocks in mixins.. in some way..?
end
format_sass(sassed_str) click to toggle source
# File lib/sassificator.rb, line 165
def format_sass (sassed_str)
  formated_sass_with_images = @fromat_image_declarations ? format_images(sassed_str) : sassed_str
  formated_sass_with_color = @colors_to_vars ? format_color(formated_sass_with_images) : sassed_str

  formated_sass_with_color
end
objectize_css(uninitialized_childrens_hash, parent_node = nil) click to toggle source
# File lib/sassificator.rb, line 94
def objectize_css (uninitialized_childrens_hash, parent_node = nil)

  childrens_stack = {}

  uninitialized_childrens_hash.each { |selector,rule|

    #TODO: optimize pattern mathcing = thei are both matching same string
    match = /^[&.#\"\'\[\]=a-zA-Z-_0-9]{1,}(?=\s(?=[^,]{0,}$))/.match(selector).to_s #checks for childrens in selector

    sub_pat = /^[\.#]{0,1}[a-zA-Z-_0-9]{1,}(?=(\.|:|\[|#))/.match(selector).to_s   #deals with pseudo elements
    #TODO : optimize this
    unless sub_pat.empty?
      unless selector.match(/,/)
        match = sub_pat
        selector = selector.sub( Regexp.new('^'+match) ,match+' &')
      end
    end

    unless match.empty?
      unless node = childrens_stack[match]
        node = CssNode.new
        childrens_stack[match] = node
      end
      node.uninitialized[selector.sub( Regexp.new('^'+match.sub( /\[/ ,'\[').sub(/\]/, '\]')+' ') ,'')] =  rule
      node.parent = parent_node
    else
      if node = childrens_stack[selector]
        node.rule = node.rule + "\n\t" + rule.gsub(/;\s/,";\n\t")
      else
        node = CssNode.new
        if rule.is_a?(Hash)
          node.children = objectize_css( rule, node )
        else
          node.rule = "\n\t"+rule.gsub(/;\s/,";\n\t")
        end
        childrens_stack[selector] = node
      end
      node.rule = node.rule.split(';').sort().join(';') + ';' if @alphabethize_output && !node.rule.empty? #alphabetize
      node.parent = parent_node
    end
  }

  childrens_stack.each { |node_selector,node|
    unless childrens_stack[node_selector].uninitialized.empty?
      childrens_stack[node_selector].children =  objectize_css( childrens_stack[node_selector].uninitialized, node )
      childrens_stack[node_selector].uninitialized = []
    end
  }

  childrens_stack
end
prepare_input_css(input_css) click to toggle source
# File lib/sassificator.rb, line 66
def prepare_input_css(input_css)
  ## 1. reduses double :: pseudo selectors to :
  ## 2. removes empty rules
  input_css.gsub(/::/,':').gsub(/^.+{[\s\t\n]{0,}}/,'')  
end
remove_white_spaces_and_new_lines(line) click to toggle source
# File lib/sassificator.rb, line 62
def remove_white_spaces_and_new_lines(line)
  line.gsub(/\n/,'').gsub(/\t/,'').gsub(/\s+(?=\})/,'').gsub(/(?<=\{)\s+/,'')
end
sass_obj_to_str(css_stack) click to toggle source
# File lib/sassificator.rb, line 146
def sass_obj_to_str (css_stack)
  sassed_str = format_sass sass_stack_to_s(css_stack)

  sassed_str
end
sass_stack_to_s(css_stack) click to toggle source
# File lib/sassificator.rb, line 152
def sass_stack_to_s (css_stack)
  str = ''
  css_stack.each { |node_key,node|
    unless node.children.empty?
      chldrn = node.children
      children_stack = sass_stack_to_s(chldrn)
    end
    str = str +"\n"+ node_key + " {\t" + ( node.rule ? node.rule  : '') + ( children_stack ? children_stack.gsub(/^/,"\t") : '' ) +"\n\t}"
  }

  str
end