liquor 0.1.1 → 0.9.1

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 (176) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +3 -9
  5. data/Gemfile +7 -0
  6. data/Guardfile +11 -0
  7. data/MIT-LICENSE +6 -2
  8. data/README.md +4 -122
  9. data/Rakefile +20 -23
  10. data/doc/language-spec.html +768 -0
  11. data/doc/language-spec.md +698 -0
  12. data/lib/liquor.rb +39 -68
  13. data/lib/liquor/ast_tools.rb +28 -0
  14. data/lib/liquor/compiler.rb +110 -0
  15. data/lib/liquor/context.rb +76 -254
  16. data/lib/liquor/diagnostics.rb +151 -0
  17. data/lib/liquor/drop/drop.rb +168 -0
  18. data/lib/liquor/drop/drop_delegation.rb +24 -0
  19. data/lib/liquor/drop/drop_scope.rb +118 -0
  20. data/lib/liquor/drop/dropable.rb +17 -0
  21. data/lib/liquor/emitter.rb +313 -0
  22. data/lib/liquor/extensions/kaminari.rb +14 -0
  23. data/lib/liquor/extensions/pagination.rb +235 -0
  24. data/lib/liquor/extensions/rails.rb +97 -0
  25. data/lib/liquor/extensions/thinking_sphinx.rb +14 -0
  26. data/lib/liquor/extensions/tire.rb +30 -0
  27. data/lib/liquor/external.rb +79 -0
  28. data/lib/liquor/function.rb +94 -0
  29. data/lib/liquor/grammar/lexer.rb +1223 -0
  30. data/lib/liquor/grammar/lexer.rl +297 -0
  31. data/lib/liquor/grammar/parser.racc +288 -0
  32. data/lib/liquor/grammar/parser.rb +885 -0
  33. data/lib/liquor/library.rb +41 -0
  34. data/lib/liquor/manager.rb +146 -0
  35. data/lib/liquor/runtime.rb +167 -0
  36. data/lib/liquor/stdlib/builtin_functions.rb +315 -0
  37. data/lib/liquor/stdlib/builtin_tags.rb +228 -0
  38. data/lib/liquor/stdlib/html_truncater.rb +162 -0
  39. data/lib/liquor/stdlib/partial_tags.rb +76 -0
  40. data/lib/liquor/tag.rb +83 -14
  41. data/lib/liquor/version.rb +1 -1
  42. data/liquor.gemspec +29 -6
  43. data/spec/builtins_spec.rb +264 -0
  44. data/spec/compiler_spec.rb +136 -0
  45. data/spec/context_spec.rb +49 -0
  46. data/spec/drop_delegation_spec.rb +21 -0
  47. data/spec/drop_spec.rb +222 -0
  48. data/spec/errors_spec.rb +40 -0
  49. data/spec/external_spec.rb +207 -0
  50. data/spec/function_spec.rb +80 -0
  51. data/spec/lexer_spec.rb +173 -0
  52. data/spec/library_spec.rb +18 -0
  53. data/spec/manager_spec.rb +84 -0
  54. data/spec/parser_spec.rb +381 -0
  55. data/spec/partials_spec.rb +74 -0
  56. data/spec/runtime_spec.rb +97 -0
  57. data/spec/spec_helper.rb +94 -0
  58. data/spec/tag_spec.rb +7 -0
  59. metadata +216 -173
  60. data/AUTHORS +0 -2
  61. data/CHANGELOG +0 -48
  62. data/Gemfile.lock +0 -91
  63. data/History.txt +0 -44
  64. data/LICENSE +0 -23
  65. data/example/server/example_servlet.rb +0 -37
  66. data/example/server/liquid_servlet.rb +0 -28
  67. data/example/server/liquor_servlet.rb +0 -28
  68. data/example/server/server.rb +0 -12
  69. data/example/server/templates/index.liquid +0 -6
  70. data/example/server/templates/index.liquor +0 -6
  71. data/example/server/templates/products.liquid +0 -45
  72. data/example/server/templates/products.liquor +0 -45
  73. data/init.rb +0 -8
  74. data/lib/extras/liquid_view.rb +0 -51
  75. data/lib/extras/liquor_view.rb +0 -51
  76. data/lib/liquor/block.rb +0 -101
  77. data/lib/liquor/condition.rb +0 -120
  78. data/lib/liquor/document.rb +0 -17
  79. data/lib/liquor/drop.rb +0 -256
  80. data/lib/liquor/errors.rb +0 -11
  81. data/lib/liquor/extensions.rb +0 -72
  82. data/lib/liquor/file_system.rb +0 -62
  83. data/lib/liquor/htmltags.rb +0 -74
  84. data/lib/liquor/module_ex.rb +0 -60
  85. data/lib/liquor/standardfilters.rb +0 -315
  86. data/lib/liquor/strainer.rb +0 -58
  87. data/lib/liquor/tags/assign.rb +0 -33
  88. data/lib/liquor/tags/capture.rb +0 -35
  89. data/lib/liquor/tags/case.rb +0 -83
  90. data/lib/liquor/tags/comment.rb +0 -9
  91. data/lib/liquor/tags/content_for.rb +0 -54
  92. data/lib/liquor/tags/cycle.rb +0 -59
  93. data/lib/liquor/tags/for.rb +0 -136
  94. data/lib/liquor/tags/if.rb +0 -80
  95. data/lib/liquor/tags/ifchanged.rb +0 -20
  96. data/lib/liquor/tags/include.rb +0 -56
  97. data/lib/liquor/tags/unless.rb +0 -33
  98. data/lib/liquor/tags/yield.rb +0 -49
  99. data/lib/liquor/template.rb +0 -181
  100. data/lib/liquor/variable.rb +0 -52
  101. data/performance/shopify.rb +0 -92
  102. data/performance/shopify/comment_form.rb +0 -33
  103. data/performance/shopify/database.rb +0 -45
  104. data/performance/shopify/json_filter.rb +0 -7
  105. data/performance/shopify/liquid.rb +0 -18
  106. data/performance/shopify/liquor.rb +0 -18
  107. data/performance/shopify/money_filter.rb +0 -18
  108. data/performance/shopify/paginate.rb +0 -93
  109. data/performance/shopify/shop_filter.rb +0 -98
  110. data/performance/shopify/tag_filter.rb +0 -25
  111. data/performance/shopify/vision.database.yml +0 -945
  112. data/performance/shopify/weight_filter.rb +0 -11
  113. data/performance/tests/dropify/article.liquid +0 -74
  114. data/performance/tests/dropify/blog.liquid +0 -33
  115. data/performance/tests/dropify/cart.liquid +0 -66
  116. data/performance/tests/dropify/collection.liquid +0 -22
  117. data/performance/tests/dropify/index.liquid +0 -47
  118. data/performance/tests/dropify/page.liquid +0 -8
  119. data/performance/tests/dropify/product.liquid +0 -68
  120. data/performance/tests/dropify/theme.liquid +0 -105
  121. data/performance/tests/ripen/article.liquid +0 -74
  122. data/performance/tests/ripen/blog.liquid +0 -13
  123. data/performance/tests/ripen/cart.liquid +0 -54
  124. data/performance/tests/ripen/collection.liquid +0 -29
  125. data/performance/tests/ripen/index.liquid +0 -32
  126. data/performance/tests/ripen/page.liquid +0 -4
  127. data/performance/tests/ripen/product.liquid +0 -75
  128. data/performance/tests/ripen/theme.liquid +0 -85
  129. data/performance/tests/tribble/404.liquid +0 -56
  130. data/performance/tests/tribble/article.liquid +0 -98
  131. data/performance/tests/tribble/blog.liquid +0 -41
  132. data/performance/tests/tribble/cart.liquid +0 -134
  133. data/performance/tests/tribble/collection.liquid +0 -70
  134. data/performance/tests/tribble/index.liquid +0 -94
  135. data/performance/tests/tribble/page.liquid +0 -56
  136. data/performance/tests/tribble/product.liquid +0 -116
  137. data/performance/tests/tribble/search.liquid +0 -51
  138. data/performance/tests/tribble/theme.liquid +0 -90
  139. data/performance/tests/vogue/article.liquid +0 -66
  140. data/performance/tests/vogue/blog.liquid +0 -32
  141. data/performance/tests/vogue/cart.liquid +0 -58
  142. data/performance/tests/vogue/collection.liquid +0 -19
  143. data/performance/tests/vogue/index.liquid +0 -22
  144. data/performance/tests/vogue/page.liquid +0 -3
  145. data/performance/tests/vogue/product.liquid +0 -62
  146. data/performance/tests/vogue/theme.liquid +0 -122
  147. data/test/assign_test.rb +0 -11
  148. data/test/block_test.rb +0 -58
  149. data/test/capture_test.rb +0 -41
  150. data/test/condition_test.rb +0 -115
  151. data/test/content_for_test.rb +0 -15
  152. data/test/context_test.rb +0 -479
  153. data/test/drop_test.rb +0 -162
  154. data/test/error_handling_test.rb +0 -89
  155. data/test/extra/breakpoint.rb +0 -547
  156. data/test/extra/caller.rb +0 -80
  157. data/test/file_system_test.rb +0 -30
  158. data/test/filter_test.rb +0 -147
  159. data/test/helper.rb +0 -24
  160. data/test/html_tag_test.rb +0 -31
  161. data/test/if_else_test.rb +0 -139
  162. data/test/include_tag_test.rb +0 -129
  163. data/test/module_ex_test.rb +0 -89
  164. data/test/output_test.rb +0 -121
  165. data/test/parsing_quirks_test.rb +0 -54
  166. data/test/regexp_test.rb +0 -45
  167. data/test/security_test.rb +0 -41
  168. data/test/standard_filter_test.rb +0 -170
  169. data/test/standard_tag_test.rb +0 -405
  170. data/test/statements_test.rb +0 -137
  171. data/test/strainer_test.rb +0 -27
  172. data/test/template_test.rb +0 -82
  173. data/test/test_helper.rb +0 -28
  174. data/test/unless_else_test.rb +0 -27
  175. data/test/variable_test.rb +0 -173
  176. data/test/yield_test.rb +0 -24
@@ -0,0 +1,151 @@
1
+ module Liquor
2
+ class Diagnostic < StandardError
3
+ attr_reader :location
4
+ attr_reader :raw_message
5
+
6
+ def initialize(message, location=nil)
7
+ location_info = ""
8
+ if location
9
+ if location.include? :file
10
+ location_info << "`#{location[:file]}'"
11
+ end
12
+
13
+ if location.include? :line
14
+ location_info << ": line #{location[:line] + 1}"
15
+ if location.include? :start
16
+ location_info << ", column #{location[:start] + 1}"
17
+ end
18
+ end
19
+ end
20
+
21
+ @location = location
22
+ @raw_message = message
23
+
24
+ if location_info.empty?
25
+ super(message)
26
+ else
27
+ super("#{message} at #{location_info}")
28
+ end
29
+ end
30
+
31
+ def error?
32
+ false
33
+ end
34
+
35
+ def decorate(source)
36
+ if @location && @location.has_key?(:line)
37
+ line = source.lines.drop(@location[:line]).first
38
+ line ||= "(source spans only #{source.lines.count} lines)"
39
+ line = line.rstrip
40
+
41
+ if @location.has_key? :start
42
+ start_col = tabify_column line, @location[:start]
43
+ pointer = " " * start_col
44
+
45
+ if location.has_key? :end
46
+ end_col = tabify_column line, @location[:end]
47
+
48
+ pointer += "^" * (end_col - start_col + 1)
49
+ else
50
+ pointer += "^"
51
+ end
52
+ end
53
+ end
54
+
55
+ [ line, pointer ].compact
56
+ end
57
+
58
+ # Dammit, why should I replicate parts of VT-52 all over again?!
59
+ # Curse backwards compatibility and C.
60
+ def tabify_column(line, column)
61
+ display_column = 0
62
+
63
+ line[0...column].each_char do |char|
64
+ if char == "\t"
65
+ display_column += 8 - (display_column % 8)
66
+ else
67
+ display_column += 1
68
+ end
69
+ end
70
+
71
+ display_column
72
+ end
73
+
74
+ def as_json(options = nil)
75
+ {
76
+ message: @raw_message,
77
+ is_error: error?,
78
+ location: @location,
79
+ }
80
+ end
81
+
82
+ def to_json(options = nil)
83
+ as_json.to_json(options)
84
+ end
85
+ end
86
+
87
+ class Error < Diagnostic
88
+ def error?
89
+ true
90
+ end
91
+ end
92
+
93
+ class SyntaxError < Error
94
+ end
95
+
96
+ class PartialError < Error
97
+ end
98
+
99
+ class ArgumentError < Error
100
+ end
101
+
102
+ class NameError < Error
103
+ end
104
+
105
+ class TypeError < Error
106
+ end
107
+
108
+ class ArgumentTypeError < Error
109
+ attr_reader :location
110
+
111
+ def initialize(message, location=nil)
112
+ location_info = ""
113
+ if location
114
+ if location.include? :function
115
+ location_info << "function `#{location[:function]}'"
116
+ if location.include? :argument
117
+ if location[:argument].nil?
118
+ location_info << ", unnamed argument"
119
+ else
120
+ location_info << ", argument `#{location[:argument]}'"
121
+ end
122
+ end
123
+ end
124
+ end
125
+
126
+ if location_info.empty?
127
+ super(message, location)
128
+ else
129
+ super("#{message} at #{location_info}", location)
130
+ end
131
+ end
132
+ end
133
+
134
+ class HostError < Error
135
+ attr_reader :original_error, :host_backtrace
136
+
137
+ def initialize(message, original_error, host_backtrace, location=nil)
138
+ @original_error = original_error
139
+ @host_backtrace = host_backtrace
140
+
141
+ if message.nil?
142
+ message = "#{original_error.class}: #{original_error.message}"
143
+ end
144
+
145
+ super(message, location)
146
+ end
147
+ end
148
+
149
+ class Deprecation < Diagnostic
150
+ end
151
+ end
@@ -0,0 +1,168 @@
1
+ require 'active_record'
2
+
3
+ module Liquor
4
+ class Drop
5
+ include Liquor::External
6
+
7
+ attr_reader :source
8
+
9
+ def self.inherited(klass)
10
+ klass.instance_exec do
11
+ const_set :Scope, Class.new(::Liquor::Drop::Scope)
12
+ const_get(:Scope).instance_exec do
13
+ export # refresh liquor_exports
14
+ end
15
+ end
16
+ end
17
+
18
+ def initialize(source)
19
+ @source = source
20
+ end
21
+
22
+ def self.attributes(*attrs)
23
+ attrs.each do |attr|
24
+ define_method(attr) {
25
+ @source.send(attr)
26
+ }
27
+ export attr
28
+ end
29
+ end
30
+
31
+ def self.scopes(*scopes)
32
+ const_get(:Scope).instance_exec do
33
+ scopes.each do |scope|
34
+ define_method(scope) { |*args|
35
+ args = Drop.unwrap_scope_arguments(args)
36
+ DropDelegation.wrap_scope @source.send(scope, *args)
37
+ }
38
+ export scope
39
+ end
40
+ end
41
+ end
42
+
43
+ def self.unwrap_scope_arguments(args)
44
+ args.map do |arg|
45
+ case arg
46
+ when Drop
47
+ arg.id
48
+ when Drop::Scope
49
+ unwrap_scope_arguments(arg.to_a)
50
+ when Array
51
+ unwrap_scope_arguments(arg)
52
+ when Hash
53
+ Hash[arg.keys.zip(unwrap_scope_arguments(arg.values))]
54
+ else
55
+ arg
56
+ end
57
+ end
58
+ end
59
+
60
+ def self.define_singular_association_accessor(name, options={})
61
+ if options.has_key?(:if) && options.has_key?(:unless)
62
+ raise ArgumentError, "It is pointless to pass both :if and :unless conditions to has_one or belongs_to"
63
+ end
64
+
65
+ unsupported_options = (options.keys - [:if, :unless])
66
+ if unsupported_options.any?
67
+ raise ArgumentError, "Unsupported options #{unsupported_options.join(", ")}"
68
+ end
69
+
70
+ define_method(name) {
71
+ value = @source.send(name)
72
+
73
+ if self.class.check_singular_condition(value, options)
74
+ if value.nil?
75
+ nil
76
+ else
77
+ DropDelegation.wrap_element(value)
78
+ end
79
+ end
80
+ }
81
+
82
+ export name
83
+ end
84
+
85
+ def self.check_singular_condition(object, options)
86
+ execute = lambda do |object, condition|
87
+ if condition.is_a? Symbol
88
+ object.send(condition)
89
+ elsif condition.respond_to? :call
90
+ condition.(object)
91
+ else
92
+ raise ArgumentError, "Invalid condition type #{condition.class}"
93
+ end
94
+ end
95
+
96
+ if options[:if]
97
+ execute.(object, options[:if])
98
+ elsif options[:unless]
99
+ !execute.(object, options[:unless])
100
+ else
101
+ true
102
+ end
103
+ end
104
+
105
+ def self.belongs_to(name, options={})
106
+ define_singular_association_accessor(name, options)
107
+ end
108
+
109
+ def self.has_one(name, options={})
110
+ define_singular_association_accessor(name, options)
111
+ end
112
+
113
+ def self.has_many(name, options={})
114
+ unsupported_options = (options.keys - [:scope, :include])
115
+ if unsupported_options.any?
116
+ raise ArgumentError, "Unsupported options #{unsupported_options.join(", ")}"
117
+ end
118
+
119
+ define_method(name) {
120
+ value = @source.send(name)
121
+
122
+ if options[:scope]
123
+ # Sequentally apply each symbol from options[:scope]
124
+ # to the current scope, starting from `value' and using
125
+ # each result as current scope for the next operation.
126
+ value = Array(options[:scope]).reduce(value, &:send)
127
+ end
128
+
129
+ if options[:include]
130
+ Array(options[:include]).each do |collection|
131
+ value = value.includes(collection)
132
+ end
133
+ end
134
+
135
+ DropDelegation.wrap_scope(value, value.klass)
136
+ }
137
+
138
+ export name
139
+ end
140
+
141
+ def entity
142
+ @source.class.model_name.to_s
143
+ end
144
+ export :entity
145
+
146
+ def ==(other)
147
+ if other.is_a? Liquor::Drop
148
+ self.source == other.source
149
+ else
150
+ other == self
151
+ end
152
+ end
153
+
154
+ alias eql? ==
155
+
156
+ def hash
157
+ @source.hash
158
+ end
159
+
160
+ def to_drop
161
+ self
162
+ end
163
+ end
164
+ end
165
+
166
+ require_relative 'drop_delegation'
167
+ require_relative 'drop_scope'
168
+ require_relative 'dropable'
@@ -0,0 +1,24 @@
1
+ module Liquor
2
+ module DropDelegation
3
+ # aka module_method for every method.
4
+ extend self
5
+
6
+ def wrap_scope(scope, klass=scope)
7
+ drop_klass = "#{klass.name}Drop::Scope".constantize
8
+ drop_klass.new(scope)
9
+ end
10
+
11
+ def wrap_element(element)
12
+ unless element.nil?
13
+ drop_klass = "#{element.class.name}Drop".constantize
14
+ drop_klass.new(element)
15
+ end
16
+ end
17
+
18
+ def unwrap_drop_class(klass)
19
+ drop_klass_name = klass.name
20
+ model_class_name = drop_klass_name.sub(/Drop$/, '')
21
+ model_class_name.constantize
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,118 @@
1
+ module Liquor
2
+ class Drop::Scope
3
+ include Liquor::External
4
+
5
+ attr_reader :source
6
+
7
+ def initialize(source)
8
+ unless source.respond_to? :each
9
+ source = source.all
10
+ end
11
+
12
+ @source = source
13
+ end
14
+
15
+ # Not exported.
16
+ def to_a
17
+ @source.map do |elem|
18
+ DropDelegation.wrap_element(elem)
19
+ end
20
+ end
21
+ alias to_ary to_a
22
+
23
+ def entity
24
+ @source.model_name.to_s
25
+ end
26
+ export :entity
27
+
28
+ def find_by(_, fields={})
29
+ fields, = Drop.unwrap_scope_arguments([ fields ])
30
+
31
+ result = @source.where(fields).first
32
+ DropDelegation.wrap_element result if result
33
+ end
34
+
35
+ def find_all_by(_, fields={})
36
+ fields, = Drop.unwrap_scope_arguments([ fields ])
37
+
38
+ result = @source.where(fields)
39
+ DropDelegation.wrap_scope(result)
40
+ end
41
+
42
+ export :find_all_by, :find_by
43
+
44
+ def except(record)
45
+ record, = Drop.unwrap_scope_arguments([ record ])
46
+
47
+ result = @source.where(@source.arel_table[:id].eq(record).not)
48
+ DropDelegation.wrap_scope(result)
49
+ end
50
+
51
+ def find_except_by(_, fields={})
52
+ fields, = Drop.unwrap_scope_arguments([ fields ])
53
+
54
+ result = @source.
55
+ where(fields.map do |key, value|
56
+ @source.arel_table[key].eq(value)
57
+ end.reduce(&:and).not)
58
+ DropDelegation.wrap_scope(result)
59
+ end
60
+
61
+ export :except, :find_except_by
62
+
63
+ def first
64
+ DropDelegation.wrap_element @source.first
65
+ end
66
+
67
+ def last
68
+ DropDelegation.wrap_element @source.last
69
+ end
70
+
71
+ def [](index)
72
+ DropDelegation.wrap_element @source[index]
73
+ end
74
+
75
+ # Not exported. No block support in Liquor.
76
+ def each
77
+ @source.each do |elem|
78
+ yield DropDelegation.wrap_element(elem)
79
+ end
80
+ end
81
+
82
+ # Not exported. No ? in names in Liquor.
83
+ def include?(elem)
84
+ if elem.is_a? Liquor::Drop
85
+ @source.include? elem.source
86
+ else
87
+ false
88
+ end
89
+ end
90
+
91
+ def pluck(attribute)
92
+ @source.map do |elem|
93
+ DropDelegation.wrap_element(elem).liquor_send(attribute.to_s, [])
94
+ end
95
+ end
96
+
97
+ def count
98
+ @source.count
99
+ end
100
+ alias size count # for builtin compatibility
101
+
102
+ export :first, :last, :[], :pluck
103
+
104
+ def limit(count)
105
+ DropDelegation.wrap_scope @source.limit(count)
106
+ end
107
+
108
+ def offset(count)
109
+ DropDelegation.wrap_scope @source.offset(count)
110
+ end
111
+
112
+ def reverse
113
+ DropDelegation.wrap_scope @source.reverse_order
114
+ end
115
+
116
+ export :limit, :offset, :reverse
117
+ end
118
+ end