liquor 0.1.1 → 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
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