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.
- data/README +3 -3
- data/Rakefile +3 -3
- data/{CHANGELOG → doc/CHANGELOG.1} +0 -0
- data/doc/RELEASES +10 -0
- data/lib/glue.rb +7 -12
- data/lib/glue/configuration.rb +4 -4
- data/lib/glue/fixture.rb +3 -4
- data/lib/glue/helper.rb +1 -2
- data/lib/glue/logger.rb +43 -23
- data/lib/glue/metadata.rb +60 -0
- data/lib/glue/mock.rb +40 -0
- data/lib/glue/property.rb +33 -9
- data/lib/glue/property.rb.old +438 -0
- data/lib/glue/template.rb +1 -1
- data/lib/glue/uri.rb +2 -2
- data/test/glue/tc_metadata.rb +33 -0
- metadata +53 -57
- data/lib/glue/annotation.rb +0 -33
- data/lib/glue/array.rb +0 -61
- data/lib/glue/autoreload.rb +0 -30
- data/lib/glue/cache.rb +0 -189
- data/lib/glue/hash.rb +0 -122
- data/lib/glue/misc.rb +0 -15
- data/lib/glue/number.rb +0 -24
- data/lib/glue/object.rb +0 -35
- data/lib/glue/pool.rb +0 -60
- data/lib/glue/snapshot.rb +0 -104
- data/lib/glue/string.rb +0 -162
- data/lib/glue/time.rb +0 -85
- data/lib/vendor/blankslate.rb +0 -53
- data/test/glue/tc_hash.rb +0 -36
- data/test/glue/tc_numbers.rb +0 -18
- data/test/glue/tc_strings.rb +0 -102
@@ -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>
|
data/lib/glue/template.rb
CHANGED
data/lib/glue/uri.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'uri'
|
2
2
|
require 'cgi'
|
3
3
|
|
4
|
-
require '
|
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
|
-
|
163
|
+
unless query_string.blank?
|
164
164
|
return "#{rest}?#{query_string}"
|
165
165
|
else
|
166
166
|
return rest
|