opulent 0.0.9 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (83) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +9 -0
  3. data/.libold/opulent.rb +96 -0
  4. data/.libold/opulent/context.rb +80 -0
  5. data/.libold/opulent/engine.rb +88 -0
  6. data/.libold/opulent/filter.rb +101 -0
  7. data/.libold/opulent/logger.rb +67 -0
  8. data/.libold/opulent/nodes.rb +13 -0
  9. data/.libold/opulent/nodes/block.rb +29 -0
  10. data/.libold/opulent/nodes/comment.rb +35 -0
  11. data/.libold/opulent/nodes/control.rb +230 -0
  12. data/.libold/opulent/nodes/define.rb +42 -0
  13. data/.libold/opulent/nodes/eval.rb +41 -0
  14. data/.libold/opulent/nodes/expression.rb +28 -0
  15. data/.libold/opulent/nodes/filter.rb +70 -0
  16. data/.libold/opulent/nodes/helper.rb +69 -0
  17. data/.libold/opulent/nodes/node.rb +101 -0
  18. data/.libold/opulent/nodes/root.rb +62 -0
  19. data/.libold/opulent/nodes/text.rb +54 -0
  20. data/.libold/opulent/nodes/theme.rb +36 -0
  21. data/.libold/opulent/parser.rb +252 -0
  22. data/.libold/opulent/parser/block.rb +70 -0
  23. data/.libold/opulent/parser/comment.rb +32 -0
  24. data/.libold/opulent/parser/control.rb +83 -0
  25. data/.libold/opulent/parser/define.rb +39 -0
  26. data/.libold/opulent/parser/eval.rb +33 -0
  27. data/.libold/opulent/parser/expression.rb +350 -0
  28. data/.libold/opulent/parser/filter.rb +41 -0
  29. data/.libold/opulent/parser/node.rb +232 -0
  30. data/.libold/opulent/parser/root.rb +96 -0
  31. data/.libold/opulent/parser/text.rb +114 -0
  32. data/.libold/opulent/parser/theme.rb +36 -0
  33. data/.libold/opulent/preprocessor.rb +102 -0
  34. data/.libold/opulent/runtime.rb +144 -0
  35. data/.libold/opulent/template.rb +43 -0
  36. data/.libold/opulent/tokens.rb +276 -0
  37. data/.libold/opulent/version.rb +5 -0
  38. data/.rspec +2 -0
  39. data/.travis.yml +4 -0
  40. data/Gemfile +2 -0
  41. data/LICENSE +21 -0
  42. data/README.md +102 -0
  43. data/Rakefile +6 -0
  44. data/benchmark/benchmark.rb +46 -0
  45. data/benchmark/cases/node/node.haml +17 -0
  46. data/benchmark/cases/node/node.op +14 -0
  47. data/benchmark/cases/node/node.slim +19 -0
  48. data/bin/console +14 -0
  49. data/bin/setup +7 -0
  50. data/docs/syntax.md +3 -0
  51. data/docs/usage.md +3 -0
  52. data/lib/opulent.rb +11 -64
  53. data/lib/opulent/compiler.rb +132 -0
  54. data/lib/opulent/compiler/block.rb +31 -0
  55. data/lib/opulent/compiler/comment.rb +22 -0
  56. data/lib/opulent/compiler/control.rb +152 -0
  57. data/lib/opulent/compiler/define.rb +88 -0
  58. data/lib/opulent/compiler/eval.rb +15 -0
  59. data/lib/opulent/compiler/filter.rb +54 -0
  60. data/lib/opulent/compiler/node.rb +232 -0
  61. data/lib/opulent/compiler/root.rb +19 -0
  62. data/lib/opulent/compiler/text.rb +60 -0
  63. data/lib/opulent/context.rb +88 -0
  64. data/lib/opulent/engine.rb +60 -0
  65. data/lib/opulent/filters.rb +222 -0
  66. data/lib/opulent/logger.rb +47 -0
  67. data/lib/opulent/parser.rb +196 -0
  68. data/lib/opulent/parser/block.rb +56 -0
  69. data/lib/opulent/parser/comment.rb +27 -0
  70. data/lib/opulent/parser/control.rb +112 -0
  71. data/lib/opulent/parser/define.rb +30 -0
  72. data/lib/opulent/parser/eval.rb +21 -0
  73. data/lib/opulent/parser/expression.rb +344 -0
  74. data/lib/opulent/parser/filter.rb +25 -0
  75. data/lib/opulent/parser/node.rb +246 -0
  76. data/lib/opulent/parser/root.rb +48 -0
  77. data/lib/opulent/parser/text.rb +127 -0
  78. data/lib/opulent/settings.rb +79 -0
  79. data/lib/opulent/template.rb +67 -0
  80. data/lib/opulent/tokens.rb +166 -0
  81. data/lib/opulent/version.rb +4 -0
  82. data/opulent.gemspec +36 -0
  83. metadata +160 -7
@@ -0,0 +1,19 @@
1
+ # @Opulent
2
+ module Opulent
3
+ # @Compiler
4
+ class Compiler
5
+ # Generate code for all nodes by calling the method with their type name
6
+ #
7
+ # @param current [Array] Current node data with options
8
+ # @param indent [Fixnum] Indentation size for current node
9
+ # @param context [Context] Context holding environment variables
10
+ #
11
+ def root(current, indent, context)
12
+ if Keywords.include? current[@type]
13
+ send :"#{current[@type]}_node", current, indent, context
14
+ else
15
+ send current[@type], current, indent, context
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,60 @@
1
+ # @Opulent
2
+ module Opulent
3
+ # @Compiler
4
+ class Compiler
5
+ # Generate the code for a standard text node
6
+ #
7
+ # @param node [Array] Node code generation data
8
+ # @param indent [Fixnum] Size of the indentation to be added
9
+ # @param context [Context] Processing environment data
10
+ #
11
+ def plain(node, indent, context)
12
+ indentation = " " * indent
13
+
14
+ inline = @inline_node.include? @node_stack.last
15
+
16
+ # Evaluate text node if it's marked as such and print nodes in the
17
+ # current context
18
+ if node[@value] == :text
19
+ if node[@options][:evaluate]
20
+ value = context.evaluate "\"#{node[@options][:value]}\""
21
+ else
22
+ value = node[@options][:value]
23
+ end
24
+ else
25
+ value = if node[@options][:value] == 'yield'
26
+ context.evaluate_yield
27
+ else
28
+ context.evaluate(node[@options][:value])
29
+ end
30
+ end
31
+
32
+ # Indent all the lines with the given indentation
33
+ value = indent_lines value, indentation
34
+
35
+ # If the last node was an inline node, we remove the trailing newline
36
+ # character and we left strip the value
37
+ #pp @node_stack
38
+ if @node_stack.last == :text
39
+ remove_trailing_newline
40
+ value = " " + value.lstrip
41
+ elsif inline
42
+ remove_trailing_newline
43
+ value.lstrip!
44
+ end
45
+ value.rstrip!
46
+
47
+ # Escape the value unless explicitly set to false
48
+ value = node[@options][:escaped] ? escape(value) : value
49
+
50
+ # Create the text tag to be added
51
+ text_tag = "#{value}"
52
+ text_tag += "\n"
53
+
54
+ # Set the current child node as last processed node
55
+ @node_stack << :text
56
+
57
+ @code += text_tag
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,88 @@
1
+ # @Opulent
2
+ module Opulent
3
+ # @Context
4
+ #
5
+ # The context class is used to differentiate local, instance and class variables
6
+ # and to define the current working environment. Each class, method and instance
7
+ # has its own context
8
+ #
9
+ class Context
10
+ attr_accessor :binding, :name, :parent
11
+
12
+ # Create a context from the environment binding, extended with the locals
13
+ # given as arguments
14
+ #
15
+ # @param locals [Hash] Binding extension
16
+ # @param bind [Binding] Call environment binding
17
+ #
18
+ def initialize(locals = {}, &block)
19
+ @block = block || Proc.new
20
+ @binding = if @block
21
+ @block.binding.clone
22
+ else
23
+ Binding.new
24
+ end
25
+
26
+ extend_locals locals
27
+ end
28
+
29
+ # Evaluate ruby code in current context
30
+ #
31
+ # @param code [String] Code to be evaluated
32
+ #
33
+ def evaluate(code)
34
+ begin
35
+ eval code, @binding
36
+ rescue NameError => variable
37
+ Compiler.error :binding, variable, code
38
+ end
39
+ end
40
+
41
+ # Call given input block and return the output
42
+ #
43
+ def evaluate_yield
44
+ @block.call
45
+ end
46
+
47
+ # Extend the call context with a Hash, String or other Object
48
+ #
49
+ # @param context [Object] Extension object
50
+ #
51
+ def extend_locals(locals)
52
+ # Create new local variables from the input hash
53
+ locals.each do |key, value|
54
+ begin
55
+ @binding.local_variable_set(key.to_sym, value)
56
+ rescue NameError => variable
57
+ Compiler.error :variable_name, variable, key
58
+ end
59
+ end
60
+ end
61
+
62
+ # Extend instance, class and global variables for use in definitions
63
+ #
64
+ # @param bind [Binding] Binding to extend current context binding
65
+ #
66
+ def extend_nonlocals(bind)
67
+ bind.eval('instance_variables').each do |var|
68
+ @binding.eval('self').instance_variable_set var, bind.eval(var.to_s)
69
+ end
70
+
71
+ bind.eval('self.class.class_variables').each do |var|
72
+ @binding.eval('self').class_variable_set var, bind.eval(var.to_s)
73
+ end
74
+
75
+ bind.eval('self.class.constants').each do |var|
76
+ @binding.eval('self').const_set var, bind.eval(var.to_s)
77
+ end
78
+ end
79
+ end
80
+
81
+
82
+ # @Binding
83
+ class Binding
84
+ def self.new
85
+ binding
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,60 @@
1
+ # @Opulent
2
+ module Opulent
3
+ # Module method wrapper for creating a new engine instance
4
+ #
5
+ def Opulent.new(settings = nil)
6
+ return Engine.new settings
7
+ end
8
+
9
+ # @Engine
10
+ class Engine
11
+ attr_reader :nodes, :preamble, :buffer
12
+
13
+ def initialize(settings = nil)
14
+ # Update render settings
15
+ Settings.update_settings settings if settings
16
+ end
17
+
18
+ # Analyze the input code and check for matching tokens. In case no match was
19
+ # found, throw an exception. In special cases, modify the token hash.
20
+ #
21
+ # @param file [String] The file that needs to be analyzed
22
+ # @param locals [Hash] Render call local variables
23
+ # @param block [Proc] Processing environment data
24
+ #
25
+ def render_file(file, locals = {}, &block)
26
+ # Render the file
27
+ render File.read(file), locals, &block
28
+ end
29
+
30
+ # Analyze the input code and check for matching tokens. In case no match was
31
+ # found, throw an exception. In special cases, modify the token hash.
32
+ #
33
+ # @param file [String] The file that needs to be analyzed
34
+ # @param locals [Hash] Render call local variables
35
+ # @param block [Proc] Processing environment data
36
+ #
37
+ def render(code, locals = {}, &block)
38
+ # Get the nodes tree
39
+ @nodes = Parser.new.parse code
40
+
41
+ # @TODO
42
+ # Implement precompiled template handling
43
+ @preamble = @nodes.inspect.inspect
44
+
45
+ # Create a new context based on our rendering environment
46
+ @context = Context.new locals, &block
47
+
48
+ # Compile our syntax tree using input context
49
+ @output = Compiler.new.compile @nodes, @context
50
+
51
+ # puts "Nodes\n---\n"
52
+ # pp @nodes
53
+ #
54
+ # puts "\n\nCode\n---\n"
55
+ # puts @output
56
+
57
+ return @output
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,222 @@
1
+ # @Opulent
2
+ module Opulent
3
+ # @Filters
4
+ module Filters
5
+ # @Singleton
6
+ class << self
7
+ attr_accessor :filters
8
+
9
+ # Add a new Opulent filter to the filters knowledgebase
10
+ #
11
+ # @param class [Class] Class to be used for filter instance
12
+ # @param name [Symbol] Identifier in the filters hash
13
+ # @param options [Hash] Filter engine instance options
14
+ #
15
+ def register(klass, name, options)
16
+ @filters ||= {}
17
+ @filters[name] = klass.new name, options
18
+ end
19
+
20
+ # Check if the chosen filter name is registed within our knowledgebase
21
+ #
22
+ def has_filter?(name)
23
+ @filters.has_key? name
24
+ end
25
+ end
26
+
27
+ # @Filter
28
+ class Filter
29
+ attr_accessor :name, :options, :loaded
30
+
31
+ # Set tag and attribute options for filter
32
+ #
33
+ def initialize(name, options)
34
+ @name = name
35
+ @options = options
36
+ @loaded = false
37
+ end
38
+
39
+ # Error output in case the filter does not exist
40
+ #
41
+ def load_filter
42
+ unless gem_name.nil? || @loaded
43
+ # Try to load the library associated to the chosen filter
44
+ begin
45
+ require gem_name
46
+ @loaded = true
47
+ rescue LoadError => error
48
+ # Error output with filter name and installation instructions
49
+ Compiler.error :filter_load, @name, install_error
50
+ end
51
+ end
52
+ end
53
+
54
+ # Error message to be shown in order to provide installation instructions
55
+ # for the developer
56
+ #
57
+ def install_error
58
+ "gem install #{gem_name}"
59
+ end
60
+
61
+ # Process input code using this filter and return the output to the
62
+ # evaluation method from the Filter Node
63
+ #
64
+ def render(code, options = {})
65
+ raise NoMethodError
66
+ end
67
+
68
+ # RubyGems name for explicit library require
69
+ #
70
+ def gem_name
71
+ raise NoMethodError
72
+ # "gem_name"
73
+ end
74
+
75
+ # After defining how to render the code,
76
+ #
77
+ # Filters.register self, :filter, tag: :tag, attributes: { type: 'text/css' }
78
+ end
79
+
80
+
81
+ # Add the default registered rendering filters for Opulent
82
+
83
+ # @CoffeeScript
84
+ class CoffeeScript < Filter
85
+ def render(code, options = {})
86
+ ::CoffeeScript.compile code, options
87
+ end
88
+
89
+ def gem_name
90
+ "coffee-script"
91
+ end
92
+
93
+ Filters.register self, :coffeescript, tag: :script, attributes: { type: 'javascript' }
94
+ end
95
+
96
+ # @JavaScript
97
+ class JavaScript < Filter
98
+ def render(code, options = {})
99
+ code
100
+ end
101
+
102
+ def gem_name
103
+ nil
104
+ end
105
+
106
+ Filters.register self, :javascript, tag: :script, attributes: { type: 'javascript' }
107
+ end
108
+
109
+ # @Scss
110
+ class Scss < Filter
111
+ def render(code, options = {})
112
+ options[:style] ||= :expanded
113
+
114
+ ::Sass.compile code, options
115
+ end
116
+
117
+ def gem_name
118
+ "sass"
119
+ end
120
+
121
+ Filters.register self, :scss, tag: :style, attributes: { type: 'text/css' }
122
+ end
123
+
124
+ # @Sass
125
+ class Sass < Filter
126
+ def render(code, options = {})
127
+ options[:syntax] = :sass
128
+ options[:style] ||= :expanded
129
+
130
+ ::Sass.compile code, options
131
+ end
132
+
133
+ def gem_name
134
+ "sass"
135
+ end
136
+
137
+ Filters.register self, :sass, tag: :style, attributes: { type: 'text/css' }
138
+ end
139
+
140
+ # @Css
141
+ class Css < Filter
142
+ def render(code, options = {})
143
+ if options[:cdata]
144
+ "<![CDATA[\n" + code + "]]>"
145
+ else
146
+ code
147
+ end
148
+ end
149
+
150
+ def gem_name
151
+ nil
152
+ end
153
+
154
+ Filters.register self, :css, tag: :style, attributes: { type: 'text/css' }
155
+ end
156
+
157
+ # @CData
158
+ class CData < Filter
159
+ def render(code, options = {})
160
+ "<![CDATA[\n" + code + "]]>"
161
+ end
162
+
163
+ def gem_name
164
+ nil
165
+ end
166
+
167
+ Filters.register self, :cdata, tag: nil, attributes: {}
168
+ end
169
+
170
+ # @Escaped
171
+ class Escaped < Filter
172
+ def render(code, options = {})
173
+ Compiler.escape code
174
+ end
175
+
176
+ def gem_name
177
+ nil
178
+ end
179
+
180
+ Filters.register self, :escaped, tag: nil, attributes: {}
181
+ end
182
+
183
+ # @Markdown
184
+ class Markdown < Filter
185
+ def render(code, options = {})
186
+ ::Kramdown::Document.new(code, options).to_html
187
+ end
188
+
189
+ def gem_name
190
+ "kramdown"
191
+ end
192
+
193
+ Filters.register self, :markdown, tag: nil, attributes: {}
194
+ end
195
+
196
+ # @Maruku
197
+ class Maruku < Filter
198
+ def render(code, options = {})
199
+ ::Maruku.new(code, options).to_html
200
+ end
201
+
202
+ def gem_name
203
+ "maruku"
204
+ end
205
+
206
+ Filters.register self, :maruku, tag: nil, attributes: {}
207
+ end
208
+
209
+ # @RedCloth
210
+ class RedCloth < Filter
211
+ def render(code, options = {})
212
+ ::RedCloth.new(code, options).to_html
213
+ end
214
+
215
+ def gem_name
216
+ "RedCloth"
217
+ end
218
+
219
+ Filters.register self, :textile, tag: nil, attributes: {}
220
+ end
221
+ end
222
+ end