asker-tool 2.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +674 -0
- data/README.md +53 -0
- data/bin/asker +4 -0
- data/docs/changelog/v2.1.md +99 -0
- data/docs/commands.md +15 -0
- data/docs/contributions.md +18 -0
- data/docs/history.md +40 -0
- data/docs/idea.md +44 -0
- data/docs/inputs/README.md +39 -0
- data/docs/inputs/code.md +69 -0
- data/docs/inputs/concepts.md +142 -0
- data/docs/inputs/jedi.md +68 -0
- data/docs/inputs/tables.md +112 -0
- data/docs/inputs/templates.md +87 -0
- data/docs/install/README.md +38 -0
- data/docs/install/manual.md +26 -0
- data/docs/install/scripts.md +26 -0
- data/docs/revise/asker-file.md +41 -0
- data/docs/revise/buenas-practicas/01-convocatoria.md +30 -0
- data/docs/revise/buenas-practicas/02-formulario.md +35 -0
- data/docs/revise/buenas-practicas/03-descripcion.md +63 -0
- data/docs/revise/buenas-practicas/04-resultados.md +17 -0
- data/docs/revise/buenas-practicas/05-reproducir.md +10 -0
- data/docs/revise/ejemplos/01/README.md +27 -0
- data/docs/revise/ejemplos/02/README.md +31 -0
- data/docs/revise/ejemplos/03/README.md +31 -0
- data/docs/revise/ejemplos/04/README.md +37 -0
- data/docs/revise/ejemplos/05/README.md +25 -0
- data/docs/revise/ejemplos/06/README.md +43 -0
- data/docs/revise/ejemplos/README.md +11 -0
- data/docs/revise/projects.md +74 -0
- data/lib/asker.rb +103 -0
- data/lib/asker/ai/ai.rb +70 -0
- data/lib/asker/ai/ai_calculate.rb +55 -0
- data/lib/asker/ai/concept_ai.rb +49 -0
- data/lib/asker/ai/question.rb +58 -0
- data/lib/asker/ai/stages/base_stage.rb +16 -0
- data/lib/asker/ai/stages/main.rb +8 -0
- data/lib/asker/ai/stages/stage_b.rb +87 -0
- data/lib/asker/ai/stages/stage_d.rb +160 -0
- data/lib/asker/ai/stages/stage_f.rb +156 -0
- data/lib/asker/ai/stages/stage_i.rb +140 -0
- data/lib/asker/ai/stages/stage_s.rb +52 -0
- data/lib/asker/ai/stages/stage_t.rb +170 -0
- data/lib/asker/application.rb +30 -0
- data/lib/asker/checker.rb +356 -0
- data/lib/asker/cli.rb +85 -0
- data/lib/asker/code/ai/base_code_ai.rb +48 -0
- data/lib/asker/code/ai/code_ai_factory.rb +26 -0
- data/lib/asker/code/ai/javascript_code_ai.rb +167 -0
- data/lib/asker/code/ai/python_code_ai.rb +167 -0
- data/lib/asker/code/ai/ruby_code_ai.rb +169 -0
- data/lib/asker/code/ai/sql_code_ai.rb +69 -0
- data/lib/asker/code/code.rb +53 -0
- data/lib/asker/data/column.rb +62 -0
- data/lib/asker/data/concept.rb +183 -0
- data/lib/asker/data/data_field.rb +87 -0
- data/lib/asker/data/row.rb +93 -0
- data/lib/asker/data/table.rb +96 -0
- data/lib/asker/data/template.rb +65 -0
- data/lib/asker/data/world.rb +53 -0
- data/lib/asker/exporter/code_gift_exporter.rb +35 -0
- data/lib/asker/exporter/code_screen_exporter.rb +45 -0
- data/lib/asker/exporter/concept_ai_gift_exporter.rb +33 -0
- data/lib/asker/exporter/concept_ai_screen_exporter.rb +115 -0
- data/lib/asker/exporter/concept_ai_yaml_exporter.rb +33 -0
- data/lib/asker/exporter/concept_doc_exporter.rb +21 -0
- data/lib/asker/exporter/concept_screen_exporter.rb +25 -0
- data/lib/asker/exporter/main.rb +9 -0
- data/lib/asker/files/config.ini +40 -0
- data/lib/asker/formatter/code_string_formatter.rb +16 -0
- data/lib/asker/formatter/concept_doc_formatter.rb +37 -0
- data/lib/asker/formatter/concept_string_formatter.rb +66 -0
- data/lib/asker/formatter/question_gift_formatter.rb +65 -0
- data/lib/asker/formatter/question_hash_formatter.rb +40 -0
- data/lib/asker/formatter/question_moodlexml_formatter.rb +71 -0
- data/lib/asker/formatter/rb2haml_formatter.rb +26 -0
- data/lib/asker/lang/lang.rb +42 -0
- data/lib/asker/lang/lang_factory.rb +19 -0
- data/lib/asker/lang/text_actions.rb +150 -0
- data/lib/asker/loader/code_loader.rb +53 -0
- data/lib/asker/loader/content_loader.rb +101 -0
- data/lib/asker/loader/directory_loader.rb +58 -0
- data/lib/asker/loader/file_loader.rb +33 -0
- data/lib/asker/loader/image_url_loader.rb +61 -0
- data/lib/asker/loader/input_loader.rb +24 -0
- data/lib/asker/loader/project_loader.rb +71 -0
- data/lib/asker/logger.rb +21 -0
- data/lib/asker/project.rb +170 -0
- metadata +261 -0
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'erb'
|
4
|
+
require 'yaml'
|
5
|
+
require_relative 'text_actions'
|
6
|
+
|
7
|
+
# Lang#lang
|
8
|
+
class Lang
|
9
|
+
include TextActions
|
10
|
+
|
11
|
+
attr_reader :code, :mistakes
|
12
|
+
|
13
|
+
def initialize(code = 'en')
|
14
|
+
@code = code.to_s
|
15
|
+
load_files
|
16
|
+
end
|
17
|
+
|
18
|
+
def lang
|
19
|
+
@code
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def load_files
|
25
|
+
dirbase = File.dirname(__FILE__)
|
26
|
+
filename = File.join(dirbase, 'locales', @code, 'templates.yaml')
|
27
|
+
begin
|
28
|
+
@templates = YAML.load(File.new(filename))
|
29
|
+
rescue StandardError => e
|
30
|
+
p = Project.instance
|
31
|
+
p.vervose "[ERROR] lang.initialize(): Reading YAML file <#{filename}>"
|
32
|
+
p.vervose "[ADVISE] Revise apostrophe into string without \\ char\n"
|
33
|
+
raise e
|
34
|
+
end
|
35
|
+
filename = File.join(dirbase, 'locales', @code, 'connectors.yaml')
|
36
|
+
@connectors = YAML.load(File.new(filename))
|
37
|
+
|
38
|
+
filename = File.join(dirbase, 'locales', @code, 'mistakes.yaml')
|
39
|
+
@mistakes = YAML.load(File.new(filename))
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'singleton'
|
4
|
+
require_relative 'lang'
|
5
|
+
require_relative '../project'
|
6
|
+
|
7
|
+
# LangFactory#get
|
8
|
+
class LangFactory
|
9
|
+
include Singleton
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@langs = {}
|
13
|
+
Project.instance.locales.each { |i| @langs[i] = Lang.new(i) }
|
14
|
+
end
|
15
|
+
|
16
|
+
def get(locale)
|
17
|
+
@langs[locale]
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,150 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module TextActions
|
4
|
+
|
5
|
+
def text_for(pOption, pText1="",pText2="",pText3="",pText4="",pText5="",pText6="",pText7="")
|
6
|
+
text1=pText1
|
7
|
+
text2=pText2
|
8
|
+
text3=pText3
|
9
|
+
text4=pText4
|
10
|
+
text5=pText5
|
11
|
+
text6=pText6
|
12
|
+
text7=pText7
|
13
|
+
|
14
|
+
# TODO: check if exists pOption before use it
|
15
|
+
renderer = ERB.new(@templates[pOption])
|
16
|
+
output = renderer.result(binding)
|
17
|
+
return output
|
18
|
+
end
|
19
|
+
|
20
|
+
def text_filter_connectors(pText, pFilter)
|
21
|
+
input_lines = pText.split(".")
|
22
|
+
output_lines = []
|
23
|
+
output_words = []
|
24
|
+
input_lines.each_with_index do |line, rowindex|
|
25
|
+
row=[]
|
26
|
+
line.split(' ').each_with_index do |word, colindex|
|
27
|
+
flag = @connectors.include? word.downcase
|
28
|
+
|
29
|
+
# if <word> is a conector and <pFilter>==true Then Choose this <word>
|
30
|
+
# if <word> isn't a conector and <pFilter>==true and <word>.length>1 Then Choose this <word>
|
31
|
+
if (flag and pFilter) || (!flag and !pFilter and word.length>1)
|
32
|
+
output_words << {:word => word,
|
33
|
+
:row => rowindex,
|
34
|
+
:col => colindex }
|
35
|
+
row << (output_words.size-1)
|
36
|
+
else
|
37
|
+
row << word
|
38
|
+
end
|
39
|
+
end
|
40
|
+
row << '.'
|
41
|
+
output_lines << row
|
42
|
+
end
|
43
|
+
|
44
|
+
indexes = []
|
45
|
+
exclude = ['[', ']', '(', ')', "\"" ]
|
46
|
+
output_words.each_with_index do |item, index|
|
47
|
+
flag = true
|
48
|
+
exclude.each { |e| flag = false if (item[:word].include?(e)) }
|
49
|
+
indexes << index if flag
|
50
|
+
end
|
51
|
+
|
52
|
+
result={ :lines => output_lines, :words => output_words, :indexes => indexes }
|
53
|
+
return result
|
54
|
+
end
|
55
|
+
|
56
|
+
def text_with_connectors(text)
|
57
|
+
text_filter_connectors(text, false)
|
58
|
+
end
|
59
|
+
|
60
|
+
def text_without_connectors(text)
|
61
|
+
text_filter_connectors(text, true)
|
62
|
+
end
|
63
|
+
|
64
|
+
def build_text_from_filtered(pStruct, pIndexes)
|
65
|
+
lines = pStruct[:lines]
|
66
|
+
lIndexes = pIndexes.sort
|
67
|
+
counter = 1
|
68
|
+
lText = ''
|
69
|
+
|
70
|
+
lines.each do |line|
|
71
|
+
line.each do |value|
|
72
|
+
if value.class == String
|
73
|
+
lText += (' ' + value)
|
74
|
+
elsif value == value.to_i
|
75
|
+
# INFO: ruby 2.4 unifies Fixnum and Bignum into Integer
|
76
|
+
# Avoid using deprecated classes.
|
77
|
+
if lIndexes.include? value
|
78
|
+
lText += " [#{counter.to_s}]"
|
79
|
+
counter += 1
|
80
|
+
else
|
81
|
+
lword = pStruct[:words][value][:word]
|
82
|
+
lText += (' ' + lword)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
lText.gsub!(' .', '.')
|
88
|
+
lText.gsub!(' ,', ',')
|
89
|
+
lText = lText[1, lText.size] if lText[0] == ' '
|
90
|
+
lText
|
91
|
+
end
|
92
|
+
|
93
|
+
def count_words(pInputText)
|
94
|
+
return 0 if pInputText.nil?
|
95
|
+
|
96
|
+
t = pInputText.clone
|
97
|
+
t.gsub!("\n"," ")
|
98
|
+
t.gsub!("/"," ")
|
99
|
+
#t.gsub!("-"," ")
|
100
|
+
t.gsub!("."," ")
|
101
|
+
t.gsub!(","," ")
|
102
|
+
t.gsub!(" "," ")
|
103
|
+
t.gsub!(" "," ")
|
104
|
+
t.split(" ").count
|
105
|
+
end
|
106
|
+
|
107
|
+
def do_mistake_to(pText = '')
|
108
|
+
lText = pText.dup
|
109
|
+
keys = @mistakes.keys
|
110
|
+
|
111
|
+
# Try to do mistake with one item from the key list
|
112
|
+
keys.shuffle!
|
113
|
+
keys.each do |key|
|
114
|
+
if lText.include? key.to_s
|
115
|
+
values = @mistakes[key].split(',')
|
116
|
+
values.shuffle!
|
117
|
+
lText = lText.sub(key.to_s,values[0].to_s)
|
118
|
+
return lText
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
# Force mistake by swapping letters
|
123
|
+
if lText.size > 1
|
124
|
+
i = rand(lText.size - 2)
|
125
|
+
aux = lText[i]
|
126
|
+
lText[i] = lText[i + 1]
|
127
|
+
lText[i + 1] = aux
|
128
|
+
end
|
129
|
+
return lText if lText != pText
|
130
|
+
|
131
|
+
lText + 's'
|
132
|
+
end
|
133
|
+
|
134
|
+
def hide_text(input_text)
|
135
|
+
input = input_text.clone
|
136
|
+
if count_words(input) < 2 && input.size < 10
|
137
|
+
output = '[*]'
|
138
|
+
else
|
139
|
+
output = ''
|
140
|
+
input.each_char do |char|
|
141
|
+
if ' !|"@#$%&/()=?¿¡+*(){}[],.-_<>'.include? char
|
142
|
+
output += char
|
143
|
+
else
|
144
|
+
output += '?'
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
output
|
149
|
+
end
|
150
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rainbow'
|
4
|
+
require 'rexml/document'
|
5
|
+
require_relative '../logger'
|
6
|
+
require_relative '../code/code'
|
7
|
+
|
8
|
+
# Read XML info about Code input data
|
9
|
+
module CodeLoader
|
10
|
+
##
|
11
|
+
# Load XML data about Code object
|
12
|
+
# @param xmldata (XML Object)
|
13
|
+
# @param filepath (String)
|
14
|
+
# @return Code object
|
15
|
+
def self.load(xmldata, filepath)
|
16
|
+
data = read_codedata_from_xml(xmldata, File.basename(filepath))
|
17
|
+
code = Code.new(File.dirname(filepath), data[:path], data[:type])
|
18
|
+
code.features << data[:features]
|
19
|
+
code
|
20
|
+
end
|
21
|
+
|
22
|
+
##
|
23
|
+
# Read Code data from XML content
|
24
|
+
# @param xmldata (XML Object)
|
25
|
+
# @param filename (String) File name that contains data
|
26
|
+
# @return Code object
|
27
|
+
def self.read_codedata_from_xml(xmldata, filename)
|
28
|
+
data = { path: '?', type: '?', features: [] }
|
29
|
+
xmldata.elements.each do |i|
|
30
|
+
data[:path] = i.text if i.name == 'path'
|
31
|
+
data[:type] = i.text.to_sym if i.name == 'type'
|
32
|
+
data[:features] << read_features(i, filename) if i.name == 'features'
|
33
|
+
end
|
34
|
+
data
|
35
|
+
end
|
36
|
+
|
37
|
+
##
|
38
|
+
# Read features data from XML input
|
39
|
+
# @param xmldata (XML object)
|
40
|
+
# @return Array with features (Strings)
|
41
|
+
def self.read_features(xmldata, filename)
|
42
|
+
features = []
|
43
|
+
xmldata.elements.each do |i|
|
44
|
+
if i.name == 'row'
|
45
|
+
features << i.text
|
46
|
+
else
|
47
|
+
msg = Rainbow("[ERROR] features/#{i.name} from #{filename}").color(:red)
|
48
|
+
Logger.verbose msg
|
49
|
+
end
|
50
|
+
end
|
51
|
+
features
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rainbow'
|
4
|
+
require 'rexml/document'
|
5
|
+
require_relative '../data/concept'
|
6
|
+
require_relative 'code_loader'
|
7
|
+
require_relative '../logger'
|
8
|
+
require_relative '../project'
|
9
|
+
|
10
|
+
# Define methods that load data from XML contents
|
11
|
+
module ContentLoader
|
12
|
+
##
|
13
|
+
# Load XML content into Asker data objects
|
14
|
+
# @param filepath (String) File path
|
15
|
+
# @param content (String) XML plane text content
|
16
|
+
def self.load(filepath, content)
|
17
|
+
concepts = []
|
18
|
+
codes = []
|
19
|
+
begin
|
20
|
+
xmlcontent = REXML::Document.new(content)
|
21
|
+
rescue REXML::ParseException
|
22
|
+
raise_error_with(filepath, content)
|
23
|
+
end
|
24
|
+
lang = read_lang_attribute(xmlcontent)
|
25
|
+
context = read_context_attribute(xmlcontent)
|
26
|
+
|
27
|
+
xmlcontent.root.elements.each do |xmldata|
|
28
|
+
case xmldata.name
|
29
|
+
when 'concept'
|
30
|
+
concepts << read_concept(xmldata, filepath, lang, context)
|
31
|
+
when 'code'
|
32
|
+
codes << read_code(xmldata, filepath)
|
33
|
+
else
|
34
|
+
Logger.verboseln Rainbow("[ERROR] Unkown tag <#{xmldata.name}>").red
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
{ concepts: concepts, codes: codes }
|
39
|
+
end
|
40
|
+
|
41
|
+
##
|
42
|
+
# Read lang attr from input XML data
|
43
|
+
# @param xmldata (XML Object)
|
44
|
+
def self.read_lang_attribute(xmldata)
|
45
|
+
begin
|
46
|
+
lang = xmldata.root.attributes['lang']
|
47
|
+
rescue StandardError
|
48
|
+
lang = Project.instance.lang
|
49
|
+
end
|
50
|
+
lang
|
51
|
+
end
|
52
|
+
|
53
|
+
##
|
54
|
+
# Read context attr from input XML data
|
55
|
+
# @param xmldata (XML Object)
|
56
|
+
def self.read_context_attribute(xmldata)
|
57
|
+
begin
|
58
|
+
context = xmldata.root.attributes['context']
|
59
|
+
rescue StandardError
|
60
|
+
context = 'unknown'
|
61
|
+
end
|
62
|
+
context
|
63
|
+
end
|
64
|
+
|
65
|
+
##
|
66
|
+
# Read concept from input XML data
|
67
|
+
# @param xmldata (XML Object)
|
68
|
+
# @param filepath (String)
|
69
|
+
# @param lang
|
70
|
+
# @param context
|
71
|
+
def self.read_concept(xmldata, filepath, lang, context)
|
72
|
+
project = Project.instance
|
73
|
+
c = Concept.new(xmldata, filepath, lang, context)
|
74
|
+
if [ File.basename(filepath), :default ].include? project.process_file
|
75
|
+
c.process = true
|
76
|
+
end
|
77
|
+
c
|
78
|
+
end
|
79
|
+
|
80
|
+
##
|
81
|
+
# Read code from input XML data
|
82
|
+
# @param xmldata (XML Object)
|
83
|
+
# @param filepath (String)
|
84
|
+
def self.read_code(xmldata, filepath)
|
85
|
+
project = Project.instance
|
86
|
+
c = CodeLoader.load(xmldata, filepath)
|
87
|
+
if [ File.basename(filepath), :default ].include? project.process_file
|
88
|
+
c.process = true
|
89
|
+
end
|
90
|
+
c
|
91
|
+
end
|
92
|
+
|
93
|
+
def self.raise_error_with(filepath, content)
|
94
|
+
msg = Rainbow("[ERROR] ContentLoader: Format error in #{filepath}").red.bright
|
95
|
+
Logger.verbose msg
|
96
|
+
f = File.open('output/error.xml', 'w')
|
97
|
+
f.write(content)
|
98
|
+
f.close
|
99
|
+
raise msg
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'file_loader'
|
4
|
+
require_relative '../logger'
|
5
|
+
|
6
|
+
# Load input data from one directory
|
7
|
+
module DirectoryLoader
|
8
|
+
##
|
9
|
+
# Load input data from directory
|
10
|
+
# @param dirname (String) Directory name
|
11
|
+
def self.load(dirname)
|
12
|
+
DirectoryLoader.check_dir(dirname)
|
13
|
+
files = (Dir.new(dirname).entries - ['.', '..']).sort
|
14
|
+
# Accept only HAML or XML files
|
15
|
+
accepted = files.select { |f| %w[.xml .haml].include? File.extname(f) }
|
16
|
+
Logger.verbose " * Input directory = #{Rainbow(dirname).bright}"
|
17
|
+
DirectoryLoader.load_files(accepted, dirname)
|
18
|
+
end
|
19
|
+
|
20
|
+
##
|
21
|
+
# Check directory
|
22
|
+
# @param dirname (String) Directory name
|
23
|
+
def self.check_dir(dirname)
|
24
|
+
return if Dir.exist? dirname
|
25
|
+
|
26
|
+
msg = Rainbow("[ERROR] #{dirname} directory dosn't exist!").color(:red)
|
27
|
+
Logger.verboseln msg
|
28
|
+
raise msg
|
29
|
+
end
|
30
|
+
|
31
|
+
##
|
32
|
+
# Load accepted files from dirname directory
|
33
|
+
# @param filenames (Array) File name list
|
34
|
+
# @param dirname (String) Base directory
|
35
|
+
def self.load_files(filenames, dirname)
|
36
|
+
output = { concepts: [], codes: [] }
|
37
|
+
filenames.each do |filename|
|
38
|
+
filepath = File.join(dirname, filename)
|
39
|
+
data = DirectoryLoader.load_file(filepath, filename == filenames.last)
|
40
|
+
output[:concepts] += data[:concepts]
|
41
|
+
output[:codes] += data[:codes]
|
42
|
+
end
|
43
|
+
output
|
44
|
+
end
|
45
|
+
|
46
|
+
##
|
47
|
+
# Load one input file
|
48
|
+
# @param filepath (String) Path to input file
|
49
|
+
# @param last (Boolean) True if it is the last filename
|
50
|
+
def self.load_file(filepath, last = false)
|
51
|
+
if last
|
52
|
+
Logger.verbose " └── Input file = #{Rainbow(filepath).bright}"
|
53
|
+
else
|
54
|
+
Logger.verbose " ├── Input file = #{Rainbow(filepath).bright}"
|
55
|
+
end
|
56
|
+
FileLoader.load(filepath)
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'haml'
|
4
|
+
require_relative 'content_loader'
|
5
|
+
require_relative '../logger'
|
6
|
+
|
7
|
+
# Methods that load a filename and return list of concepts
|
8
|
+
module FileLoader
|
9
|
+
##
|
10
|
+
# Load asker data from file
|
11
|
+
# @param filename (String) File name to be load
|
12
|
+
def self.load(filename)
|
13
|
+
if File.extname(filename).casecmp('.haml').zero?
|
14
|
+
file_content = load_haml filename
|
15
|
+
elsif File.extname(filename).casecmp('.xml').zero?
|
16
|
+
file_content = File.read(filename)
|
17
|
+
else
|
18
|
+
msg = "[ERROR] FileLoader: Format error #{filename}"
|
19
|
+
Logger.verbose msg
|
20
|
+
raise msg
|
21
|
+
end
|
22
|
+
ContentLoader.load(filename, file_content)
|
23
|
+
end
|
24
|
+
|
25
|
+
##
|
26
|
+
# Load HAML file name
|
27
|
+
# @param filename (String) HAML file name
|
28
|
+
def self.load_haml(filename)
|
29
|
+
template = File.read(filename)
|
30
|
+
haml_engine = Haml::Engine.new(template)
|
31
|
+
haml_engine.render
|
32
|
+
end
|
33
|
+
end
|