breezy_template 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1034948b51df6045cd4ebed1fec5278d578b6232
4
- data.tar.gz: 37f2073cb2c299d22fa2d12b0b022d34d531ed7e
3
+ metadata.gz: 5615a0d5186cccdec50df4bc728c6d472f877254
4
+ data.tar.gz: ae3d4643bfa89ebbf2d38fd285b74ee909a73b06
5
5
  SHA512:
6
- metadata.gz: ceab85197a0d71c53c3b4ac25263bc392fcf700b1a23e88f5dcb8a79f6634b7bc6d6e93e10d3ff0f2222a3c5d8b3345e02b66978d44e452463ae53aa05c567a4
7
- data.tar.gz: 7fc963e83b28dfdbb7a2216815e7e3319089c7bb6745ffe92f3e37d583490d492adc8607cc097b21c905052d5077ba5a6994474f60c28773315be42c0659fc64
6
+ metadata.gz: 4e639ce8e936ebb85324b90830425f005b3f17d7e1366ddb2f9d1d0a096cf4275e56b64ccc0c64aa32c66c365f94c6ca1c7e933a7ea71c50ef35101940f1d7d8
7
+ data.tar.gz: 30b7c0e630b346d037447f26ca3a5265c8d0839c42c3218060fcbd37e2b5dd431819e7ab27136789b202dd8295794fb49d74d4f17c0c5178b74648867ce7805c
@@ -1,7 +1,9 @@
1
- require 'jbuilder'
2
- require 'digest/md5'
3
- require 'action_view'
4
- require 'active_support'
1
+ require 'breezy_template/breezy_template'
2
+
3
+ require 'breezy_template/blank'
4
+ require 'breezy_template/key_formatter'
5
+ require 'breezy_template/errors'
6
+
5
7
  require 'breezy_template/active_support'
6
8
  require 'breezy_template/digestor'
7
9
  require 'breezy_template/configuration'
@@ -11,258 +13,330 @@ require 'breezy_template/cache_extension'
11
13
  require 'breezy_template/deferment_extension'
12
14
  require 'breezy_template/search_extension'
13
15
 
14
- module BreezyTemplate
15
- class Engine < ::Rails::Engine
16
- initializer :breezy_template do |app|
17
- ActiveSupport.on_load(:action_view) do
18
- ActionView::Template.register_template_handler :breezy, BreezyTemplate::Handler
19
- ActionView::Template.register_template_handler :props, BreezyTemplate::Handler
20
- require 'breezy_template/dependency_tracker'
21
- end
22
- end
23
- end
16
+ require 'digest/md5'
17
+ require 'action_view'
18
+ require 'active_support'
19
+ require 'multi_json'
20
+ require 'ostruct'
24
21
 
25
- class Template < ::Jbuilder
26
- include PartialDigestor
27
- prepend PartialExtension
22
+ class BreezyTemplate
23
+ include PartialDigestor
28
24
 
29
- prepend CacheExtension
30
- prepend SearchExtension
31
- prepend DefermentExtension
25
+ prepend PartialExtension
26
+ prepend CacheExtension
27
+ prepend SearchExtension
28
+ prepend DefermentExtension
32
29
 
33
- class << self
34
- attr_accessor :template_lookup_options
35
- end
30
+ class << self
31
+ attr_accessor :template_lookup_options
32
+ end
36
33
 
37
- self.template_lookup_options = { handlers: [:breezy] }
34
+ self.template_lookup_options = { handlers: [:breezy, :props] }
38
35
 
39
- def initialize(context, *args)
40
- @context = context
41
- @js = []
42
- @path = []
43
- @joints = {}
44
- @extensions = {}
45
- super(*args)
46
- end
36
+ @@key_formatter = nil
37
+ @@ignore_nil = false
47
38
 
48
- def empty!
49
- attributes = @attributes
50
- @attributes = {}
51
- attributes
52
- end
39
+ def initialize(context, options = {})
40
+ @context = context
41
+ @js = []
42
+ @path = []
43
+ @joints = {}
53
44
 
54
- def wrap!(name, *args)
55
- @extensions[name.to_sym] = args
56
- end
45
+ @attributes = {}
46
+ @key_formatter = options.fetch(:key_formatter){ @@key_formatter ? @@key_formatter.clone : nil}
47
+ @ignore_nil = options.fetch(:ignore_nil, @@ignore_nil)
57
48
 
58
- def _blank
59
- BLANK
60
- end
49
+ yield self if ::Kernel.block_given?
50
+ end
61
51
 
62
- def method_missing(*args)
63
- key = args[0]
64
- @path.push(key)
65
- if ::Kernel.block_given?
66
- args = _args_for_set_with_block(*args)
67
- set!(*args, &::Proc.new)
52
+ # Yields a builder and automatically turns the result into a JSON string
53
+ def self.encode(*args, &block)
54
+ new(*args, &block).target!
55
+ end
56
+
57
+ BLANK = Blank.new
58
+ NON_ENUMERABLES = [ ::Struct, ::OpenStruct ].to_set
59
+
60
+ def set!(key, value = BLANK, *args)
61
+ result = if ::Kernel.block_given?
62
+ _result(value, *args, &::Proc.new)
63
+ else
64
+ if _is_collection?(value) && !args.last.is_a?(::Hash)
65
+ _scope{ array! value, *args }
66
+ # elsif args.empty?
67
+ # _result(value, *args)
68
+ # elsif !args.last.is_a? ::Hash
69
+ # _merge_block(key){ extract! value, *args }
68
70
  else
69
- args = _args_for_set(*args)
70
- set!(*args)
71
+ _result(value, *args)
72
+ # value
71
73
  end
72
- ensure
73
- # ::Byebug.byebug
74
- @extensions = {}
75
- @path.pop
76
74
  end
77
75
 
78
- def _scope
79
- parent_extensions = @extensions
80
- @extensions = {}
81
- parent_attributes, parent_formatter = @attributes, @key_formatter
82
- @attributes = BLANK
83
- yield
84
- @attributes
85
- ensure
86
- # ::Byebug.byebug
87
- @extensions = parent_extensions
88
- @attributes, @key_formatter = parent_attributes, parent_formatter
89
- end
90
-
91
- def set!(key, value = BLANK, *args)
92
- _ensure_valid_key(key)
93
- result = if ::Kernel.block_given?
94
- _result(value, &::Proc.new)
95
- else
96
- _result(value)
97
- end
76
+ _set_value key, result
77
+ end
98
78
 
99
- _set_value key, result
79
+ def _result(value, *args)
80
+ if ::Kernel.block_given?
81
+ _scope { yield self }
82
+ elsif ::BreezyTemplate === value
83
+ # json.age 32
84
+ # json.person another_jbuilder
85
+ # { "age": 32, "person": { ... }
86
+ value.attributes!
87
+ else
88
+ # json.age 32
89
+ # { "age": 32 }
90
+ value
100
91
  end
92
+ end
101
93
 
102
- def _ensure_valid_key(key)
103
- current_value = _blank? ? _blank : @attributes.fetch(_key(key), _blank)
104
- raise NullError.build(key) if current_value.nil?
94
+ def method_missing(*args)
95
+ key = args[0]
96
+ @path.push(key)
97
+ if ::Kernel.block_given?
98
+ args = _args_for_set_with_block(*args)
99
+ set!(*args, &::Proc.new)
100
+ else
101
+ args = _args_for_set(*args)
102
+ set!(*args)
105
103
  end
104
+ ensure
105
+ @path.pop
106
+ end
106
107
 
107
- def _result(value)
108
- if ::Kernel.block_given?
109
- _scope { yield self }
110
- elsif ::Jbuilder === value
111
- # json.age 32
112
- # json.person another_jbuilder
113
- # { "age": 32, "person": { ... }
114
- value.attributes!
115
- else
116
- # json.age 32
117
- # { "age": 32 }
118
- value
119
- end
120
- end
108
+ def key_format!(*args)
109
+ @key_formatter = KeyFormatter.new(*args)
110
+ end
121
111
 
122
- def _set_value(key, result)
123
- super
124
- end
112
+ # Same as the instance method key_format! except sets the default.
113
+ def self.key_format(*args)
114
+ @@key_formatter = KeyFormatter.new(*args)
115
+ end
116
+
117
+ def empty!
118
+ attributes = @attributes
119
+ @attributes = {}
120
+ attributes
121
+ end
125
122
 
126
- def array!(collection = [], *attributes)
127
- options = attributes.first || {}
128
- options = _normalize_options(options)
123
+ def ignore_nil!(value = true)
124
+ @ignore_nil = value
125
+ end
129
126
 
130
- collection = [] if collection.nil?
131
- collection = _prepare_collection_for_map(collection)
127
+ # Same as instance method ignore_nil! except sets the default.
128
+ def self.ignore_nil(value = true)
129
+ @@ignore_nil = value
130
+ end
131
+
132
+ def child!
133
+ @attributes = [] unless ::Array === @attributes
134
+ @attributes << _scope{ yield self }
135
+ end
132
136
 
133
- array = if ::Kernel.block_given?
134
- _map_collection(collection, options, &::Proc.new)
135
- elsif attributes.any?
136
- _map_collection(collection, options) { |element| extract! element, *attributes }
137
- else
138
- collection.to_a
139
- end
140
137
 
141
- @extensions = {}
142
- merge! array #remove this depednacy
138
+ def array!(collection = [], *attributes)
139
+ options = attributes.first || {}
140
+
141
+ collection = [] if collection.nil?
142
+ collection = _prepare_collection_for_map(collection)
143
+ array = if ::Kernel.block_given?
144
+ _map_collection(collection, options, &::Proc.new)
145
+ # elsif attributes.any?
146
+ # if (!attributes.last.is_a? ::Hash)
147
+ # _map_collection(collection) { |element|
148
+ # extract! element, *attributes
149
+ # }
150
+ # else
151
+ # _map_collection(collection, options) { |element|
152
+ # extract! element, *attributes
153
+ # }
154
+ # end
155
+ else
156
+ collection.to_a
143
157
  end
144
158
 
145
- def target!
146
- js = _breezy_return(@attributes)
159
+ merge! array #remove this depednacy
160
+ end
147
161
 
148
- @js.push(js)
149
- "(function(){var joints={};var cache={};var defers=[];#{@js.join}})()"
162
+ def extract!(object, *attributes)
163
+ if ::Hash === object
164
+ _extract_hash_values(object, attributes)
165
+ else
166
+ _extract_method_values(object, attributes)
150
167
  end
168
+ end
151
169
 
152
- private
153
- def _merge_values(current_value, updates)
154
- # Always use the new values. This is because cached values
155
- # are no longer a Ruby object. They are JS values and can't
156
- # be merged.
170
+ # def call(object, *attributes)
171
+ # if ::Kernel.block_given?
172
+ # array! object, &::Proc.new
173
+ # else
174
+ # extract! object, *attributes
175
+ # end
176
+ # end
177
+
178
+ # Returns the nil JSON.
179
+ def nil!
180
+ @attributes = nil
181
+ end
157
182
 
158
- updates
159
- end
183
+ alias_method :null!, :nil!
160
184
 
161
- def _prepare_collection_for_map(collection)
162
- collection
163
- end
185
+ def attributes!
186
+ @attributes
187
+ end
164
188
 
165
- def _args_for_set_with_block(*args)
166
- key = args[0]
167
- #todo: check this
168
- if ::Hash === args[1] && _extended_options?(args[1])
169
- options = args[1]
170
- [key, BLANK, options]
171
- else
172
- [key, BLANK]
173
- end
174
- end
189
+ def target!
190
+ js = _breezy_return(@attributes)
175
191
 
176
- def _args_for_set(*args)
177
- if args.length == 3
178
- key, value, options = args
192
+ @js.push(js)
193
+ "(function(){var joints={};var cache={};var defers=[];#{@js.join}})()"
194
+ end
179
195
 
180
- [key, value, options]
181
- else
182
- key, value = args
196
+ # Merges hash or array into current builder.
197
+ # No longer works on Breezy
198
+ def merge!(hash_or_array)
199
+ @attributes = _merge_values(@attributes, hash_or_array)
200
+ end
183
201
 
184
- [key, value]
185
- end
186
- end
202
+ private
187
203
 
188
- def _extended_options?(value)
189
- false
190
- end
204
+ def _extract_hash_values(object, attributes)
205
+ attributes.each{ |key| _set_value key, object.fetch(key) }
206
+ end
191
207
 
192
- def _mapping_element(element, options)
193
- _scope { yield element }
194
- end
208
+ def _extract_method_values(object, attributes)
209
+ attributes.each{ |key| _set_value key, object.public_send(key) }
210
+ end
195
211
 
196
- def _map_collection(collection, options)
197
- @path.push(nil)
198
- collection.map.with_index do |element, i|
199
- if options.has_key? :key
200
- id_name = options[:key]
201
- id_val = element[id_name]
202
- @path[-1] = "#{id_name}=#{id_val}"
203
- else
204
- @path[-1] = i
205
- end
206
-
207
- _mapping_element(element, options, &::Proc.new)
208
-
209
- end - [BLANK]
210
- ensure
211
- @path.pop
212
- end
212
+ def _merge_block(key)
213
+ current_value = _blank? ? BLANK : @attributes.fetch(_key(key), BLANK)
214
+ raise NullError.build(key) if current_value.nil?
215
+ new_value = _scope{ yield self }
216
+ _merge_values(current_value, new_value)
217
+ end
213
218
 
219
+ def _merge_values(current_value, updates)
220
+ # Always use the new values. This is because cached values
221
+ # are no longer a Ruby object. They are JS values and can't
222
+ # be merged.
214
223
 
215
- def _breezy_return(results)
216
- "return (#{_dump(results)});"
217
- end
224
+ updates
225
+ end
218
226
 
219
- def _dump(value)
220
- ::MultiJson.dump(value)
221
- end
227
+ def _key(key)
228
+ @key_formatter ? @key_formatter.format(key) : key.to_s
229
+ end
222
230
 
223
- def _normalize_options(options)
224
- options = options.dup
225
- key = options[:cache]
226
- opts = {}
227
- return options if !key
228
-
229
- if ::Array === key && key.length == 2 && ::Hash === key.last
230
- return options
231
- end
232
-
233
- if options[:partial]
234
- opts[:partial] = options[:partial]
235
- end
236
-
237
- key = case key
238
- when ::Array
239
- key
240
- when ::Proc
241
- key
242
- else
243
- [key]
244
- end
245
-
246
- options[:cache] = [key, opts]
247
- options
248
- end
231
+ def _set_value(key, value)
232
+ raise NullError.build(key) if @attributes.nil?
233
+ raise ArrayError.build(key) if ::Array === @attributes
234
+ return if @ignore_nil && value.nil? or _blank?(value)
235
+ @attributes = {} if _blank?
236
+ @attributes[_key(key)] = value
237
+ end
238
+
239
+ def _prepare_collection_for_map(collection)
240
+ collection
241
+ end
249
242
 
250
- def _fragment_name_with_digest(key, options)
251
- if @context.respond_to?(:cache_fragment_name)
252
- # Current compatibility, fragment_name_with_digest is private again and cache_fragment_name
253
- # should be used instead.
254
- @context.cache_fragment_name(key, options)
255
- elsif @context.respond_to?(:fragment_name_with_digest)
256
- # Backwards compatibility for period of time when fragment_name_with_digest was made public.
257
- @context.fragment_name_with_digest(key)
258
- else
259
- key
260
- end
243
+ def _map_collection(collection, options = {})
244
+ @path.push(nil)
245
+ collection.map.with_index do |element, i|
246
+ if options.has_key? :key
247
+ id_name = options[:key]
248
+ id_val = element[id_name]
249
+ @path[-1] = "#{id_name}=#{id_val}"
250
+ else
251
+ @path[-1] = i
261
252
  end
262
253
 
254
+ _mapping_element(element, options, &::Proc.new)
255
+
256
+ end - [BLANK]
257
+ ensure
258
+ @path.pop
259
+ end
260
+
261
+ def _mapping_element(element, options)
262
+ _scope { yield element }
263
+ end
264
+
265
+ def _scope
266
+ parent_attributes, parent_formatter = @attributes, @key_formatter
267
+ @attributes = BLANK
268
+ yield
269
+ @attributes
270
+ ensure
271
+ @attributes, @key_formatter = parent_attributes, parent_formatter
272
+ end
273
+
274
+ def _is_collection?(object)
275
+ _object_respond_to?(object, :map, :count) && NON_ENUMERABLES.none?{ |klass| klass === object }
276
+ end
263
277
 
264
- def _logger
265
- ::ActionView::Base.logger
278
+ def _blank?(value=@attributes)
279
+ BLANK == value
280
+ end
281
+
282
+ def _blank
283
+ BLANK
284
+ end
285
+
286
+ def _object_respond_to?(object, *methods)
287
+ methods.all?{ |m| object.respond_to?(m) }
288
+ end
289
+
290
+ def _args_for_set_with_block(*args)
291
+ # return args
292
+ key = args[0]
293
+ # #todo: check this
294
+ # #
295
+ if ::Hash === args[1] && _extended_options?(args[1])
296
+ options = args[1]
297
+ [key, BLANK, options]
298
+ else
299
+ [key, BLANK]
300
+ end
301
+ end
302
+
303
+ def _args_for_set(*args)
304
+ return args
305
+ # if args.length >= 3
306
+ # return args
307
+ # # key, value, options = args
308
+ # #
309
+ # # [key, value, options]
310
+ # else
311
+ # key, value = args
312
+ #
313
+ # [key, value]
314
+ # end
315
+ end
316
+
317
+ def _extended_options?(value)
318
+ false
319
+ end
320
+
321
+ def _breezy_return(results)
322
+ "return (#{_dump(results)});"
323
+ end
324
+
325
+ def _dump(value)
326
+ ::MultiJson.dump(value)
327
+ end
328
+
329
+ def _logger
330
+ ::ActionView::Base.logger
331
+ end
332
+
333
+ class Engine < ::Rails::Engine
334
+ initializer :breezy_template do |app|
335
+ ActiveSupport.on_load(:action_view) do
336
+ ActionView::Template.register_template_handler :breezy, BreezyTemplate::Handler
337
+ ActionView::Template.register_template_handler :props, BreezyTemplate::Handler
338
+ require 'breezy_template/dependency_tracker'
266
339
  end
340
+ end
267
341
  end
268
342
  end