opulent 0.0.9 → 1.0.2

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.
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