2010-10-26 1 views
0

Я создаю приложение, в котором пользователи могут редактировать свой собственный CSS (в SCSS syntax). Это прекрасно работает, однако, в конечном итоге, я хочу, чтобы эти файлы CSS были «программируемыми», так что пользователи, которые не знают CSS, могут редактировать их в основном. Как?Ruby/Rails: определение переменных из обычного текста для обновления через форму

Если я могу отметить некоторые вещи как редактируемые, мне не нужно создавать невозможную схему базы данных. Например, у меня есть файл с именем СКС style.scss:

 
// @type color 
$header_bg_color: #555; 

// @type image 
$header_image: "http://someurl.com/image.jpg"; 

После этого я могу сделать это:

SomeParser.parse(contents of style.scss here)

Это возвращает хэш или что-то подобное переменных:

 
{:header_bg_color => {:type => "color", :value => "#555"}, :header_image => {:type => "image", :value => "http://someurl.com/image.jpg"} } 

Я могу использовать вышеупомянутый хэш для создания формы, которую пользователь-новичок может использовать для изменения данных и отправки. Я считаю, что знаю, как сделать часть GET и POST.

Что было бы лучшим способом создать/настроить собственный анализатор, чтобы я мог читать комментарии и извлекать из этого переменные? А потом, снова обновите текстовый файл?

Другой возможный способ что-то вроде этого:

 
o = SomeParser.new(contents of style.scss here) 
o.header_bg_color #returns "#555" 
o.header_image = "http://anotherurl.com/image2.jpg" # "updates" or replaces the old header image variable with the new one 
o.render # returns the text with the new values 

Спасибо заранее!

+0

Вы уверены, что вы действительно хотите поместить метаданные (или любую другую машиночитаемую информацию) в комментарии? Запах немного. –

+0

Я пытался подумать о других решениях, но не нашел. Видите ли, в зависимости от того, какие переменные CSS существуют, изменяемые переменные могут измениться. Я не хотел помещать переменные в базу данных и фиксировать переменные, потому что это означало бы тугое соединение между базой данных и стилем CSS. Это было бы не очень приятно, потому что это ограничило бы то, какой стиль CSS можно было бы сделать. –

+0

Я снова прочитал ваш комментарий, и я думаю, что неправильно понял раньше. Ваш вопрос был не в том, что переменные находятся в тексте, но что метаданные были в комментариях? Я просто получил идею от некоторых автогенерирующих систем документирования, где они помещают описание перед методом, поэтому генератор знает, чтобы его поднять. –

ответ

0

Я не использовал его полностью, но мои тесты проходят. Я думаю, этого достаточно, чтобы получить идею :) Мне потребовалось несколько часов обучения, затем еще несколько, чтобы реализовать это.

Btw, я не делал никакой оптимизации здесь. Для меня это не нужно, чтобы быть быстрым

Посмотрите на мой спецификации файла:

 
require 'spec_helper' 

describe StyleParser do 
    describe "given properly formatted input" do 
    it "should set and return variables properly" do 
     text = %{# @name Masthead Background Image 
# @kind file 
# @description Background image. 
$mbc2: "http://someurl.com/image.jpg"; 

# @name Masthead BG Color 
# @kind color 
# @description Background color. 
$mbc: #555;} 
     @s = StyleParser.new(text) 

     @s.mbc.name.should == "Masthead BG Color" 
     @s.mbc.kind.should == "color" 
     @s.mbc.description.should == "Background color." 
     @s.mbc.value.should == "#555" 

     @s.mbc2.name.should == "Masthead Background Image" 
     @s.mbc2.kind.should == "file" 
     @s.mbc2.description.should == "Background image." 
     @s.mbc2.value.should == %Q("http://someurl.com/image.jpg") 
    end 
    end 

    describe "when assigning values" do 
    it "should update its values" do 
     text = %{# @name Masthead Background Image 
# @kind file 
# @description Background image. 
$mbc2: "http://someurl.com/image.jpg";} 
     @s = StyleParser.new(text) 
     @s.mbc2.value = %Q("Another URL") 
     @s.mbc2.value.should == %Q("Another URL") 

     rendered_text = @s.render 
     rendered_text.should_not match(/http:\/\/someurl\.com\/image\.jpg/) 
     rendered_text.should match(/\$mbc2: "Another URL";/) 

     @s.mbc2.value = %Q("Some third URL") 
     @s.mbc2.value.should == %Q("Some third URL") 
     rendered_text = @s.render 
     rendered_text.should_not match(/\$mbc2: "Another URL";/) 
     rendered_text.should match(/\$mbc2: "Some third URL";/) 
    end 

    it "should render the correct values" do 
     text_old = %{# @name Masthead Background Image 
# @kind file 
# @description Background image. 
$mbc2: "http://someurl.com/image.jpg";} 
     text_new = %{# @name Masthead Background Image 
# @kind file 
# @description Background image. 
$mbc2: "Another URL";} 
     @s = StyleParser.new(text_old) 
     @s.mbc2.value = %Q("Another URL") 
     @s.render.should == text_new 
    end 
    end 
end 

Тогда следующие 2 файла:

 
# Used to parse through an scss stylesheet to make editing of that stylesheet simpler 
# Ex. Given a file called style.scss 
# 
# // @name Masthead Background Color 
# // @type color 
# // @description Background color of the masthead. 
# $masthead_bg_color: #444; 
# 
# sp = StyleParser.new(contents of style.scss) 
# 
# # Reading 
# sp.masthead_bg_color.value # returns "#444" 
# sp.masthead_bg_color.name # returns "Masthead Background Color" 
# sp.masthead_bg_color.type # returns "color" 
# sp.masthead_bg_color.description # returns "Background color of the masthead." 
# 
# # Writing 
# sp.masthead_bg_color.value = "#555" 
# sp.render # returns all the text above except masthead_bg_color is now #555; 

class StyleParser 
    def initialize(text) 
    @text = text 
    @variables = {} 

    @eol = '\n' 
    @context_lines = 3 
    @context = "((?:.*#{@eol}){#{@context_lines}})" 
    end 

    # Works this way: http://rubular.com/r/jWSYvfVrjj 
    # Derived from http://stackoverflow.com/questions/2760759/ruby-equivalent-to-grep-c-5-to-get-context-of-lines-around-the-match 
    def get_context(s) 
    regexp = /.*\${1}#{s}:.*;[#{@eol}]*/ 
    @text =~ /^#{@context}(#{regexp})/ 
    before, match = $1, $2 
    "#{before}#{match}" 
    end 

    def render 
    @variables.each do |key, var| 
     @text.gsub!(/^\$#{key}: .+;/, %Q($#{key}: #{var.value};)) 
    end 
    @text 
    end 

    def method_missing(method_name) 
    if method_name.to_s =~ /[\w]+/ 
     context = get_context(method_name) 

     @variables[method_name] ||= StyleVariable.new(method_name, context) 
    end 
    end 
end 
 
class StyleVariable 
    METADATA = %w(name kind description) 

    def initialize(var, text) 
    @var = var 
    @text = text 
    end 

    def method_missing(method_name) 
    if METADATA.include? method_name.to_s 
     content_of(method_name.to_s) 
    end 
    end 

    def value 
    @text.each do |string| 
     string =~ /^\${1}#{@var}: (.+);/ 
     return $1 if $1 
    end 
    end 

    def value=(val) 
    @text.gsub!(/^\$#{@var}: .+;/, "$#{@var}: #{val};") 
    end 

    private 

    def content_of(variable) 
    @text.each do |string| 
     string =~ /^# @([\w]+[^\s]) (.+)/ 
     return $2 if $1 == variable 
    end 
    end 
end 
Смежные вопросы