streams 0.1.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/lib/streams.rb +1107 -0
- metadata +45 -0
data/lib/streams.rb
ADDED
|
@@ -0,0 +1,1107 @@
|
|
|
1
|
+
##############################################
|
|
2
|
+
# Author: James M Snell (jasnell@gmail.com) #
|
|
3
|
+
# License: Apache v2.0 #
|
|
4
|
+
# #
|
|
5
|
+
# A simple Activity Streams implementation. #
|
|
6
|
+
# (forgive me... my ruby's a bit rusty) #
|
|
7
|
+
# #
|
|
8
|
+
# example use: #
|
|
9
|
+
# require 'activitystreams' #
|
|
10
|
+
# include ActivityStreams #
|
|
11
|
+
# #
|
|
12
|
+
# STDOUT << activity { #
|
|
13
|
+
# verb :post #
|
|
14
|
+
# actor person { #
|
|
15
|
+
# display_name 'James' #
|
|
16
|
+
# } #
|
|
17
|
+
# object note { #
|
|
18
|
+
# content 'This is a test' #
|
|
19
|
+
# } #
|
|
20
|
+
# } #
|
|
21
|
+
# #
|
|
22
|
+
##############################################
|
|
23
|
+
|
|
24
|
+
require 'json'
|
|
25
|
+
require "time"
|
|
26
|
+
require 'addressable/uri'
|
|
27
|
+
require 'base64'
|
|
28
|
+
require 'zlib'
|
|
29
|
+
require 'i18n'
|
|
30
|
+
require 'mime/types'
|
|
31
|
+
|
|
32
|
+
class Hash
|
|
33
|
+
# allows a Hash to be used in place of an
|
|
34
|
+
# ASObj ... we don't necessarily want a
|
|
35
|
+
# mixin on this, so just set a property
|
|
36
|
+
def __object_type= ot
|
|
37
|
+
@object_type = ot
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def __object_type
|
|
41
|
+
@object_type
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
class Object
|
|
47
|
+
|
|
48
|
+
# Tests whether the method exists. Unlike the std method, however,
|
|
49
|
+
# does not throw a NameError if it doesn't. Just returns false
|
|
50
|
+
def method? sym
|
|
51
|
+
method sym rescue false
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Tests to see if the Object is one of several possible types
|
|
55
|
+
# like is_a? but accepting multiple arguments
|
|
56
|
+
def one_of_type? *args
|
|
57
|
+
args.any? {|x| is_a? x}
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
module ActivityStreams
|
|
63
|
+
|
|
64
|
+
# Provides a number of generally useful utilities methods
|
|
65
|
+
module Matchers
|
|
66
|
+
include Addressable
|
|
67
|
+
|
|
68
|
+
# true if m validates as a isegment-nz-nc as defined by RFC 3987.
|
|
69
|
+
# The activity streams spec requires that verbs and object types
|
|
70
|
+
# MUST either be isegment-nz-nc or absolute IRI productions.
|
|
71
|
+
def is_token? m
|
|
72
|
+
return false if m == nil
|
|
73
|
+
m = m.to_s
|
|
74
|
+
(m =~ /^([a-zA-Z0-9\-\.\_\~]|\%[0-9a-fA-F]{2}|[\!\$\&\'\(\)\*\+\,\;\=\@])+$/) != nil
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# true if m is parseable as an IRI and is absolute
|
|
78
|
+
def is_absolute_iri? m
|
|
79
|
+
URI.parse(m).absolute? rescue false
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# true if m is a valid verb
|
|
83
|
+
def is_verb? m
|
|
84
|
+
is_token?(m) || is_absolute_iri?(m)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# true if m is a valid MIME Media Type
|
|
88
|
+
def is_mime_type? m
|
|
89
|
+
MIME::Type.new m rescue false
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# true if m is a valid RFC 4646 Lang tag
|
|
93
|
+
def is_lang_tag? m
|
|
94
|
+
I18n::Locale::Tag::Rfc4646.tag m rescue false
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# utility method providing the basic structure of validation
|
|
98
|
+
# checkers for the various fields...
|
|
99
|
+
def checker &block
|
|
100
|
+
->(v) do
|
|
101
|
+
raise ArgumentError unless block.call v
|
|
102
|
+
v
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
module_function :checker, :is_token?, :is_verb?, :is_absolute_iri?, :is_mime_type?, :is_lang_tag?
|
|
107
|
+
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Defines the basic functions for the Activity Streams DSL
|
|
111
|
+
module Makers
|
|
112
|
+
|
|
113
|
+
# Create a new object by copying another.
|
|
114
|
+
# A list of properties to omit from the new
|
|
115
|
+
# copy can be provided a variable arguments.
|
|
116
|
+
# For instance, if you have an existing activity
|
|
117
|
+
# object and wish to copy everything but the
|
|
118
|
+
# current verb and actor properties, you could
|
|
119
|
+
# call new_act = copy_from(old_act, :verb, :actor) { ... }
|
|
120
|
+
def copy_from(other,*without,&block)
|
|
121
|
+
ASObj.from other,*without,&block
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# Create a new Activity Streams Object
|
|
125
|
+
def object(object_type=nil,&block)
|
|
126
|
+
ASObj.generate object_type,&block
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Create a new Media Link
|
|
130
|
+
def media_link &block
|
|
131
|
+
ASObj.generate :media_link,true,&block
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Create a new Collection Object
|
|
135
|
+
def collection include_object_type=false, &block
|
|
136
|
+
ASObj.generate :collection, !include_object_type, &block
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# Create a new Activity Object
|
|
140
|
+
def activity include_object_type=false, &block
|
|
141
|
+
ASObj.generate :activity, !include_object_type, &block
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# Utility method for returning the current time
|
|
145
|
+
def now
|
|
146
|
+
Time.now.utc
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# For the remain object types from the Activity Streams Schema
|
|
150
|
+
# iterate through them and create a factory method for each
|
|
151
|
+
[ :alert,
|
|
152
|
+
:application,
|
|
153
|
+
:article,
|
|
154
|
+
:audio,
|
|
155
|
+
:badge,
|
|
156
|
+
:binary,
|
|
157
|
+
:bookmark,
|
|
158
|
+
:comment,
|
|
159
|
+
:device,
|
|
160
|
+
:event,
|
|
161
|
+
:file,
|
|
162
|
+
:game,
|
|
163
|
+
:group,
|
|
164
|
+
:image,
|
|
165
|
+
:issue,
|
|
166
|
+
:job,
|
|
167
|
+
:note,
|
|
168
|
+
:offer,
|
|
169
|
+
:organization,
|
|
170
|
+
:page,
|
|
171
|
+
:permission,
|
|
172
|
+
:person,
|
|
173
|
+
:place,
|
|
174
|
+
:process,
|
|
175
|
+
:product,
|
|
176
|
+
:question,
|
|
177
|
+
:review,
|
|
178
|
+
:role,
|
|
179
|
+
:service,
|
|
180
|
+
:task,
|
|
181
|
+
:team,
|
|
182
|
+
:video
|
|
183
|
+
].each {|o|
|
|
184
|
+
module_eval "def #{o}(&block) object :#{o}, &block end"
|
|
185
|
+
}
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def checker &block
|
|
189
|
+
Matchers.checker &block
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
module_function :checker
|
|
193
|
+
|
|
194
|
+
include Makers, Matchers
|
|
195
|
+
|
|
196
|
+
# Represents a basic Activity Streams Object...
|
|
197
|
+
# Instances, once created, are immutable for
|
|
198
|
+
# all the core properties. The object maintains
|
|
199
|
+
# an internal hash and performs basic input
|
|
200
|
+
# validation to ensure that the built object
|
|
201
|
+
# conforms to the basic requirements of the
|
|
202
|
+
# Activity Streams specifications. Specific
|
|
203
|
+
# validation requirements are defined by the
|
|
204
|
+
# Spec module associated with the object_type
|
|
205
|
+
# specified for the ASObj instance
|
|
206
|
+
class ASObj
|
|
207
|
+
include Makers
|
|
208
|
+
|
|
209
|
+
def initialize object_type=nil
|
|
210
|
+
@_ = {}
|
|
211
|
+
@_.__object_type = object_type
|
|
212
|
+
@object_type = object_type
|
|
213
|
+
extend SPECS[object_type] || SPECS[nil]
|
|
214
|
+
strict
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
# Puts this ASObj into lenient validation mode
|
|
218
|
+
def lenient
|
|
219
|
+
@lenient = true
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
# Puts this ASObj into strict validation mode
|
|
223
|
+
def strict
|
|
224
|
+
@lenient = false
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
# Tells this ASObj to generate formatted JSON output
|
|
228
|
+
def pretty
|
|
229
|
+
@pretty = true
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
# true if this ASObj has been configured to generate formatted JSON output
|
|
233
|
+
def pretty?
|
|
234
|
+
@pretty || false
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
# true if this ASObj is operating in lenient mode
|
|
238
|
+
def lenient?
|
|
239
|
+
@lenient
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
# true if this ASObj is operating in strict mode
|
|
243
|
+
def strict?
|
|
244
|
+
!lenient?
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
# the internal object type identifier for this ASObj
|
|
248
|
+
def __object_type
|
|
249
|
+
@object_type
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
# return a frozen copy of the internal hash
|
|
253
|
+
def finish
|
|
254
|
+
@_.dup.freeze
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
# write this thing out, the param must repond to the << operator for appending
|
|
258
|
+
def append_to out
|
|
259
|
+
raise ArgumentError unless out.respond_to?:<<
|
|
260
|
+
out << to_s
|
|
261
|
+
end
|
|
262
|
+
alias :>> append_to
|
|
263
|
+
|
|
264
|
+
# force pretty printing
|
|
265
|
+
def pretty_to out
|
|
266
|
+
out << JSON.pretty_generate(@_)
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
# generates a copy of this object
|
|
270
|
+
def copy_of *without, &block
|
|
271
|
+
ASObj.from self, *without, &block
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
def to_s
|
|
275
|
+
pretty? ? JSON.pretty_generate(@_) : @_.to_json
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
def [] k
|
|
279
|
+
@_[send("alias_for_#{k}".to_sym) || k]
|
|
280
|
+
end
|
|
281
|
+
protected :[]
|
|
282
|
+
|
|
283
|
+
# Sets a value on the internal hash without any type checking
|
|
284
|
+
# if v is an instance of ASObj, finish will be called
|
|
285
|
+
# to get the frozen hash ...
|
|
286
|
+
def set k,v
|
|
287
|
+
key = k.to_s.to_sym
|
|
288
|
+
v = (v.is_a?(ASObj) ? v.finish : v) unless v == nil
|
|
289
|
+
@_[key] = v unless v == nil
|
|
290
|
+
@_.delete key if v == nil
|
|
291
|
+
end
|
|
292
|
+
alias :[]= :set
|
|
293
|
+
|
|
294
|
+
def freeze
|
|
295
|
+
@_.freeze
|
|
296
|
+
super
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
# Within the generator, any method call that has exactly one
|
|
300
|
+
# parameter will be turned into a member of the underlying hash
|
|
301
|
+
def property m, *args, &block
|
|
302
|
+
|
|
303
|
+
# Return nil if it's looking for an unknown alias_for_* method
|
|
304
|
+
return nil if m =~ /^alias\_for\_.*/
|
|
305
|
+
|
|
306
|
+
# Special magic... if an unknown ? method is called, it's treated
|
|
307
|
+
# as a check against the internal hash to see if the given field
|
|
308
|
+
# has been set. For instance, priority? will check to see if the
|
|
309
|
+
# priority field has been set
|
|
310
|
+
return @_.has_key?(m.to_s[0...-1].to_sym) if m =~ /.*\?/
|
|
311
|
+
|
|
312
|
+
# Is it an unknown to_ method? e.g. to_ary ... if so, fall through to default
|
|
313
|
+
if m =~ /^to\_.*/
|
|
314
|
+
super
|
|
315
|
+
return
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
# Once we get past the special cases, check to see if we have the
|
|
319
|
+
# expected number of arguments.
|
|
320
|
+
if !args.length.within?(1..2)
|
|
321
|
+
raise NoMethodError
|
|
322
|
+
return
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
# Now we start to figure out the exact value to set
|
|
326
|
+
transform, alias_for, checker = [:transform_,:alias_for_,:check_].map {|x| "#{x}#{m}".to_sym }
|
|
327
|
+
|
|
328
|
+
v = args[0]
|
|
329
|
+
|
|
330
|
+
# First, if the value given is a ASObj, call finish on it
|
|
331
|
+
v = (v.is_a?(ASObj) ? v.finish : v) unless v == nil
|
|
332
|
+
|
|
333
|
+
# If it's an Enumerable, but not a Hash, convert to an Array using Map,
|
|
334
|
+
# If each of the member items are ASObj's call finish.
|
|
335
|
+
v = v.map {|i| i.is_a?(ASObj) ? i.finish : i } if v.is_a?(Enumerable) && !v.is_a?(Hash)
|
|
336
|
+
|
|
337
|
+
# If the value is a Time object, let's make sure it's ISO 8601
|
|
338
|
+
v = v.iso8601 if v.is_a? Time
|
|
339
|
+
|
|
340
|
+
# Finally, do the object_type specific transform, if any
|
|
341
|
+
# note, this could potentially undo the previous checks if
|
|
342
|
+
# the transform provided by a given spec module isn't well
|
|
343
|
+
# behaved. that's ok tho, we'll trust that it's doing the
|
|
344
|
+
# right thing and just move on ... we're going to be validating
|
|
345
|
+
# next anyway
|
|
346
|
+
v = send transform, v if method? transform
|
|
347
|
+
|
|
348
|
+
# Now let's do validation... unless lenient is set
|
|
349
|
+
if !args[1] && strict?
|
|
350
|
+
ok = method?(checker) ? send(checker,v) : missing_check(v)
|
|
351
|
+
raise ArgumentError unless ok
|
|
352
|
+
end
|
|
353
|
+
m = send alias_for if method? alias_for
|
|
354
|
+
@_[m] = v unless v == nil
|
|
355
|
+
@_.delete m if v == nil
|
|
356
|
+
end
|
|
357
|
+
alias :method_missing :property
|
|
358
|
+
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
class << ASObj
|
|
362
|
+
|
|
363
|
+
# Performs the actual work of creating an ASObj and executing
|
|
364
|
+
# the associated block to build it up, then freezing the
|
|
365
|
+
# ASObj before returning it
|
|
366
|
+
def generate object_type=nil, do_not_set_object_type=false, &block
|
|
367
|
+
m = ASObj.new object_type
|
|
368
|
+
m[:objectType] = object_type unless do_not_set_object_type
|
|
369
|
+
m.instance_eval &block unless not block_given?
|
|
370
|
+
m.freeze
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
# Creates a new ASObj by copying from another one
|
|
374
|
+
def from other, *without, &block
|
|
375
|
+
raise ArgumentError unless other.one_of_type?(ASObj,Hash)
|
|
376
|
+
m = ASObj.new other.__object_type
|
|
377
|
+
m.pretty if other.pretty?
|
|
378
|
+
m.lenient if other.lenient?
|
|
379
|
+
other.finish.each_pair {|k,y| m[k] = y unless without.include? k }
|
|
380
|
+
m.instance_eval &block unless not block_given?
|
|
381
|
+
m.freeze
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
end
|
|
385
|
+
|
|
386
|
+
# The base module for all Validation Spec Modules.. these
|
|
387
|
+
# define the requirements for the various Activity Streams
|
|
388
|
+
# object types
|
|
389
|
+
module Spec
|
|
390
|
+
|
|
391
|
+
# by default, allow all values if a specific check hasn't been provided
|
|
392
|
+
# Spec modules can override this behavior by defining their own missing_check
|
|
393
|
+
def missing_check v
|
|
394
|
+
true
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
# Defines the various utility methods used to build Spec modules
|
|
398
|
+
module Defs
|
|
399
|
+
|
|
400
|
+
# Maps an input symbol to a property name in the hash
|
|
401
|
+
def def_alias sym, name
|
|
402
|
+
define_method("alias_for_#{sym}".to_sym) {
|
|
403
|
+
name
|
|
404
|
+
} if name
|
|
405
|
+
module_function "alias_for_#{sym}".to_sym
|
|
406
|
+
end
|
|
407
|
+
|
|
408
|
+
# Defines the method for validating the value of a
|
|
409
|
+
# specific property.
|
|
410
|
+
def def_checker sym, &block
|
|
411
|
+
sym = "check_#{sym}".to_sym
|
|
412
|
+
define_method sym,&block
|
|
413
|
+
module_function sym
|
|
414
|
+
end
|
|
415
|
+
|
|
416
|
+
# Defines a transform for the value of a specific property
|
|
417
|
+
def def_transform sym, &block
|
|
418
|
+
sym = "transform_#{sym}".to_sym
|
|
419
|
+
if block_given?
|
|
420
|
+
define_method sym,&block
|
|
421
|
+
else
|
|
422
|
+
define_method(sym) {|v|v} # just return self if no transform defined
|
|
423
|
+
end
|
|
424
|
+
module_function sym
|
|
425
|
+
end
|
|
426
|
+
|
|
427
|
+
# Mark def_alias, def_checker and def_transform as private
|
|
428
|
+
# these should only be called from within the Defs module
|
|
429
|
+
private :def_alias, :def_checker, :def_transform
|
|
430
|
+
|
|
431
|
+
# Define a property as being an absolute IRI
|
|
432
|
+
def def_absolute_iri sym, name=nil
|
|
433
|
+
def_transform(sym) {|v|
|
|
434
|
+
next nil if v == nil
|
|
435
|
+
Addressable::URI.parse(v)
|
|
436
|
+
}
|
|
437
|
+
def_checker(sym) { |v|
|
|
438
|
+
# v must be an absolute IRI
|
|
439
|
+
!v || is_absolute_iri?(v)
|
|
440
|
+
}
|
|
441
|
+
def_alias sym, name if name
|
|
442
|
+
end
|
|
443
|
+
|
|
444
|
+
# Define a property as being an ISO 8601 DateTime
|
|
445
|
+
def def_date_time sym, name=nil
|
|
446
|
+
def_transform(sym) {|v|
|
|
447
|
+
next v if v == nil || v.is_a?(Time)
|
|
448
|
+
Time.parse(v.to_s) rescue v
|
|
449
|
+
}
|
|
450
|
+
def_checker(sym) { |v|
|
|
451
|
+
# v must be parseable as a time
|
|
452
|
+
next true if v == nil || v.is_a?(Time)
|
|
453
|
+
Time.parse(v.to_s) rescue next false
|
|
454
|
+
true
|
|
455
|
+
}
|
|
456
|
+
def_alias sym, name if name
|
|
457
|
+
end
|
|
458
|
+
|
|
459
|
+
# Define a property as being an IRI ... does not have to be absolute
|
|
460
|
+
def def_iri sym, name=nil
|
|
461
|
+
def_transform(sym) {|v|
|
|
462
|
+
next nil if v == nill
|
|
463
|
+
Addressable::URI.parse(v)}
|
|
464
|
+
def_checker(sym) { |v|
|
|
465
|
+
# v must be parseable as a URI
|
|
466
|
+
!v || Addressable::URI.parse(v) rescue false
|
|
467
|
+
}
|
|
468
|
+
def_alias sym, name if name
|
|
469
|
+
end
|
|
470
|
+
|
|
471
|
+
# Define a property as being a string, an additional block
|
|
472
|
+
# can be passed in to perform additional checking (e.g. regex matching, etc)
|
|
473
|
+
def def_string sym, name=nil, &block
|
|
474
|
+
def_transform(sym) {|v|
|
|
475
|
+
next nil if v == nil
|
|
476
|
+
v.to_s
|
|
477
|
+
}
|
|
478
|
+
def_checker(sym) { |v|
|
|
479
|
+
# v will be converted to a string, then checked against the optional
|
|
480
|
+
# block... if the block returns false, raise a validation error
|
|
481
|
+
next true if v == nil
|
|
482
|
+
next block.call(v.to_s) if block_given?
|
|
483
|
+
true
|
|
484
|
+
}
|
|
485
|
+
def_alias sym, name if name
|
|
486
|
+
end
|
|
487
|
+
|
|
488
|
+
# Define a property as being an ASObj.
|
|
489
|
+
def def_object sym, object_type=nil, name=nil
|
|
490
|
+
def_transform(sym)
|
|
491
|
+
def_checker(sym) { |v|
|
|
492
|
+
next true if v == nil
|
|
493
|
+
# v must be an instance of the given object_type
|
|
494
|
+
if object_type
|
|
495
|
+
next false if v.__object_type != object_type
|
|
496
|
+
end
|
|
497
|
+
# right now this is pretty strict... we only allow Hash or ASObj
|
|
498
|
+
# instances to be passed. TODO: see if we can relax this to enable
|
|
499
|
+
# more flexible duck typing ...
|
|
500
|
+
v.one_of_type? Hash, ASObj
|
|
501
|
+
}
|
|
502
|
+
def_alias sym, name if name
|
|
503
|
+
def_property(sym, object_type, name) if object_type
|
|
504
|
+
end
|
|
505
|
+
|
|
506
|
+
# Define a property as being an Array of ASObj's
|
|
507
|
+
def def_object_array sym, object_type=nil, name=nil
|
|
508
|
+
def_alias sym, name if name
|
|
509
|
+
def_transform(sym) {|v|
|
|
510
|
+
next nil if v == nil
|
|
511
|
+
orig = self[sym]
|
|
512
|
+
if v.is_a?(Array)
|
|
513
|
+
next orig ? orig + v : v
|
|
514
|
+
end
|
|
515
|
+
orig ? orig << v : [v]
|
|
516
|
+
}
|
|
517
|
+
def_checker(sym) { |v|
|
|
518
|
+
next true if v == nil
|
|
519
|
+
# v must be either an array or enumerable and each item
|
|
520
|
+
# must be either a Hash or ASObj that matches the given
|
|
521
|
+
# object_type, if any
|
|
522
|
+
next false unless (v.one_of_type?(Array, Enumerable) && !v.is_a?(Hash))
|
|
523
|
+
v.each {|x|
|
|
524
|
+
return false unless x.one_of_type? ASObj, Hash
|
|
525
|
+
return false if (object_type && x.__object_type != object_type)
|
|
526
|
+
}
|
|
527
|
+
true
|
|
528
|
+
}
|
|
529
|
+
end
|
|
530
|
+
|
|
531
|
+
# Define a property as being an Array of Strings, an additional
|
|
532
|
+
# block can be passed to perform additional checking
|
|
533
|
+
def def_string_array sym, name=nil, &block
|
|
534
|
+
def_transform(sym) {|v|
|
|
535
|
+
next nil if v == nil
|
|
536
|
+
orig = self[sym]
|
|
537
|
+
if v.one_of_type? Array, Enumerable
|
|
538
|
+
add = v.map {|x| x.to_s}
|
|
539
|
+
next orig ? orig + add : add
|
|
540
|
+
end
|
|
541
|
+
orig ? orig << v.to_s : [v.to_s]
|
|
542
|
+
}
|
|
543
|
+
def_checker(sym) { |v|
|
|
544
|
+
next true if v == nil
|
|
545
|
+
next false unless (v.one_of_type?(Array, Enumerable) && !v.is_a?(Hash))
|
|
546
|
+
v.each {|x|
|
|
547
|
+
return false unless block.call(x)
|
|
548
|
+
} if block_given?
|
|
549
|
+
true
|
|
550
|
+
}
|
|
551
|
+
def_alias sym, name if name
|
|
552
|
+
end
|
|
553
|
+
|
|
554
|
+
def def_boolean sym, name=nil
|
|
555
|
+
def_transform(sym) {|v|
|
|
556
|
+
next false if v == nil
|
|
557
|
+
v ? true : false
|
|
558
|
+
}
|
|
559
|
+
def_checker(sym) { |v|
|
|
560
|
+
v.one_of_type? TrueClass, FalseClass
|
|
561
|
+
}
|
|
562
|
+
def_alias sym, name if name
|
|
563
|
+
|
|
564
|
+
module_eval %Q/def #{sym}() property(:'#{sym}', true) end/
|
|
565
|
+
module_eval %Q/def not_#{sym}() property(:'#{sym}', false) end/
|
|
566
|
+
end
|
|
567
|
+
|
|
568
|
+
# Define a property as being a Numeric
|
|
569
|
+
def def_numeric sym, name=nil, &block
|
|
570
|
+
def_checker(sym) { |v|
|
|
571
|
+
next true if v == nil
|
|
572
|
+
return false unless v.is_a? Numeric
|
|
573
|
+
if block_given?
|
|
574
|
+
next false unless block.call v
|
|
575
|
+
end
|
|
576
|
+
true
|
|
577
|
+
}
|
|
578
|
+
def_alias sym, name if name
|
|
579
|
+
end
|
|
580
|
+
|
|
581
|
+
# Define a property as being a non-negative fixnum
|
|
582
|
+
def def_non_negative_int sym, name=nil
|
|
583
|
+
def_numeric(sym, name) {|v|
|
|
584
|
+
next false unless (v.is_a?(Fixnum) && v >= 0)
|
|
585
|
+
true
|
|
586
|
+
}
|
|
587
|
+
end
|
|
588
|
+
|
|
589
|
+
# Define a property as being a float with a bounded range
|
|
590
|
+
def def_bound_float sym, range, name=nil
|
|
591
|
+
def_numeric(sym, name) {|v|
|
|
592
|
+
next false if (range.respond_to?(:include?) && !range.include?(v))
|
|
593
|
+
true
|
|
594
|
+
}
|
|
595
|
+
end
|
|
596
|
+
|
|
597
|
+
def def_property sym, type=nil, name=nil
|
|
598
|
+
sym = sym.to_s
|
|
599
|
+
module_eval %Q/
|
|
600
|
+
def #{sym} &block
|
|
601
|
+
self[:#{name || sym}] = ASObj.generate(:#{type},true,&block)
|
|
602
|
+
end
|
|
603
|
+
/
|
|
604
|
+
end
|
|
605
|
+
private :def_property
|
|
606
|
+
end
|
|
607
|
+
|
|
608
|
+
# Ensure the the Defs module is included in all spec modules...
|
|
609
|
+
extend Defs
|
|
610
|
+
def self.included(other)
|
|
611
|
+
other.extend Defs
|
|
612
|
+
end
|
|
613
|
+
|
|
614
|
+
end
|
|
615
|
+
|
|
616
|
+
# The base spec for all ASObj's
|
|
617
|
+
module ObjectSpec
|
|
618
|
+
include Spec
|
|
619
|
+
def_string :content
|
|
620
|
+
def_string :display_name, :displayName
|
|
621
|
+
def_string :object_type, :objectType
|
|
622
|
+
def_string :summary
|
|
623
|
+
def_string :aka, :alias
|
|
624
|
+
def_date_time :updated
|
|
625
|
+
def_date_time :published
|
|
626
|
+
def_date_time :start_time, :'start-time'
|
|
627
|
+
def_date_time :end_time, :'end-time'
|
|
628
|
+
def_object :links, :links
|
|
629
|
+
def_object :author
|
|
630
|
+
def_object :img, :media_link, :image
|
|
631
|
+
def_object :source
|
|
632
|
+
def_object :location, :place
|
|
633
|
+
def_object :mood, :mood
|
|
634
|
+
def_bound_float :rating, 0.0..5.0
|
|
635
|
+
def_absolute_iri :id
|
|
636
|
+
def_iri :url
|
|
637
|
+
def_object_array :attachments
|
|
638
|
+
def_object_array :in_reply_to, nil, :inReplyTo
|
|
639
|
+
|
|
640
|
+
check = ->(x){ is_absolute_iri? x }
|
|
641
|
+
def_string_array :downstream_duplicates, :downstreamDuplicates, &check
|
|
642
|
+
def_string_array :upstream_duplicates, :upstreamDuplicates, &check
|
|
643
|
+
|
|
644
|
+
def attachment m, &block
|
|
645
|
+
property :attachments, m, &block
|
|
646
|
+
end
|
|
647
|
+
|
|
648
|
+
def downstream_duplicate m, &block
|
|
649
|
+
property :downstream_duplicates, m, &block
|
|
650
|
+
end
|
|
651
|
+
|
|
652
|
+
def upstream_duplicate m, &block
|
|
653
|
+
property :upstream_duplicates, m, &block
|
|
654
|
+
end
|
|
655
|
+
|
|
656
|
+
# Basic support for external vocabularies..
|
|
657
|
+
# Developers will have to register their own
|
|
658
|
+
# spec modules for these, but we at least
|
|
659
|
+
# provide the constructor methods
|
|
660
|
+
def ext_vocab sym, &block
|
|
661
|
+
self[sym] = ASObj.generate(sym,true,&block)
|
|
662
|
+
end
|
|
663
|
+
[:schema_org, :ld, :dc, :odata, :opengraph].each do |x|
|
|
664
|
+
module_eval "def #{x}(&block) ext_vocab(:#{x},&block) end"
|
|
665
|
+
end
|
|
666
|
+
|
|
667
|
+
# ensure that all spec object include the Defs module...
|
|
668
|
+
include Defs
|
|
669
|
+
def self.included(other)
|
|
670
|
+
other.extend Defs
|
|
671
|
+
other.module_exec {
|
|
672
|
+
def self.included(o)
|
|
673
|
+
o.extend Defs
|
|
674
|
+
end
|
|
675
|
+
}
|
|
676
|
+
end
|
|
677
|
+
|
|
678
|
+
end
|
|
679
|
+
|
|
680
|
+
module ActivitySpec
|
|
681
|
+
include ObjectSpec
|
|
682
|
+
def_string :verb do |v| is_verb? v end
|
|
683
|
+
def_string :content
|
|
684
|
+
def_string :title
|
|
685
|
+
def_object :icon, :media_link
|
|
686
|
+
def_object :generator
|
|
687
|
+
def_object :actor
|
|
688
|
+
def_object :target
|
|
689
|
+
def_object :obj, nil, :object
|
|
690
|
+
def_object :provider
|
|
691
|
+
def_object :context
|
|
692
|
+
def_object :result
|
|
693
|
+
def_object_array :to
|
|
694
|
+
def_object_array :cc
|
|
695
|
+
def_object_array :bto
|
|
696
|
+
def_object_array :bcc
|
|
697
|
+
def_bound_float :priority, 0.0..1.0
|
|
698
|
+
end
|
|
699
|
+
|
|
700
|
+
module MediaLinkSpec
|
|
701
|
+
include Spec
|
|
702
|
+
def_absolute_iri :url
|
|
703
|
+
def_non_negative_int :duration
|
|
704
|
+
def_non_negative_int :width
|
|
705
|
+
def_non_negative_int :height
|
|
706
|
+
end
|
|
707
|
+
|
|
708
|
+
module MoodSpec
|
|
709
|
+
include Spec
|
|
710
|
+
def_string :display_name, :displayName
|
|
711
|
+
def_object :img, :mediaLink, :image
|
|
712
|
+
end
|
|
713
|
+
|
|
714
|
+
module AddressSpec
|
|
715
|
+
include Spec
|
|
716
|
+
def_string :formatted
|
|
717
|
+
def_string :street_address, :streetAddress
|
|
718
|
+
def_string :locality
|
|
719
|
+
def_string :region
|
|
720
|
+
def_string :postal_code, :postalCode
|
|
721
|
+
def_string :country
|
|
722
|
+
end
|
|
723
|
+
|
|
724
|
+
module PositionSpec
|
|
725
|
+
include Spec
|
|
726
|
+
def_numeric :altitude
|
|
727
|
+
def_bound_float :longitude, -180.00..180.00
|
|
728
|
+
def_bound_float :latitude, -90.00..90.00
|
|
729
|
+
end
|
|
730
|
+
|
|
731
|
+
module PlaceSpec
|
|
732
|
+
include ObjectSpec
|
|
733
|
+
def_object :position, :position
|
|
734
|
+
def_object :address, :address
|
|
735
|
+
end
|
|
736
|
+
|
|
737
|
+
module CollectionSpec
|
|
738
|
+
include ObjectSpec
|
|
739
|
+
def_date_time :items_after, :itemsAfter
|
|
740
|
+
def_date_time :items_before, :itemsBefore
|
|
741
|
+
def_non_negative_int :items_per_page, :itemsPerPage
|
|
742
|
+
def_non_negative_int :start_index, :startIndex
|
|
743
|
+
def_non_negative_int :total_items, :totalItems
|
|
744
|
+
def_object_array :items
|
|
745
|
+
def_string_array :object_types, :objectTypes
|
|
746
|
+
|
|
747
|
+
def item m, &block
|
|
748
|
+
property :items, m, &block
|
|
749
|
+
end
|
|
750
|
+
|
|
751
|
+
end
|
|
752
|
+
|
|
753
|
+
module AVSpec
|
|
754
|
+
include ObjectSpec
|
|
755
|
+
def_string :embed_code, :embedCode
|
|
756
|
+
def_object :stream, :media_link
|
|
757
|
+
end
|
|
758
|
+
|
|
759
|
+
module FileSpec
|
|
760
|
+
include ObjectSpec
|
|
761
|
+
def_string :mime_type, :mimeType do |x| is_mime_type? x end
|
|
762
|
+
def_string :md5
|
|
763
|
+
def_absolute_iri :file_url, :fileUrl
|
|
764
|
+
end
|
|
765
|
+
|
|
766
|
+
module BinarySpec
|
|
767
|
+
include FileSpec
|
|
768
|
+
def_string :compression
|
|
769
|
+
def_string :data
|
|
770
|
+
def_non_negative_int :length
|
|
771
|
+
|
|
772
|
+
def init_hasher hash
|
|
773
|
+
require 'Digest'
|
|
774
|
+
hash_name = "#{hash.to_s.upcase}"
|
|
775
|
+
Digest.module_eval "#{hash_name}.new"
|
|
776
|
+
rescue LoadError
|
|
777
|
+
raise ArgumentError.new("Invalid Hash [#{hash}]")
|
|
778
|
+
end
|
|
779
|
+
|
|
780
|
+
def do_compression data, compress, level
|
|
781
|
+
case compress
|
|
782
|
+
when nil then return data
|
|
783
|
+
when :deflate
|
|
784
|
+
data = Zlib::Deflate.deflate(data,level)
|
|
785
|
+
when :gzip
|
|
786
|
+
data = IO.pipe { |r,w|
|
|
787
|
+
gzip = Zlib::GzipWriter.new(w,level)
|
|
788
|
+
gzip.write data
|
|
789
|
+
gzip.close
|
|
790
|
+
r.read
|
|
791
|
+
}
|
|
792
|
+
else raise ArgumentError
|
|
793
|
+
end
|
|
794
|
+
data
|
|
795
|
+
end
|
|
796
|
+
|
|
797
|
+
private :init_hasher, :do_compression
|
|
798
|
+
|
|
799
|
+
# Specify the data for the Binary object. The src must either be an IO object
|
|
800
|
+
# or a string containing a file path and name or an ArgumentError will be raised.
|
|
801
|
+
# Deflate compression by default, level 9, pass in :gzip to use Gzip compression
|
|
802
|
+
# or nil to disable compression entirely. The length and compression fields will
|
|
803
|
+
# automatically be set. This method will NOT close the src IO when it's done, you'll
|
|
804
|
+
# need to handle that yourself. Currently this doesn't do any error handling
|
|
805
|
+
# on the IO read. Also, it currently reads the entire IO stream first,
|
|
806
|
+
# buffers it into memory, then compresses before base64 encoding...
|
|
807
|
+
def data(src, options={:compress=>:deflate, :level=>9, :hash=>:md5})
|
|
808
|
+
compress = options.fetch :compress, :deflate
|
|
809
|
+
level = options.fetch :level, 9
|
|
810
|
+
hash = options.fetch :hash, :md5
|
|
811
|
+
|
|
812
|
+
if src.is_a? String
|
|
813
|
+
File.open(src, 'r') {|f| data f, options }
|
|
814
|
+
else
|
|
815
|
+
raise ArgumentError unless src.is_a? IO
|
|
816
|
+
|
|
817
|
+
# Optionally generate a hash over the data as it is read
|
|
818
|
+
if hash
|
|
819
|
+
hasher = init_hasher(hash)
|
|
820
|
+
d = src.read {|block| hasher.update block }
|
|
821
|
+
self[hash] = hasher.hexdigest
|
|
822
|
+
else
|
|
823
|
+
d = src.read
|
|
824
|
+
end
|
|
825
|
+
|
|
826
|
+
# Set the uncompressed length of the data in octets
|
|
827
|
+
self[:length] = d.length
|
|
828
|
+
|
|
829
|
+
# Apply compression if necessary
|
|
830
|
+
if compress
|
|
831
|
+
d = do_compression d, compress, level
|
|
832
|
+
self[:compress] = compress
|
|
833
|
+
end
|
|
834
|
+
|
|
835
|
+
# Set the data
|
|
836
|
+
self[:data] = Base64.urlsafe_encode64(d)
|
|
837
|
+
end
|
|
838
|
+
end
|
|
839
|
+
|
|
840
|
+
end
|
|
841
|
+
|
|
842
|
+
module EventSpec
|
|
843
|
+
include ObjectSpec
|
|
844
|
+
def_object :attended_by, :collection, :attendedBy
|
|
845
|
+
def_object :attending, :collection
|
|
846
|
+
def_object :invited, :collection
|
|
847
|
+
def_object :maybe_attending, :collection, :maybeAttending
|
|
848
|
+
def_object :not_attended_by, :collection, :notAttendedBy
|
|
849
|
+
def_object :not_attending, :collection, :notAttending
|
|
850
|
+
end
|
|
851
|
+
|
|
852
|
+
module IssueSpec
|
|
853
|
+
include ObjectSpec
|
|
854
|
+
def_string_array(:types) {|v| is_absolute_iri? v }
|
|
855
|
+
|
|
856
|
+
def type m, &block
|
|
857
|
+
property :types, m, &block
|
|
858
|
+
end
|
|
859
|
+
end
|
|
860
|
+
|
|
861
|
+
module PermissionsSpec
|
|
862
|
+
include ObjectSpec
|
|
863
|
+
def_object :scope
|
|
864
|
+
def_string_array :actions
|
|
865
|
+
|
|
866
|
+
def action m, &block
|
|
867
|
+
property :actions, m, &block
|
|
868
|
+
end
|
|
869
|
+
end
|
|
870
|
+
|
|
871
|
+
module RGSpec # For "role" and "group" objects
|
|
872
|
+
include ObjectSpec
|
|
873
|
+
def_object :members, :collection
|
|
874
|
+
end
|
|
875
|
+
|
|
876
|
+
module TaskSpec
|
|
877
|
+
include ActivitySpec
|
|
878
|
+
def_date_time :by
|
|
879
|
+
def_object_array :prerequisites, :task
|
|
880
|
+
def_object_array :supersedes, :task
|
|
881
|
+
|
|
882
|
+
def prerequisite m, &block
|
|
883
|
+
property :prerequisites, m, &block
|
|
884
|
+
end
|
|
885
|
+
|
|
886
|
+
def supersede m, &block
|
|
887
|
+
property :supersedes, m, &block
|
|
888
|
+
end
|
|
889
|
+
|
|
890
|
+
end
|
|
891
|
+
|
|
892
|
+
module ImageSpec
|
|
893
|
+
include ObjectSpec
|
|
894
|
+
def_object :full_image, :media_link, :fullImage
|
|
895
|
+
end
|
|
896
|
+
|
|
897
|
+
module BookmarkSpec
|
|
898
|
+
include ObjectSpec
|
|
899
|
+
def_absolute_iri :target_url, :targetUrl
|
|
900
|
+
end
|
|
901
|
+
|
|
902
|
+
module LinkSpec
|
|
903
|
+
include ObjectSpec
|
|
904
|
+
def_absolute_iri :href
|
|
905
|
+
def_string :title
|
|
906
|
+
def_string :hreflang do |x| is_lang_tag? x end # must be a RFC 4646 tag
|
|
907
|
+
def_string :type do |x| is_mime_type? x end # must be a valid MIME Media Type
|
|
908
|
+
end
|
|
909
|
+
|
|
910
|
+
module LinksSpec
|
|
911
|
+
include Spec
|
|
912
|
+
# Require that all properties on the Links spec are link objects
|
|
913
|
+
def missing_check v
|
|
914
|
+
v.one_of_type? Hash, LinkSpec
|
|
915
|
+
end
|
|
916
|
+
|
|
917
|
+
def link rel, include_object_type=false, &block
|
|
918
|
+
self[rel.to_sym] = ASObj.generate :link, !include_object_type, &block
|
|
919
|
+
end
|
|
920
|
+
|
|
921
|
+
def link_with_object_type rel, &block
|
|
922
|
+
link rel, true, &block
|
|
923
|
+
end
|
|
924
|
+
end
|
|
925
|
+
|
|
926
|
+
# Collect the various Specs and map to their respective object types
|
|
927
|
+
SPECS = {
|
|
928
|
+
nil => ObjectSpec,
|
|
929
|
+
:activity => ActivitySpec,
|
|
930
|
+
:media_link => MediaLinkSpec,
|
|
931
|
+
:mood => MoodSpec,
|
|
932
|
+
:address => AddressSpec,
|
|
933
|
+
:place => PlaceSpec,
|
|
934
|
+
:position => PositionSpec,
|
|
935
|
+
:collection => CollectionSpec,
|
|
936
|
+
:audio => AVSpec,
|
|
937
|
+
:video => AVSpec,
|
|
938
|
+
:binary => BinarySpec,
|
|
939
|
+
:file => FileSpec,
|
|
940
|
+
:event => EventSpec,
|
|
941
|
+
:issue => IssueSpec,
|
|
942
|
+
:permission => PermissionsSpec,
|
|
943
|
+
:role => RGSpec,
|
|
944
|
+
:group => RGSpec,
|
|
945
|
+
:task => TaskSpec,
|
|
946
|
+
:product => ImageSpec,
|
|
947
|
+
:image => ImageSpec,
|
|
948
|
+
:link => LinkSpec,
|
|
949
|
+
:links => LinksSpec
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
# override or add a new spec... be careful here.. the existing
|
|
953
|
+
# spec definitions can be overridden
|
|
954
|
+
def add_spec sym, spec
|
|
955
|
+
SPECS[sym] = spec
|
|
956
|
+
end
|
|
957
|
+
|
|
958
|
+
# create a new Spec module
|
|
959
|
+
def spec *specs, &block
|
|
960
|
+
o = Module.new.extend Spec, Spec::Defs, *specs
|
|
961
|
+
o.module_exec &block
|
|
962
|
+
o
|
|
963
|
+
end
|
|
964
|
+
|
|
965
|
+
# create a new Spec module based on ObjectSpec
|
|
966
|
+
def object_spec *specs, &block
|
|
967
|
+
spec ObjectSpec, *specs, &block
|
|
968
|
+
end
|
|
969
|
+
|
|
970
|
+
# define the template method as an alias to lambda
|
|
971
|
+
alias :template :lambda
|
|
972
|
+
|
|
973
|
+
module_function :add_spec, :spec, :object_spec
|
|
974
|
+
|
|
975
|
+
# syntactic sugar
|
|
976
|
+
LENIENT, STRICT = true, false
|
|
977
|
+
|
|
978
|
+
# basic priorities...
|
|
979
|
+
HIGHEST, HIGH, MEDIUM, NORMAL, LOW, LOWEST, NONE = 1.0, 0.75, 0.50, 0.50, 0.25, 0.00, 0.00
|
|
980
|
+
|
|
981
|
+
# Provide additional , currently experimental object types and features
|
|
982
|
+
# These may change at any time...
|
|
983
|
+
module Experimental
|
|
984
|
+
extend ActivityStreams
|
|
985
|
+
|
|
986
|
+
ANY = :'*'
|
|
987
|
+
|
|
988
|
+
def verb_object &block
|
|
989
|
+
ASObj.generate :verb,false,&block
|
|
990
|
+
end
|
|
991
|
+
|
|
992
|
+
# Experimental!! May change.. see http://goo.gl/x2XZl
|
|
993
|
+
module VerbSpec
|
|
994
|
+
include ObjectSpec
|
|
995
|
+
verb_check = ->(x){is_verb? x}
|
|
996
|
+
def_string :value, &verb_check
|
|
997
|
+
def_string_array :hypernyms, &verb_check
|
|
998
|
+
def_string_array :synonyms, &verb_check
|
|
999
|
+
def_object_array :objects, :object_combination
|
|
1000
|
+
|
|
1001
|
+
def combo &block
|
|
1002
|
+
ASObj.generate :object_combination,true,&block
|
|
1003
|
+
end
|
|
1004
|
+
|
|
1005
|
+
def hypernym x
|
|
1006
|
+
property :hypernyms, x
|
|
1007
|
+
end
|
|
1008
|
+
|
|
1009
|
+
def synonym x
|
|
1010
|
+
property :synonyms, x
|
|
1011
|
+
end
|
|
1012
|
+
|
|
1013
|
+
def obj x, &block
|
|
1014
|
+
property :foos, x, &block
|
|
1015
|
+
end
|
|
1016
|
+
end
|
|
1017
|
+
|
|
1018
|
+
module ObjectCombinationSpec
|
|
1019
|
+
include Spec
|
|
1020
|
+
def_string :actor
|
|
1021
|
+
def_string :obj, :object
|
|
1022
|
+
def_string :target
|
|
1023
|
+
def_boolean :target_required, :targetRequired
|
|
1024
|
+
def_object :templates, :object_templates
|
|
1025
|
+
|
|
1026
|
+
def target t, required=false, &block
|
|
1027
|
+
property :target, t, &block
|
|
1028
|
+
target_required if required
|
|
1029
|
+
end
|
|
1030
|
+
|
|
1031
|
+
end
|
|
1032
|
+
|
|
1033
|
+
module ObjectTemplatesSpec
|
|
1034
|
+
include Spec
|
|
1035
|
+
def missing_checker v
|
|
1036
|
+
v.is_a? String
|
|
1037
|
+
end
|
|
1038
|
+
end
|
|
1039
|
+
|
|
1040
|
+
add_spec :verb, VerbSpec
|
|
1041
|
+
add_spec :object_combination, ObjectCombinationSpec
|
|
1042
|
+
add_spec :object_templates, ObjectTemplatesSpec
|
|
1043
|
+
|
|
1044
|
+
end # END EXPERIMENTAL MODULE
|
|
1045
|
+
|
|
1046
|
+
end
|
|
1047
|
+
|
|
1048
|
+
# some syntactic sugar for Fixnums... useful for
|
|
1049
|
+
# working with Time .. e.g. updated now - 1.week #updated one week ago
|
|
1050
|
+
class Fixnum
|
|
1051
|
+
|
|
1052
|
+
# true if this number is within the given range
|
|
1053
|
+
def within? r
|
|
1054
|
+
raise ArgumentError if not r.is_a?Range
|
|
1055
|
+
r.include? self
|
|
1056
|
+
end unless method_defined?(:within?)
|
|
1057
|
+
|
|
1058
|
+
# treats the fixnum as a representation of a number
|
|
1059
|
+
# of milliseconds, returns the approximate total number
|
|
1060
|
+
# of seconds represented e.g. 1000.milliseconds => 1
|
|
1061
|
+
# fractional seconds are truncated (rounded down)
|
|
1062
|
+
def milliseconds
|
|
1063
|
+
self / 1000
|
|
1064
|
+
end unless method_defined?(:milliseconds)
|
|
1065
|
+
|
|
1066
|
+
# treats the fixnum as a representation of a number
|
|
1067
|
+
# of seconds
|
|
1068
|
+
def seconds
|
|
1069
|
+
self
|
|
1070
|
+
end unless method_defined?(:seconds)
|
|
1071
|
+
|
|
1072
|
+
# treats the fixnum as a representation of a
|
|
1073
|
+
# number of minutes and returns the total number
|
|
1074
|
+
# of seconds represented.. e.g. 2.minutes => 120,
|
|
1075
|
+
# 3.minutes => 180
|
|
1076
|
+
def minutes
|
|
1077
|
+
seconds * 60
|
|
1078
|
+
end unless method_defined?(:minutes)
|
|
1079
|
+
|
|
1080
|
+
# treats the fixnum as a representation of a
|
|
1081
|
+
# number of hours and returns the total number
|
|
1082
|
+
# of seconds represented.. e.g. 2.hours => 7200
|
|
1083
|
+
def hours
|
|
1084
|
+
minutes * 60
|
|
1085
|
+
end unless method_defined?(:hours)
|
|
1086
|
+
|
|
1087
|
+
# treats the fixnum as a representation of a
|
|
1088
|
+
# number of days and returns the total number
|
|
1089
|
+
# of seconds represented.. e.g. 2.days => 172800
|
|
1090
|
+
def days
|
|
1091
|
+
hours * 24
|
|
1092
|
+
end unless method_defined?(:days)
|
|
1093
|
+
|
|
1094
|
+
# treats the fixnum as a representatin of a
|
|
1095
|
+
# number of weeks and returns the total number
|
|
1096
|
+
# of seconds represented.. e.g. 2.weeks => 1209600
|
|
1097
|
+
def weeks
|
|
1098
|
+
days * 7
|
|
1099
|
+
end unless method_defined?(:weeks)
|
|
1100
|
+
|
|
1101
|
+
alias second seconds unless method_defined?(:second)
|
|
1102
|
+
alias minute minutes unless method_defined?(:minute)
|
|
1103
|
+
alias hour hours unless method_defined?(:hour)
|
|
1104
|
+
alias day days unless method_defined?(:day)
|
|
1105
|
+
alias week weeks unless method_defined?(:week)
|
|
1106
|
+
|
|
1107
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: streams
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
prerelease:
|
|
6
|
+
platform: ruby
|
|
7
|
+
authors:
|
|
8
|
+
- James M Snell
|
|
9
|
+
autorequire:
|
|
10
|
+
bindir: bin
|
|
11
|
+
cert_chain: []
|
|
12
|
+
date: 2012-09-29 00:00:00.000000000 Z
|
|
13
|
+
dependencies: []
|
|
14
|
+
description: An Activity Streams DSL
|
|
15
|
+
email: jasnell@gmail.com
|
|
16
|
+
executables: []
|
|
17
|
+
extensions: []
|
|
18
|
+
extra_rdoc_files: []
|
|
19
|
+
files:
|
|
20
|
+
- lib/streams.rb
|
|
21
|
+
homepage: https://github.com/jasnell/js.strea.ms
|
|
22
|
+
licenses: []
|
|
23
|
+
post_install_message:
|
|
24
|
+
rdoc_options: []
|
|
25
|
+
require_paths:
|
|
26
|
+
- lib
|
|
27
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
28
|
+
none: false
|
|
29
|
+
requirements:
|
|
30
|
+
- - ! '>='
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '0'
|
|
33
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
34
|
+
none: false
|
|
35
|
+
requirements:
|
|
36
|
+
- - ! '>='
|
|
37
|
+
- !ruby/object:Gem::Version
|
|
38
|
+
version: '0'
|
|
39
|
+
requirements: []
|
|
40
|
+
rubyforge_project:
|
|
41
|
+
rubygems_version: 1.8.24
|
|
42
|
+
signing_key:
|
|
43
|
+
specification_version: 3
|
|
44
|
+
summary: An Activity Streams DSL
|
|
45
|
+
test_files: []
|