tequila 0.2.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.
@@ -0,0 +1,30 @@
1
+ #-*- coding: utf-8 -*-
2
+
3
+ require 'treetop'
4
+ require 'tequila'
5
+ require 'preprocessor'
6
+ require 'tree'
7
+
8
+ module Tequila
9
+ class JazzHandler < ActionView::TemplateHandler
10
+ include ActionView::TemplateHandlers::Compilable
11
+
12
+ def compile(template)
13
+ <<CODE.gsub('\n',';')
14
+ controller.response.content_type = Mime::JSON
15
+ src = ::TequilaPreprocessor.run(template.source)
16
+ _tequila_out =::TequilaParser.new.parse(src).eval(binding).build_hash.to_json
17
+ CODE
18
+ end
19
+
20
+ def cache_fragment(block, name = {}, options = nil)
21
+ @view.fragment_for(block, name, options) do
22
+ eval("_tequila_out", block.binding)
23
+ end
24
+ end
25
+
26
+ end
27
+ end
28
+
29
+ ActionView::Template.register_template_handler :jazz, Tequila::JazzHandler
30
+ ActionView::Template.exempt_from_layout(/.jazz$/)
@@ -0,0 +1,315 @@
1
+ require 'ostruct'
2
+
3
+ module Tequila
4
+
5
+ class Config
6
+
7
+ class Default
8
+ @@show_initial_label = false
9
+
10
+ def self.show_initial_label!
11
+ @@show_initial_label = true
12
+ end
13
+
14
+ def self.hide_initial_label!
15
+ @@show_initial_label = false
16
+ end
17
+
18
+ def self.show_initial_label?
19
+ @@show_initial_label
20
+ end
21
+ end
22
+
23
+ attr_reader :show_initial_label
24
+
25
+ def show_initial_label!
26
+ @show_initial_label = true
27
+ end
28
+
29
+ def hide_initial_label!
30
+ @show_initial_label = false
31
+ end
32
+
33
+ def initialize
34
+ @show_initial_label = Default.show_initial_label?
35
+ end
36
+
37
+ end
38
+
39
+ class Tree
40
+
41
+ attr_reader :root
42
+ attr_accessor :config
43
+
44
+ def initialize
45
+ @root = OpenStruct.new({:name => :root})
46
+ @tree = { @root => [] }
47
+ @config = Tequila::Config.new
48
+ end
49
+
50
+ def add_child_to(branch, child)
51
+ @tree[branch] ||= []
52
+ @tree[branch] << child
53
+ end
54
+
55
+ def parent_for?(node)
56
+ @tree.keys.select {|n| @tree[n].include?(node)}[0]
57
+ end
58
+
59
+ def nodes
60
+ @tree.values.inject([]){ |res, nodes| res + nodes }.uniq
61
+ end
62
+
63
+ def build_hash
64
+ res = @tree[root].inject({}) do |out, n|
65
+ out.merge(build_hash_with_context(n, n.content.call))
66
+ end
67
+ ((res.values.first.kind_of? Array) && !config.show_initial_label) ?
68
+ res.values.first : res
69
+ end
70
+
71
+ def build_hash_with_context(node, context)
72
+ if context.kind_of? Array
73
+ { node.label =>
74
+ context.map do |elementary_context|
75
+ build_hash_with_context(node, elementary_context)
76
+ end
77
+ }
78
+ elsif @tree[node]
79
+ node_value = node.apply(context).merge(
80
+ @tree[node].inject({}) do |out, n|
81
+ new_context = n.content.call(context)
82
+ if new_context.nil?
83
+ out
84
+ else
85
+ if n.bounded?
86
+ out.merge(build_hash_with_context(n, new_context).values.first)
87
+ else
88
+ out.merge(build_hash_with_context(n, new_context))
89
+ end
90
+ end
91
+ end)
92
+ node.suppress_label ?
93
+ node_value :
94
+ { node.label.singularize => node_value }
95
+ else
96
+ node.suppress_label ?
97
+ node.apply(context) :
98
+ { node.label.singularize => node.apply(context) }
99
+ end
100
+ end
101
+
102
+ def to_s
103
+ nodes.inject('') do |res, node|
104
+ res <<
105
+ node.to_s <<
106
+ (node.bounded? ? " bounded_to: " : " parent: ") <<
107
+ "#{parent_for?(node).name}\n"
108
+ end
109
+ end
110
+ end
111
+
112
+ class Node
113
+
114
+ attr_accessor :methods
115
+ attr_accessor :attributes
116
+ attr_accessor :code_blocks
117
+ attr_accessor :label
118
+ attr_accessor :suppress_label
119
+ attr_accessor :statics
120
+ attr_reader :name
121
+ attr_reader :content
122
+ attr_reader :type
123
+
124
+ # it is used for attributes if 'all' keyword was specified
125
+ attr_reader :pick_all
126
+ attr_reader :drop_all
127
+
128
+ class ImpreciseAttributesDeclarationError < StandardError; end
129
+ class NoAttributeError < StandardError; end
130
+
131
+ class CodeBlock
132
+ attr_reader :label
133
+ attr_reader :code
134
+
135
+ def initialize(label, code)
136
+ @label = label
137
+ @code = code
138
+ end
139
+
140
+ def to_s
141
+ label + code + "\n"
142
+ end
143
+ end
144
+
145
+ class Method
146
+ attr_accessor :label
147
+ attr_accessor :params
148
+ attr_reader :name
149
+
150
+ def initialize(name)
151
+ @name = name
152
+ @label = name
153
+ @params = []
154
+ end
155
+
156
+ def to_s
157
+ name +
158
+ (params.empty? ? '' : "(#{params * ','})") +
159
+ (name == label ? '' : " => #{label}")
160
+ end
161
+ end
162
+
163
+ class Static
164
+ attr_accessor :label
165
+ attr_accessor :value
166
+
167
+ def initialize label, value
168
+ @label = label
169
+ @value = value
170
+ end
171
+
172
+ def to_s
173
+ "#{label}: #{value}"
174
+ end
175
+ end
176
+
177
+ class Attribute
178
+ attr_accessor :label
179
+ attr_reader :name
180
+
181
+ def initialize(name)
182
+ @name = name
183
+ @label = name
184
+ end
185
+
186
+ def to_s
187
+ name == label ?
188
+ name : "#{name}(#{label}) "
189
+ end
190
+
191
+ end
192
+
193
+ #====================================#
194
+ # main class #
195
+ #====================================#
196
+
197
+ def initialize(name, type, bounded = false)
198
+ @name = name
199
+ @label = @name.gsub(/[@\$]/,'')
200
+ @type = type
201
+ @methods = []
202
+ @attributes = { :only => [], :except => []}
203
+ @code_blocks = []
204
+ @statics = []
205
+ @suppress_label = false
206
+ end
207
+
208
+ def eval(vars)
209
+ @content = case type
210
+ when :variable
211
+ lambda { Kernel.eval name, vars }
212
+ when :association, :bounded
213
+ lambda { |context| context.send(name.intern)}
214
+ end
215
+ self
216
+ end
217
+
218
+ def apply(context)
219
+ mapping = {}
220
+ if context.respond_to?(:attributes) && !drop_all
221
+
222
+ if (pick_all || drop_all)
223
+ unless attributes[:only].empty? && attributes[:except].empty?
224
+ raise ImpreciseAttributesDeclarationError, "declaration conflict"
225
+ end
226
+ end
227
+
228
+ context_attributes = context.attributes
229
+ #p "All: #{context_attributes.keys.inspect}"
230
+ if attributes[:only].size > 0
231
+ chosen_attributes = attributes[:only].map(&:name)
232
+ #p "Chosen: #{chosen_attributes}"
233
+ unless (foreign_attributes = (chosen_attributes.to_set - context_attributes.keys.to_set)).empty?
234
+ raise NoAttributeError, "can't find attributes: #{foreign_attributes.to_a.join(', ')}"
235
+ end
236
+ attributes[:only].inject({}) do |res, att|
237
+ res[att.label] = context_attributes[att.name]
238
+ res
239
+ end
240
+ elsif attributes[:except].size > 0
241
+
242
+ ignored_attributes = attributes[:except].map(&:name)
243
+ #p "Ignored: #{ignored_attributes.inspect}"
244
+ unless (foreign_attributes = (ignored_attributes.to_set - context_attributes.keys.to_set)).empty?
245
+ raise NoAttributeError, "can't find attributes: #{foreign_attributes.to_a.join(', ')}"
246
+ end
247
+ context_attributes.delete_if { |att_name, _| ignored_attributes.include?(att_name) }
248
+ else
249
+ # use all variables by default if they are supported
250
+ context_attributes
251
+ end
252
+ else
253
+ {}
254
+ end.merge(
255
+ (methods || []).inject({}) do |res, m|
256
+ res[m.label] = context.send(m.name.intern, *(m.params.map {|p| context.instance_eval p}))
257
+ res
258
+ end
259
+ ).merge(
260
+ code_blocks.inject({}) do |res, cb|
261
+ res[cb.label] = context.instance_eval cb.code
262
+ res
263
+ end
264
+ ).merge(
265
+ statics.inject({}) do |res, s|
266
+ res[s.label] = s.value
267
+ res
268
+ end
269
+ )
270
+ end
271
+
272
+ def add_attribute(key, attr)
273
+ raise ImpreciseAttributesDeclarationError if (pick_all || drop_all)
274
+ unless @attributes[key].include?(attr)
275
+ @attributes[key] << attr
276
+ end
277
+ end
278
+
279
+ def no_attributes!
280
+ if pick_all
281
+ raise ImpreciseAttributesDeclarationError, "declaration conflict"
282
+ else
283
+ @drop_all = true
284
+ end
285
+ end
286
+
287
+ def all_attributes!
288
+ if drop_all
289
+ raise ImpreciseAttributesDeclarationError, "declaration conflict"
290
+ else
291
+ @pick_all = true
292
+ end
293
+ end
294
+
295
+ def bounded?
296
+ :bounded == type
297
+ end
298
+
299
+ def to_s
300
+ "Node: " <<
301
+ ((name == label) ? name : "#{name}(#{label})") <<
302
+ (methods.empty? ? ' ' : " Methods: #{methods.map(&:to_s).join(',')} ") <<
303
+ (if attributes[:except].size > 0
304
+ "Except: #{attributes[:except].to_s}"
305
+ elsif attributes[:only].size > 0
306
+ "Only: #{attributes[:only].to_s}"
307
+ else
308
+ ''
309
+ end) <<
310
+ ((code_blocks.size > 0) ? "Code blocks: \n" << code_blocks.map(&:to_s).join : '')
311
+ end
312
+ end
313
+
314
+ end
315
+
@@ -0,0 +1,25 @@
1
+ require 'benchmark'
2
+ require 'test/test_helper'
3
+
4
+ module TequilaBenchmark
5
+ def TequilaBenchmark.run(count = 500)
6
+ humans = Human.all
7
+ src = <<END
8
+ -humans
9
+ :except
10
+ .id
11
+ +pets
12
+ :only
13
+ .id
14
+ +toys
15
+ END
16
+ Benchmark.bm(18) do |x|
17
+ x.report('to_json') { (1..count).each { json = humans.to_json(:except => :id, :include => { :pets => { :include => :toys, :only => :id }}) } }
18
+ x.report('jazz') { (1..count).each { json = TequilaParser.new.parse(TequilaPreprocessor.run(src)).eval(binding).build_hash.to_json } }
19
+ x.report('jazz with preparse') {
20
+ tree = TequilaParser.new.parse(TequilaPreprocessor.run(src))
21
+ (1..count).each { json = tree.eval(binding).build_hash.to_json }
22
+ }
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,3 @@
1
+ demo:
2
+ adapter: sqlite3
3
+ database: demo.sqlite3
@@ -0,0 +1,12 @@
1
+ nem:
2
+ name: Ivan
3
+ address: district1
4
+ sletix:
5
+ name: Oleg
6
+ address: district2
7
+ ex:
8
+ name: Eugene
9
+ address: district3
10
+ aaa:
11
+ name: Alex
12
+ address: district4
@@ -0,0 +1,9 @@
1
+ cat:
2
+ class_name: cat
3
+ dog:
4
+ class_name: dog
5
+ fish:
6
+ class_name: fish
7
+
8
+
9
+
@@ -0,0 +1,19 @@
1
+ skooby:
2
+ name: Skooby Doo
3
+ pet_type: dog
4
+ human: nem
5
+ toys: hum, con
6
+ poiyo:
7
+ name: Poiyo
8
+ pet_type: fish
9
+ human: ex
10
+ toys: lhc, hum
11
+ barbos:
12
+ name: Pes Barbos
13
+ pet_type: dog
14
+ human: sletix
15
+ toys: grz, con
16
+ matroskin:
17
+ name: Matroskin
18
+ pet_type: cat
19
+ human: aaa
@@ -0,0 +1,12 @@
1
+ lhc:
2
+ label: LHC
3
+ price: 1000000.0
4
+ hum:
5
+ label: Humanity
6
+ price: 10
7
+ con:
8
+ label: continents
9
+ price: 1000
10
+ grz:
11
+ label: gravizapa
12
+ price: 9999
@@ -0,0 +1,76 @@
1
+ require 'rubygems'
2
+ require 'activerecord'
3
+ require 'sqlite3'
4
+
5
+ # setup db connection
6
+
7
+ ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + 'debug.log')
8
+ ActiveRecord::Base.configurations = YAML::load(IO.read(File.dirname(__FILE__) + '/database.yml'))
9
+ ActiveRecord::Base.establish_connection('demo')
10
+
11
+ # setup db layout
12
+
13
+ ActiveRecord::Schema.define :version => 0 do
14
+ create_table :humans, :force => true do |t|
15
+ t.string :name
16
+ t.string :address
17
+ end
18
+
19
+ create_table :pets, :force => true do |t|
20
+ t.string :name
21
+ t.integer :pet_type_id
22
+ t.integer :human_id
23
+ end
24
+
25
+ create_table :pet_types, :force => true do |t|
26
+ t.string :class_name
27
+ end
28
+
29
+ create_table :toys, :force => true do |t|
30
+ t.string :label
31
+ t.float :price
32
+ end
33
+
34
+ create_table :pets_toys, :force => true, :id => false do |t|
35
+ t.integer :toy_id
36
+ t.integer :pet_id
37
+ end
38
+
39
+ end
40
+
41
+ # define models
42
+
43
+ class Human < ActiveRecord::Base
44
+ set_table_name :humans
45
+ has_many :pets
46
+ end
47
+
48
+ class Pet < ActiveRecord::Base
49
+ has_and_belongs_to_many :toys
50
+ belongs_to :pet_type
51
+ belongs_to :human
52
+ def which(first = 'big', second = 'cheerful')
53
+ "#{name} is #{first} and #{second}"
54
+ end
55
+ end
56
+
57
+ class PetType < ActiveRecord::Base
58
+ end
59
+
60
+ class Toy < ActiveRecord::Base
61
+ has_and_belongs_to_many :pets
62
+
63
+ def railsize(*strs)
64
+ strs.join.gsub('PHP','Ruby').gsub('Django','Rails')
65
+ end
66
+ end
67
+
68
+ # load fixtures
69
+
70
+ require 'active_record/fixtures'
71
+
72
+ Fixtures.reset_cache
73
+ fixtures_folder = File.dirname(__FILE__) + '/fixtures'
74
+ fixtures = Dir[File.join(fixtures_folder, '*.yml')].map {|f| File.basename(f, '.yml') }
75
+ Fixtures.create_fixtures(fixtures_folder, fixtures)
76
+