breezy_template 0.2.0 → 0.3.0

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