glue 0.22.0 → 0.23.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.
@@ -0,0 +1,438 @@
1
+ require 'mega/synchash'
2
+ require 'mega/syncarray'
3
+
4
+ require 'glue/attribute'
5
+ require 'glue/flexob'
6
+
7
+ module Glue
8
+
9
+ # Ruby attributes are typeless and generally this is good.
10
+ # Some times we need extra metadata though, for example in
11
+ # relational mapping, or web form population.
12
+ #
13
+ # Only Fixnums, Strings, Floats, Times, Booleans are
14
+ # converted.
15
+ #
16
+ # The default = methods do not force the types. A special
17
+ # __force_set method should be used instead.
18
+ #--
19
+ # TODO:
20
+ # Perhaps a sync is needed in evals (!!!!)
21
+ #++
22
+
23
+ class Property
24
+
25
+ # If set to true, perform type checking on property set.
26
+ # Useful when debugging.
27
+
28
+ cattr_accessor :type_checking, false
29
+
30
+ # The symbol of the property.
31
+
32
+ attr_accessor :symbol
33
+
34
+ # The class of the property.
35
+
36
+ attr_accessor :klass
37
+
38
+ # Additional metadata (like sql declaration, sql index, etc)
39
+ # Here is a list of predefined metadata:
40
+ #
41
+ # [+:reader+]
42
+ # create reader?
43
+ #
44
+ # [+:writer+]
45
+ # create writer?
46
+ #
47
+ # [+:sql_index+]
48
+ # create an sql index for the column this poperty maps to?
49
+ #
50
+ # You can use this mechanism to add your own, custom,
51
+ # metadata.
52
+
53
+ attr_accessor :metadata
54
+
55
+ # Support legacy code, will be deprecated.
56
+
57
+ alias_method :meta, :metadata
58
+ alias_method :meta=, :metadata=
59
+
60
+ def initialize(symbol, klass, metadata = {})
61
+ @symbol, @klass = symbol, klass
62
+ @metadata = metadata
63
+ end
64
+
65
+ def ==(other)
66
+ return @symbol == other.symbol
67
+ end
68
+
69
+ def to_s
70
+ return @symbol.to_s
71
+ end
72
+ end
73
+
74
+ # A collection of Property related utility methods.
75
+
76
+ module PropertyUtils
77
+
78
+ # Add accessors to the properties to the given target
79
+ # (Module or Class). For simplicity also create the
80
+ # meta accessors.
81
+ #
82
+ # [+target+]
83
+ # The target class or module
84
+ #--
85
+ # gmosx: Perhaps we 'll optimize this in the future.
86
+ #++
87
+
88
+ def self.enchant(target, force = false)
89
+ unless target.instance_variables.include?('@__props')
90
+ # FIXME: should be thread safe here!
91
+ target.instance_variable_set('@__meta', Flexob.new)
92
+ target.instance_variable_set('@__props', SyncArray.new)
93
+
94
+ # gmosx: Ruby surprises and amazes me! We are in the Metaclass
95
+ # when defining methods and attributes so @__props is really
96
+ # a class scoped variable that unlike @@__props is not shared
97
+ # through the hierarchy.
98
+
99
+ target.module_eval %{
100
+ def self.__props
101
+ @__props
102
+ end
103
+
104
+ def self.properties
105
+ @__props
106
+ end
107
+
108
+ def self.__props=(props)
109
+ @__props = props
110
+ end
111
+
112
+ def self.__meta
113
+ @__meta
114
+ end
115
+
116
+ def self.__meta=(meta)
117
+ @__meta = meta
118
+ end
119
+
120
+ def self.metadata
121
+ @__meta
122
+ end
123
+ }
124
+
125
+ if target.is_a?(Class)
126
+
127
+ # Add some extra code to append features to
128
+ # subclasses.
129
+
130
+ target.module_eval %{
131
+ def self.inherited(child)
132
+ Glue::PropertyUtils.enchant(child)
133
+ Glue::PropertyUtils.copy_props(self, child)
134
+ # gmosx: We have to define @@__props first to avoid
135
+ # reusing the hash from the module. super must stay
136
+ # at the end.
137
+ super
138
+ end
139
+ }
140
+
141
+ else
142
+
143
+ # Add some extra code for modules to append
144
+ # their features to classes that include it.
145
+
146
+ target.module_eval %{
147
+ def self.append_features(base)
148
+ # gmosx: We have to define @@__props first to avoid
149
+ # reusing the hash from the module. super must stay
150
+ # at the end.
151
+ Glue::PropertyUtils.copy_features(self, base)
152
+ super
153
+ end
154
+ }
155
+
156
+ end
157
+ end
158
+ end
159
+
160
+ # Copy properties from src (Module or Class) to dest.
161
+
162
+ def self.copy_props(src, dest)
163
+ src.__props.each do |p|
164
+ add_prop(dest, p)
165
+ end
166
+
167
+ # copy the metadata.
168
+ src.__meta.each do |k, val|
169
+ if val.is_a?(TrueClass)
170
+ dest.__meta[k] = val
171
+ else
172
+ dest.__meta[k] = val.dup
173
+ end
174
+ # val.each { |v| dest.meta(k, v) } if val
175
+ end
176
+ end
177
+
178
+ # Add the property to the target (Class or Module)
179
+
180
+ def self.add_prop(target, prop)
181
+ if idx = target.__props.index(prop)
182
+ # override in case of duplicates. Keep the order of the props.
183
+ target.__props[idx] = prop
184
+ else
185
+ target.__props << prop
186
+ end
187
+
188
+ # Store the property in the :props_and_relations
189
+ # metadata array.
190
+
191
+ target.meta :props_and_relations, prop
192
+
193
+ # Precompile the property read/write methods
194
+
195
+ s, klass = prop.symbol, prop.klass
196
+
197
+ if prop.meta[:reader]
198
+ target.module_eval %{
199
+ def #{s}
200
+ return @#{s}
201
+ end
202
+ }
203
+ end
204
+
205
+ # gmosx: __force_xxx reuses xxx= to allow for easier
206
+ # overrides.
207
+
208
+ if prop.meta[:writer]
209
+ target.module_eval %{
210
+ #{prop_setter(prop)}
211
+
212
+ def __force_#{s}(val)
213
+ self.#{s}=(} + case klass.name
214
+ when Fixnum.name
215
+ "val.to_i()"
216
+ when String.name
217
+ "val.to_s()"
218
+ when Float.name
219
+ "val.to_f()"
220
+ when Time.name
221
+ "Time.parse(val.to_s())"
222
+ when TrueClass.name, FalseClass.name
223
+ "val.to_i() > 0"
224
+ else
225
+ "val"
226
+ end + %{)
227
+ end
228
+ }
229
+ end
230
+ end
231
+
232
+ # Generates the property setter code. Can be overriden
233
+ # to support extra functionality (example: markup)
234
+
235
+ def self.prop_setter(prop)
236
+ s = prop.symbol
237
+
238
+ code = %{
239
+ def #{s}=(val)
240
+ }
241
+
242
+ if Glue::Property.type_checking
243
+ code << %{
244
+ unless #{prop.klass} == val.class
245
+ raise "Invalid type, expected '#{prop.klass}', is '\#\{val.class\}'."
246
+ end
247
+ }
248
+ end
249
+
250
+ code << %{
251
+ @#{s} = val
252
+ end
253
+ }
254
+
255
+ return code
256
+ end
257
+
258
+ # Get the property metadata for the given symbol.
259
+
260
+ def self.get_prop(klass, sym)
261
+ return klass.__props.find { |p| p.symbol == sym }
262
+ end
263
+
264
+ # Include meta-language mixins
265
+
266
+ def self.include_meta_mixins(target)
267
+ target.module_eval %{ include Glue::Validation } if defined?(Glue::Validation)
268
+ # gmosx: TODO, make Og::MetaLanguage equivalent to Validation.
269
+ # target.module_eval %{ extend Og::MetaLanguage } if defined?(Og::MetaLanguage)
270
+ target.module_eval %{ include Glue::Aspects } if defined?(Glue::Aspects)
271
+ target.send(:include, Og::EntityMixin) if defined?(Og::EntityMixin)
272
+ end
273
+
274
+ def self.copy_features(this, other)
275
+ Glue::PropertyUtils.enchant(other)
276
+ Glue::PropertyUtils.copy_props(this, other)
277
+ Glue::PropertyUtils.include_meta_mixins(other)
278
+ end
279
+
280
+ # Resolves the parameters passed to the propxxx macros
281
+ # to generate the meta, klass and symbols variables. This
282
+ # way the common functionality is factored out.
283
+ #
284
+ # [+params+]
285
+ # The params to resolve.
286
+ # [+one_symbol+]
287
+ # If true, only resolves one symbol (used in prop).
288
+
289
+ def self.resolve_prop_params(*params)
290
+ meta = {}
291
+ klass = Object
292
+ symbols = []
293
+
294
+ for param in params.flatten
295
+ if param.is_a?(Class)
296
+ klass = param
297
+ elsif param.is_a?(Symbol)
298
+ symbols << param
299
+ elsif param.is_a?(TrueClass) or param.is_a?(TrueClass)
300
+ writer = param
301
+ elsif param.is_a?(Hash)
302
+ # the meta hash.
303
+ meta.update(param) { |k, a, b| [a,b].join(' ') }
304
+ else
305
+ raise 'Error when defining property!'
306
+ end
307
+ end
308
+
309
+ raise 'No symbols provided!' if symbols.empty?
310
+
311
+ return meta, klass, symbols
312
+ end
313
+
314
+ end
315
+
316
+ end
317
+
318
+ class Module
319
+
320
+ # Define a property (== typed attribute)
321
+ # This works like Ruby's standard attr method, ie creates
322
+ # only one property.
323
+ #
324
+ # Use the prop_reader, prop_writer, prop_accessor methods
325
+ # for multiple properties.
326
+ #
327
+ # === Examples
328
+ #
329
+ # prop String, :name, :sql => "char(32), :sql_index => "name(32)"
330
+ # --> creates only writer.
331
+ # prop Fixnum, :oid, writer = true, :sql => "integer PRIMARY KEY"
332
+ # --> creates reader and writer.
333
+
334
+ def prop(*params)
335
+ meta, klass, symbols = Glue::PropertyUtils.resolve_prop_params(params)
336
+ symbol = symbols.first
337
+
338
+ Glue::PropertyUtils.enchant(self)
339
+
340
+ property = Glue::Property.new(symbol, klass, meta)
341
+
342
+ reader = meta[:reader] || true
343
+ writer = writer || meta[:writer] || false
344
+
345
+ meta[:reader] = true if meta[:reader].nil?
346
+ if defined?(writer)
347
+ meta[:writer] = writer
348
+ else
349
+ meta[:writer] = true if meta[:writer].nil?
350
+ end
351
+
352
+ Glue::PropertyUtils.add_prop(self, property)
353
+
354
+ # gmosx: should be placed AFTER enchant!
355
+
356
+ Glue::PropertyUtils.include_meta_mixins(self)
357
+ end
358
+
359
+ # Helper method. Accepts a collection of symbols and generates
360
+ # properties. Only generates reader.
361
+ #
362
+ # Example:
363
+ # prop_reader String, :name, :title, :body, :sql => "char(32)"
364
+
365
+ def prop_reader(*params)
366
+ meta, klass, symbols = Glue::PropertyUtils.resolve_prop_params(params)
367
+
368
+ meta[:reader] = true
369
+ meta[:writer] = false
370
+
371
+ for symbol in symbols
372
+ prop(klass, symbol, meta)
373
+ end
374
+ end
375
+
376
+ # Helper method. Accepts a collection of symbols and generates
377
+ # properties. Only generates writer.
378
+ #
379
+ # Example:
380
+ # prop_writer String, :name, :title, :body, :sql => "char(32)"
381
+
382
+ def prop_writer(*params)
383
+ meta, klass, symbols = Glue::PropertyUtils.resolve_prop_params(params)
384
+
385
+ meta[:reader] = false
386
+ meta[:writer] = true
387
+
388
+ for symbol in symbols
389
+ prop(klass, symbol, meta)
390
+ end
391
+ end
392
+
393
+ # Helper method. Accepts a collection of symbols and generates
394
+ # properties. Generates reader and writer.
395
+ #
396
+ # Example:
397
+ # prop_accessor String, :name, :title, :body, :sql => "char(32)"
398
+
399
+ def prop_accessor(*params)
400
+ meta, klass, symbols = Glue::PropertyUtils.resolve_prop_params(params)
401
+
402
+ meta[:reader] = true
403
+ meta[:writer] = true
404
+
405
+ for symbol in symbols
406
+ prop(klass, symbol, meta)
407
+ end
408
+ end
409
+ alias_method :property, :prop_accessor
410
+
411
+ # Attach metadata.
412
+ # Guard against duplicates, no need to keep order.
413
+ # This method uses closures :)
414
+ #--
415
+ # gmosx: crappy implementation, recode.
416
+ #++
417
+
418
+ def meta(key, *val)
419
+ Glue::PropertyUtils.enchant(self)
420
+
421
+ if val.empty?
422
+ self.module_eval %{
423
+ @__meta[key] = true
424
+ }
425
+ else
426
+ val = val.first if val.size == 1
427
+
428
+ self.module_eval %{
429
+ @__meta[key] ||= []
430
+ @__meta[key].delete_if { |v| val == v }
431
+ @__meta[key] << val
432
+ }
433
+ end
434
+ end
435
+
436
+ end
437
+
438
+ # * George Moschovitis <gm@navel.gr>
@@ -18,7 +18,7 @@ module TemplateMixin
18
18
  #
19
19
  # [+buffer+]
20
20
  # The variable to act as a buffer where the ruby code
21
- # for this template will be generated. Passed as a|
21
+ # for this template will be generated. Passed as a
22
22
  # String.
23
23
  #
24
24
  # [+base_dir+]
@@ -1,7 +1,7 @@
1
1
  require 'uri'
2
2
  require 'cgi'
3
3
 
4
- require 'glue/string'
4
+ require 'nano/string/blank%3F'
5
5
 
6
6
  module Glue
7
7
 
@@ -160,7 +160,7 @@ module UriUtils
160
160
  hash.update(parameters)
161
161
  query_string = self.hash_to_query_string(hash)
162
162
 
163
- if Glue::StringUtils.valid?(query_string)
163
+ unless query_string.blank?
164
164
  return "#{rest}?#{query_string}"
165
165
  else
166
166
  return rest