webfinger-jrd 0.0.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.
- data/lib/wfjrd/wfjrd.rb +722 -0
- data/lib/wfjrd.rb +9 -0
- metadata +46 -0
data/lib/wfjrd/wfjrd.rb
ADDED
|
@@ -0,0 +1,722 @@
|
|
|
1
|
+
##############################################
|
|
2
|
+
# Author: James M Snell (jasnell@gmail.com) #
|
|
3
|
+
# License: Apache v2.0 #
|
|
4
|
+
# #
|
|
5
|
+
# A simple WebFinger implementation. #
|
|
6
|
+
# (forgive me... my ruby's a bit rusty) #
|
|
7
|
+
# #
|
|
8
|
+
# example use: #
|
|
9
|
+
# require 'webfinger' #
|
|
10
|
+
# include WebFinger #
|
|
11
|
+
# #
|
|
12
|
+
# STDOUT << jrd { #
|
|
13
|
+
# pretty #
|
|
14
|
+
# subject 'acct:me@here.com' #
|
|
15
|
+
# expires now + 1.day #
|
|
16
|
+
# aka [ #
|
|
17
|
+
# 'http://example.com', #
|
|
18
|
+
# 'http://example.net' #
|
|
19
|
+
# ] #
|
|
20
|
+
# properties { #
|
|
21
|
+
# property 'http://example.org', 'foo' #
|
|
22
|
+
# } #
|
|
23
|
+
# link { #
|
|
24
|
+
# rel 'foo' #
|
|
25
|
+
# href 'http://example.org' #
|
|
26
|
+
# type 'application/json' #
|
|
27
|
+
# } #
|
|
28
|
+
# } #
|
|
29
|
+
# #
|
|
30
|
+
# #
|
|
31
|
+
##############################################
|
|
32
|
+
require 'json'
|
|
33
|
+
require "time"
|
|
34
|
+
require 'addressable/uri'
|
|
35
|
+
require 'base64'
|
|
36
|
+
require 'zlib'
|
|
37
|
+
require 'i18n'
|
|
38
|
+
require 'mime/types'
|
|
39
|
+
|
|
40
|
+
class Hash
|
|
41
|
+
# allows a Hash to be used in place of an
|
|
42
|
+
# ASObj ... we don't necessarily want a
|
|
43
|
+
# mixin on this, so just set a property
|
|
44
|
+
def __object_type= ot
|
|
45
|
+
@object_type = ot
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def __object_type
|
|
49
|
+
@object_type
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
class Object
|
|
55
|
+
|
|
56
|
+
# Tests whether the method exists. Unlike the std method, however,
|
|
57
|
+
# does not throw a NameError if it doesn't. Just returns false
|
|
58
|
+
def method? sym
|
|
59
|
+
method sym rescue false
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Tests to see if the Object is one of several possible types
|
|
63
|
+
# like is_a? but accepting multiple arguments
|
|
64
|
+
def one_of_type? *args
|
|
65
|
+
args.any? {|x| is_a? x}
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
module WebFinger
|
|
71
|
+
|
|
72
|
+
# Provides a number of generally useful utilities methods
|
|
73
|
+
module Matchers
|
|
74
|
+
include Addressable
|
|
75
|
+
|
|
76
|
+
# true if m validates as a isegment-nz-nc as defined by RFC 3987.
|
|
77
|
+
def is_token? m
|
|
78
|
+
return false if m == nil
|
|
79
|
+
m = m.to_s
|
|
80
|
+
(m =~ /^([a-zA-Z0-9\-\.\_\~]|\%[0-9a-fA-F]{2}|[\!\$\&\'\(\)\*\+\,\;\=\@])+$/) != nil
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# true if m is parseable as an IRI and is absolute
|
|
84
|
+
def is_absolute_iri? m
|
|
85
|
+
URI.parse(m).absolute? rescue false
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# true if m is a valid MIME Media Type
|
|
89
|
+
def is_mime_type? m
|
|
90
|
+
MIME::Type.new m rescue false
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# true if m is a valid RFC 4646 Lang tag
|
|
94
|
+
def is_lang_tag? m
|
|
95
|
+
I18n::Locale::Tag::Rfc4646.tag m rescue false
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# utility method providing the basic structure of validation
|
|
99
|
+
# checkers for the various fields...
|
|
100
|
+
def checker &block
|
|
101
|
+
->(v) do
|
|
102
|
+
raise ArgumentError unless block.call v
|
|
103
|
+
v
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
module_function :checker, :is_token?, :is_absolute_iri?, :is_mime_type?, :is_lang_tag?
|
|
108
|
+
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Defines the basic functions for the WebFinger JRD DSL
|
|
112
|
+
module Makers
|
|
113
|
+
|
|
114
|
+
# Create a new object by copying another.
|
|
115
|
+
# A list of properties to omit from the new
|
|
116
|
+
# copy can be provided a variable arguments.
|
|
117
|
+
# For instance, if you have an existing activity
|
|
118
|
+
# object and wish to copy everything but the
|
|
119
|
+
# current verb and actor properties, you could
|
|
120
|
+
# call new_act = copy_from(old_act, :verb, :actor) { ... }
|
|
121
|
+
def copy_from(other,*without,&block)
|
|
122
|
+
JrdObj.from other,*without,&block
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Create a new JRD Object
|
|
126
|
+
def jrd(&block)
|
|
127
|
+
JrdObj.generate :jrd,&block
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def _link(&block)
|
|
131
|
+
JrdObj.generate :link,&block
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Utility method for returning the current time
|
|
135
|
+
def now
|
|
136
|
+
Time.now.utc
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def checker &block
|
|
142
|
+
Matchers.checker &block
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
module_function :checker
|
|
146
|
+
|
|
147
|
+
include Makers, Matchers
|
|
148
|
+
|
|
149
|
+
# Represents a basic JRD Object...
|
|
150
|
+
# Instances, once created, are immutable for
|
|
151
|
+
# all the core properties. The object maintains
|
|
152
|
+
# an internal hash and performs basic input
|
|
153
|
+
# validation to ensure that the built object
|
|
154
|
+
# conforms to the basic requirements of the
|
|
155
|
+
# JRD specification. Specific
|
|
156
|
+
# validation requirements are defined by the
|
|
157
|
+
# Spec module associated with the object_type
|
|
158
|
+
# specified for the JrdObj instance
|
|
159
|
+
class JrdObj
|
|
160
|
+
include Makers
|
|
161
|
+
|
|
162
|
+
def initialize object_type=nil
|
|
163
|
+
@_ = {}
|
|
164
|
+
@_.__object_type = object_type
|
|
165
|
+
@object_type = object_type
|
|
166
|
+
extend SPECS[object_type] || SPECS[nil]
|
|
167
|
+
strict
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# Puts this JrdObj into lenient validation mode
|
|
171
|
+
def lenient
|
|
172
|
+
@lenient = true
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# Puts this JrdObj into strict validation mode
|
|
176
|
+
def strict
|
|
177
|
+
@lenient = false
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
# Tells this JrdObj to generate formatted JSON output
|
|
181
|
+
def pretty
|
|
182
|
+
@pretty = true
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# true if this JrdObj has been configured to generate formatted JSON output
|
|
186
|
+
def pretty?
|
|
187
|
+
@pretty || false
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
# true if this JrdObj is operating in lenient mode
|
|
191
|
+
def lenient?
|
|
192
|
+
@lenient
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
# true if this JrdObj is operating in strict mode
|
|
196
|
+
def strict?
|
|
197
|
+
!lenient?
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
# the internal object type identifier for this JrdObj
|
|
201
|
+
def __object_type
|
|
202
|
+
@object_type
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
# return a frozen copy of the internal hash
|
|
206
|
+
def finish
|
|
207
|
+
@_.dup.freeze
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
# write this thing out, the param must repond to the << operator for appending
|
|
211
|
+
def append_to out
|
|
212
|
+
raise ArgumentError unless out.respond_to?:<<
|
|
213
|
+
out << to_s
|
|
214
|
+
end
|
|
215
|
+
alias :>> append_to
|
|
216
|
+
|
|
217
|
+
# force pretty printing
|
|
218
|
+
def pretty_to out
|
|
219
|
+
out << JSON.pretty_generate(@_)
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
# generates a copy of this object
|
|
223
|
+
def copy_of *without, &block
|
|
224
|
+
JrdObj.from self, *without, &block
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
def to_s
|
|
228
|
+
pretty? ? JSON.pretty_generate(@_) : @_.to_json
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
def [] k
|
|
232
|
+
@_[send("alias_for_#{k}".to_sym) || k]
|
|
233
|
+
end
|
|
234
|
+
protected :[]
|
|
235
|
+
|
|
236
|
+
# Sets a value on the internal hash without any type checking
|
|
237
|
+
# if v is an instance of JrdObj, finish will be called
|
|
238
|
+
# to get the frozen hash ...
|
|
239
|
+
def set k,v
|
|
240
|
+
key = k.to_s.to_sym
|
|
241
|
+
v = (v.is_a?(JrdObj) ? v.finish : v) unless v == nil
|
|
242
|
+
@_[key] = v unless v == nil
|
|
243
|
+
@_.delete key if v == nil
|
|
244
|
+
end
|
|
245
|
+
alias :[]= :set
|
|
246
|
+
|
|
247
|
+
def freeze
|
|
248
|
+
@_.freeze
|
|
249
|
+
super
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
# Within the generator, any method call that has exactly one
|
|
253
|
+
# parameter will be turned into a member of the underlying hash
|
|
254
|
+
def property m, *args, &block
|
|
255
|
+
# Return nil if it's looking for an unknown alias_for_* method
|
|
256
|
+
return nil if m =~ /^alias\_for\_.*/
|
|
257
|
+
|
|
258
|
+
# Special magic... if an unknown ? method is called, it's treated
|
|
259
|
+
# as a check against the internal hash to see if the given field
|
|
260
|
+
# has been set. For instance, priority? will check to see if the
|
|
261
|
+
# priority field has been set
|
|
262
|
+
return @_.has_key?(m.to_s[0...-1].to_sym) if m =~ /.*\?/
|
|
263
|
+
|
|
264
|
+
# Is it an unknown to_ method? e.g. to_ary ... if so, fall through to default
|
|
265
|
+
if m =~ /^to\_.*/
|
|
266
|
+
super
|
|
267
|
+
return
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
# Once we get past the special cases, check to see if we have the
|
|
271
|
+
# expected number of arguments.
|
|
272
|
+
if !args.length.within?(1..2)
|
|
273
|
+
raise NoMethodError
|
|
274
|
+
return
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
# Now we start to figure out the exact value to set
|
|
278
|
+
transform, alias_for, checker = [:transform_,:alias_for_,:check_].map {|x| "#{x}#{m}".to_sym }
|
|
279
|
+
|
|
280
|
+
v = args[0]
|
|
281
|
+
|
|
282
|
+
# First, if the value given is a JrdObj, call finish on it
|
|
283
|
+
v = (v.is_a?(JrdObj) ? v.finish : v) unless v == nil
|
|
284
|
+
|
|
285
|
+
# If it's an Enumerable, but not a Hash, convert to an Array using Map,
|
|
286
|
+
# If each of the member items are JrdObj's call finish.
|
|
287
|
+
v = v.map {|i| i.is_a?(JrdObj) ? i.finish : i } if v.is_a?(Enumerable) && !v.is_a?(Hash)
|
|
288
|
+
|
|
289
|
+
# If the value is a Time object, let's make sure it's ISO 8601
|
|
290
|
+
v = v.iso8601 if v.is_a? Time
|
|
291
|
+
|
|
292
|
+
# Finally, do the object_type specific transform, if any
|
|
293
|
+
# note, this could potentially undo the previous checks if
|
|
294
|
+
# the transform provided by a given spec module isn't well
|
|
295
|
+
# behaved. that's ok tho, we'll trust that it's doing the
|
|
296
|
+
# right thing and just move on ... we're going to be validating
|
|
297
|
+
# next anyway
|
|
298
|
+
v = send transform, v if method? transform
|
|
299
|
+
|
|
300
|
+
# Now let's do validation... unless lenient is set
|
|
301
|
+
if !args[1] && strict?
|
|
302
|
+
ok = method?(checker) ? send(checker,v) : missing_check(v)
|
|
303
|
+
raise ArgumentError unless ok
|
|
304
|
+
end
|
|
305
|
+
m = send alias_for if method? alias_for
|
|
306
|
+
@_[m] = v unless v == nil
|
|
307
|
+
@_.delete m if v == nil
|
|
308
|
+
end
|
|
309
|
+
alias :method_missing :property
|
|
310
|
+
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
class << JrdObj
|
|
314
|
+
|
|
315
|
+
# Performs the actual work of creating an JrdObj and executing
|
|
316
|
+
# the associated block to build it up, then freezing the
|
|
317
|
+
# JrdObj before returning it
|
|
318
|
+
def generate object_type=nil, &block
|
|
319
|
+
m = JrdObj.new object_type
|
|
320
|
+
m.instance_eval &block unless not block_given?
|
|
321
|
+
m.freeze
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
# Creates a new JrdObj by copying from another one
|
|
325
|
+
def from other, *without, &block
|
|
326
|
+
raise ArgumentError unless other.one_of_type?(JrdObj,Hash)
|
|
327
|
+
m = JrdObj.new other.__object_type
|
|
328
|
+
m.pretty if other.pretty?
|
|
329
|
+
m.lenient if other.lenient?
|
|
330
|
+
other.finish.each_pair {|k,y| m[k] = y unless without.include? k }
|
|
331
|
+
m.instance_eval &block unless not block_given?
|
|
332
|
+
m.freeze
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
# The base module for all Validation Spec Modules.. these
|
|
338
|
+
# define the requirements for the various WebFinger
|
|
339
|
+
# object types
|
|
340
|
+
module Spec
|
|
341
|
+
|
|
342
|
+
# by default, allow all values if a specific check hasn't been provided
|
|
343
|
+
# Spec modules can override this behavior by defining their own missing_check
|
|
344
|
+
def missing_check v
|
|
345
|
+
true
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
# Defines the various utility methods used to build Spec modules
|
|
349
|
+
module Defs
|
|
350
|
+
|
|
351
|
+
# Maps an input symbol to a property name in the hash
|
|
352
|
+
def def_alias sym, name
|
|
353
|
+
define_method("alias_for_#{sym}".to_sym) {
|
|
354
|
+
name
|
|
355
|
+
} if name
|
|
356
|
+
module_function "alias_for_#{sym}".to_sym
|
|
357
|
+
end
|
|
358
|
+
|
|
359
|
+
# Defines the method for validating the value of a
|
|
360
|
+
# specific property.
|
|
361
|
+
def def_checker sym, &block
|
|
362
|
+
sym = "check_#{sym}".to_sym
|
|
363
|
+
define_method sym,&block
|
|
364
|
+
module_function sym
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
# Defines a transform for the value of a specific property
|
|
368
|
+
def def_transform sym, &block
|
|
369
|
+
sym = "transform_#{sym}".to_sym
|
|
370
|
+
if block_given?
|
|
371
|
+
define_method sym,&block
|
|
372
|
+
else
|
|
373
|
+
define_method(sym) {|v|v} # just return self if no transform defined
|
|
374
|
+
end
|
|
375
|
+
module_function sym
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
# Mark def_alias, def_checker and def_transform as private
|
|
379
|
+
# these should only be called from within the Defs module
|
|
380
|
+
private :def_alias, :def_checker, :def_transform
|
|
381
|
+
|
|
382
|
+
# Define a property as being an absolute IRI
|
|
383
|
+
def def_absolute_iri sym, name=nil
|
|
384
|
+
def_transform(sym) {|v|
|
|
385
|
+
next nil if v == nil
|
|
386
|
+
Addressable::URI.parse(v)
|
|
387
|
+
}
|
|
388
|
+
def_checker(sym) { |v|
|
|
389
|
+
# v must be an absolute IRI
|
|
390
|
+
!v || is_absolute_iri?(v)
|
|
391
|
+
}
|
|
392
|
+
def_alias sym, name if name
|
|
393
|
+
end
|
|
394
|
+
|
|
395
|
+
# Define a property as being an ISO 8601 DateTime
|
|
396
|
+
def def_date_time sym, name=nil
|
|
397
|
+
def_transform(sym) {|v|
|
|
398
|
+
next v if v == nil || v.is_a?(Time)
|
|
399
|
+
Time.parse(v.to_s).iso8601 rescue v
|
|
400
|
+
}
|
|
401
|
+
def_checker(sym) { |v|
|
|
402
|
+
# v must be parseable as a time
|
|
403
|
+
next true if v == nil || v.is_a?(Time)
|
|
404
|
+
Time.parse(v.to_s) rescue next false
|
|
405
|
+
true
|
|
406
|
+
}
|
|
407
|
+
def_alias sym, name if name
|
|
408
|
+
end
|
|
409
|
+
|
|
410
|
+
# Define a property as being an IRI ... does not have to be absolute
|
|
411
|
+
def def_iri sym, name=nil
|
|
412
|
+
def_transform(sym) {|v|
|
|
413
|
+
next nil if v == nill
|
|
414
|
+
Addressable::URI.parse(v)}
|
|
415
|
+
def_checker(sym) { |v|
|
|
416
|
+
# v must be parseable as a URI
|
|
417
|
+
!v || Addressable::URI.parse(v) rescue false
|
|
418
|
+
}
|
|
419
|
+
def_alias sym, name if name
|
|
420
|
+
end
|
|
421
|
+
|
|
422
|
+
# Define a property as being a string, an additional block
|
|
423
|
+
# can be passed in to perform additional checking (e.g. regex matching, etc)
|
|
424
|
+
def def_string sym, name=nil, &block
|
|
425
|
+
def_transform(sym) {|v|
|
|
426
|
+
next nil if v == nil
|
|
427
|
+
v.to_s
|
|
428
|
+
}
|
|
429
|
+
def_checker(sym) { |v|
|
|
430
|
+
# v will be converted to a string, then checked against the optional
|
|
431
|
+
# block... if the block returns false, raise a validation error
|
|
432
|
+
next true if v == nil
|
|
433
|
+
next block.call(v.to_s) if block_given?
|
|
434
|
+
true
|
|
435
|
+
}
|
|
436
|
+
def_alias sym, name if name
|
|
437
|
+
end
|
|
438
|
+
|
|
439
|
+
# Define a property as being an JrdObj.
|
|
440
|
+
def def_object sym, object_type=nil, name=nil
|
|
441
|
+
def_transform(sym)
|
|
442
|
+
def_checker(sym) { |v|
|
|
443
|
+
next true if v == nil
|
|
444
|
+
# v must be an instance of the given object_type
|
|
445
|
+
if object_type
|
|
446
|
+
next false if v.__object_type != object_type
|
|
447
|
+
end
|
|
448
|
+
# right now this is pretty strict... we only allow Hash or JrdObj
|
|
449
|
+
# instances to be passed. TODO: see if we can relax this to enable
|
|
450
|
+
# more flexible duck typing ...
|
|
451
|
+
v.one_of_type? Hash, JrdObj
|
|
452
|
+
}
|
|
453
|
+
def_alias sym, name if name
|
|
454
|
+
def_property(sym, object_type, name) if object_type
|
|
455
|
+
end
|
|
456
|
+
|
|
457
|
+
# Define a property as being an Array of JrdObj's
|
|
458
|
+
def def_object_array sym, object_type=nil, name=nil
|
|
459
|
+
def_alias sym, name if name
|
|
460
|
+
def_transform(sym) {|v|
|
|
461
|
+
next nil if v == nil
|
|
462
|
+
orig = self[sym]
|
|
463
|
+
if v.is_a?(Array)
|
|
464
|
+
next orig ? orig + v : v
|
|
465
|
+
end
|
|
466
|
+
orig ? orig << v : [v]
|
|
467
|
+
}
|
|
468
|
+
def_checker(sym) { |v|
|
|
469
|
+
next true if v == nil
|
|
470
|
+
# v must be either an array or enumerable and each item
|
|
471
|
+
# must be either a Hash or JrdObj that matches the given
|
|
472
|
+
# object_type, if any
|
|
473
|
+
next false unless (v.one_of_type?(Array, Enumerable) && !v.is_a?(Hash))
|
|
474
|
+
v.each {|x|
|
|
475
|
+
return false unless x.one_of_type? JrdObj, Hash
|
|
476
|
+
return false if (object_type && x.__object_type != object_type)
|
|
477
|
+
}
|
|
478
|
+
true
|
|
479
|
+
}
|
|
480
|
+
end
|
|
481
|
+
|
|
482
|
+
# Define a property as being an Array of Strings, an additional
|
|
483
|
+
# block can be passed to perform additional checking
|
|
484
|
+
def def_string_array sym, name=nil, &block
|
|
485
|
+
def_transform(sym) {|v|
|
|
486
|
+
next nil if v == nil
|
|
487
|
+
orig = self[sym]
|
|
488
|
+
if v.one_of_type? Array, Enumerable
|
|
489
|
+
add = v.map {|x| x.to_s}
|
|
490
|
+
next orig ? orig + add : add
|
|
491
|
+
end
|
|
492
|
+
orig ? orig << v.to_s : [v.to_s]
|
|
493
|
+
}
|
|
494
|
+
def_checker(sym) { |v|
|
|
495
|
+
next true if v == nil
|
|
496
|
+
next false unless (v.one_of_type?(Array, Enumerable) && !v.is_a?(Hash))
|
|
497
|
+
v.each {|x|
|
|
498
|
+
return false unless block.call(x)
|
|
499
|
+
} if block_given?
|
|
500
|
+
true
|
|
501
|
+
}
|
|
502
|
+
def_alias sym, name if name
|
|
503
|
+
end
|
|
504
|
+
|
|
505
|
+
def def_boolean sym, name=nil
|
|
506
|
+
def_transform(sym) {|v|
|
|
507
|
+
next false if v == nil
|
|
508
|
+
v ? true : false
|
|
509
|
+
}
|
|
510
|
+
def_checker(sym) { |v|
|
|
511
|
+
v.one_of_type? TrueClass, FalseClass
|
|
512
|
+
}
|
|
513
|
+
def_alias sym, name if name
|
|
514
|
+
|
|
515
|
+
module_eval %Q/def #{sym}() property(:'#{sym}', true) end/
|
|
516
|
+
module_eval %Q/def not_#{sym}() property(:'#{sym}', false) end/
|
|
517
|
+
end
|
|
518
|
+
|
|
519
|
+
# Define a property as being a Numeric
|
|
520
|
+
def def_numeric sym, name=nil, &block
|
|
521
|
+
def_checker(sym) { |v|
|
|
522
|
+
next true if v == nil
|
|
523
|
+
return false unless v.is_a? Numeric
|
|
524
|
+
if block_given?
|
|
525
|
+
next false unless block.call v
|
|
526
|
+
end
|
|
527
|
+
true
|
|
528
|
+
}
|
|
529
|
+
def_alias sym, name if name
|
|
530
|
+
end
|
|
531
|
+
|
|
532
|
+
# Define a property as being a non-negative fixnum
|
|
533
|
+
def def_non_negative_int sym, name=nil
|
|
534
|
+
def_numeric(sym, name) {|v|
|
|
535
|
+
next false unless (v.is_a?(Fixnum) && v >= 0)
|
|
536
|
+
true
|
|
537
|
+
}
|
|
538
|
+
end
|
|
539
|
+
|
|
540
|
+
# Define a property as being a float with a bounded range
|
|
541
|
+
def def_bound_float sym, range, name=nil
|
|
542
|
+
def_numeric(sym, name) {|v|
|
|
543
|
+
next false if (range.respond_to?(:include?) && !range.include?(v))
|
|
544
|
+
true
|
|
545
|
+
}
|
|
546
|
+
end
|
|
547
|
+
|
|
548
|
+
def def_property sym, type=nil, name=nil
|
|
549
|
+
sym = sym.to_s
|
|
550
|
+
module_eval %Q/
|
|
551
|
+
def #{sym} &block
|
|
552
|
+
self[:#{name || sym}] = JrdObj.generate(:#{type},&block)
|
|
553
|
+
end
|
|
554
|
+
/
|
|
555
|
+
end
|
|
556
|
+
private :def_property
|
|
557
|
+
end
|
|
558
|
+
|
|
559
|
+
# Ensure the the Defs module is included in all spec modules...
|
|
560
|
+
extend Defs
|
|
561
|
+
def self.included(other)
|
|
562
|
+
other.extend Defs
|
|
563
|
+
end
|
|
564
|
+
|
|
565
|
+
end
|
|
566
|
+
|
|
567
|
+
# The base spec for all JrdObj's
|
|
568
|
+
module ObjectSpec
|
|
569
|
+
include Spec
|
|
570
|
+
|
|
571
|
+
def_object :properties, :properties, :properties
|
|
572
|
+
|
|
573
|
+
# ensure that all spec object include the Defs module...
|
|
574
|
+
include Defs
|
|
575
|
+
def self.included(other)
|
|
576
|
+
other.extend Defs
|
|
577
|
+
other.module_exec {
|
|
578
|
+
def self.included(o)
|
|
579
|
+
o.extend Defs
|
|
580
|
+
end
|
|
581
|
+
}
|
|
582
|
+
end
|
|
583
|
+
|
|
584
|
+
end
|
|
585
|
+
|
|
586
|
+
module JrdSpec
|
|
587
|
+
include ObjectSpec
|
|
588
|
+
def_absolute_iri :subject
|
|
589
|
+
def_date_time :expires
|
|
590
|
+
check = ->(x){ is_absolute_iri? x }
|
|
591
|
+
def_string_array :aka, :aliases, &check
|
|
592
|
+
def_object_array :links, :link, :links
|
|
593
|
+
|
|
594
|
+
def link &block
|
|
595
|
+
property :links, _link(&block)
|
|
596
|
+
end
|
|
597
|
+
|
|
598
|
+
end
|
|
599
|
+
|
|
600
|
+
module LinkSpec
|
|
601
|
+
include ObjectSpec
|
|
602
|
+
def_string :rel
|
|
603
|
+
def_iri :href
|
|
604
|
+
def_string :type do |x| is_mime_type? x end # must be a valid MIME Media Type
|
|
605
|
+
def_object :titles, :titles, :titles
|
|
606
|
+
end
|
|
607
|
+
|
|
608
|
+
module PropertiesSpec
|
|
609
|
+
include ObjectSpec
|
|
610
|
+
end
|
|
611
|
+
|
|
612
|
+
module TitlesSpec
|
|
613
|
+
include ObjectSpec
|
|
614
|
+
def missing_checker v
|
|
615
|
+
v.is_a? String
|
|
616
|
+
end
|
|
617
|
+
def lang lang, val
|
|
618
|
+
property lang.to_sym, val
|
|
619
|
+
end
|
|
620
|
+
def default val
|
|
621
|
+
lang '*', val
|
|
622
|
+
lang 'default', val
|
|
623
|
+
end
|
|
624
|
+
end
|
|
625
|
+
|
|
626
|
+
# Collect the various Specs and map to their respective object types
|
|
627
|
+
SPECS = {
|
|
628
|
+
nil => ObjectSpec,
|
|
629
|
+
:jrd => JrdSpec,
|
|
630
|
+
:link => LinkSpec,
|
|
631
|
+
:properties => PropertiesSpec,
|
|
632
|
+
:titles => TitlesSpec
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
# override or add a new spec... be careful here.. the existing
|
|
636
|
+
# spec definitions can be overridden
|
|
637
|
+
def add_spec sym, spec
|
|
638
|
+
SPECS[sym] = spec
|
|
639
|
+
end
|
|
640
|
+
|
|
641
|
+
# create a new Spec module
|
|
642
|
+
def spec *specs, &block
|
|
643
|
+
o = Module.new.extend Spec, Spec::Defs, *specs
|
|
644
|
+
o.module_exec &block
|
|
645
|
+
o
|
|
646
|
+
end
|
|
647
|
+
|
|
648
|
+
# create a new Spec module based on ObjectSpec
|
|
649
|
+
def object_spec *specs, &block
|
|
650
|
+
spec ObjectSpec, *specs, &block
|
|
651
|
+
end
|
|
652
|
+
|
|
653
|
+
# define the template method as an alias to lambda
|
|
654
|
+
alias :template :lambda
|
|
655
|
+
|
|
656
|
+
module_function :add_spec, :spec, :object_spec
|
|
657
|
+
|
|
658
|
+
# syntactic sugar
|
|
659
|
+
LENIENT, STRICT = true, false
|
|
660
|
+
|
|
661
|
+
end
|
|
662
|
+
|
|
663
|
+
# some syntactic sugar for Fixnums... useful for
|
|
664
|
+
# working with Time .. e.g. updated now - 1.week #updated one week ago
|
|
665
|
+
class Fixnum
|
|
666
|
+
|
|
667
|
+
# true if this number is within the given range
|
|
668
|
+
def within? r
|
|
669
|
+
raise ArgumentError if not r.is_a?Range
|
|
670
|
+
r.include? self
|
|
671
|
+
end unless method_defined?(:within?)
|
|
672
|
+
|
|
673
|
+
# treats the fixnum as a representation of a number
|
|
674
|
+
# of milliseconds, returns the approximate total number
|
|
675
|
+
# of seconds represented e.g. 1000.milliseconds => 1
|
|
676
|
+
# fractional seconds are truncated (rounded down)
|
|
677
|
+
def milliseconds
|
|
678
|
+
self / 1000
|
|
679
|
+
end unless method_defined?(:milliseconds)
|
|
680
|
+
|
|
681
|
+
# treats the fixnum as a representation of a number
|
|
682
|
+
# of seconds
|
|
683
|
+
def seconds
|
|
684
|
+
self
|
|
685
|
+
end unless method_defined?(:seconds)
|
|
686
|
+
|
|
687
|
+
# treats the fixnum as a representation of a
|
|
688
|
+
# number of minutes and returns the total number
|
|
689
|
+
# of seconds represented.. e.g. 2.minutes => 120,
|
|
690
|
+
# 3.minutes => 180
|
|
691
|
+
def minutes
|
|
692
|
+
seconds * 60
|
|
693
|
+
end unless method_defined?(:minutes)
|
|
694
|
+
|
|
695
|
+
# treats the fixnum as a representation of a
|
|
696
|
+
# number of hours and returns the total number
|
|
697
|
+
# of seconds represented.. e.g. 2.hours => 7200
|
|
698
|
+
def hours
|
|
699
|
+
minutes * 60
|
|
700
|
+
end unless method_defined?(:hours)
|
|
701
|
+
|
|
702
|
+
# treats the fixnum as a representation of a
|
|
703
|
+
# number of days and returns the total number
|
|
704
|
+
# of seconds represented.. e.g. 2.days => 172800
|
|
705
|
+
def days
|
|
706
|
+
hours * 24
|
|
707
|
+
end unless method_defined?(:days)
|
|
708
|
+
|
|
709
|
+
# treats the fixnum as a representatin of a
|
|
710
|
+
# number of weeks and returns the total number
|
|
711
|
+
# of seconds represented.. e.g. 2.weeks => 1209600
|
|
712
|
+
def weeks
|
|
713
|
+
days * 7
|
|
714
|
+
end unless method_defined?(:weeks)
|
|
715
|
+
|
|
716
|
+
alias second seconds unless method_defined?(:second)
|
|
717
|
+
alias minute minutes unless method_defined?(:minute)
|
|
718
|
+
alias hour hours unless method_defined?(:hour)
|
|
719
|
+
alias day days unless method_defined?(:day)
|
|
720
|
+
alias week weeks unless method_defined?(:week)
|
|
721
|
+
|
|
722
|
+
end
|
data/lib/wfjrd.rb
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
##############################################
|
|
2
|
+
# Author: James M Snell (jasnell@gmail.com) #
|
|
3
|
+
# License: Apache v2.0 #
|
|
4
|
+
# #
|
|
5
|
+
# A simple WebFinger JRD implementation. #
|
|
6
|
+
##############################################
|
|
7
|
+
REQUIRED_VERSION = '1.9.3'
|
|
8
|
+
raise "The webfinger gem currently requires Ruby version #{REQUIRED_VERSION} or higher" if RUBY_VERSION < REQUIRED_VERSION
|
|
9
|
+
require 'wfjrd/wfjrd'
|
metadata
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: webfinger-jrd
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.1
|
|
5
|
+
prerelease:
|
|
6
|
+
platform: ruby
|
|
7
|
+
authors:
|
|
8
|
+
- James M Snell
|
|
9
|
+
autorequire:
|
|
10
|
+
bindir: bin
|
|
11
|
+
cert_chain: []
|
|
12
|
+
date: 2012-12-18 00:00:00.000000000 Z
|
|
13
|
+
dependencies: []
|
|
14
|
+
description: A WebFinger JRD DSL
|
|
15
|
+
email: jasnell@gmail.com
|
|
16
|
+
executables: []
|
|
17
|
+
extensions: []
|
|
18
|
+
extra_rdoc_files: []
|
|
19
|
+
files:
|
|
20
|
+
- lib/wfjrd.rb
|
|
21
|
+
- lib/wfjrd/wfjrd.rb
|
|
22
|
+
homepage: https://github.com/jasnell/webfinger
|
|
23
|
+
licenses: []
|
|
24
|
+
post_install_message:
|
|
25
|
+
rdoc_options: []
|
|
26
|
+
require_paths:
|
|
27
|
+
- lib
|
|
28
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
29
|
+
none: false
|
|
30
|
+
requirements:
|
|
31
|
+
- - ! '>='
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '0'
|
|
34
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
35
|
+
none: false
|
|
36
|
+
requirements:
|
|
37
|
+
- - ! '>='
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '0'
|
|
40
|
+
requirements: []
|
|
41
|
+
rubyforge_project:
|
|
42
|
+
rubygems_version: 1.8.24
|
|
43
|
+
signing_key:
|
|
44
|
+
specification_version: 3
|
|
45
|
+
summary: A WebFinger JRD DSL
|
|
46
|
+
test_files: []
|