numerousapp 0.9.0 → 0.9.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.
- checksums.yaml +4 -4
- data/lib/numerousapp.rb +425 -83
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 01cd1c5e889cbf214c82da8f8901ea435602708e
|
4
|
+
data.tar.gz: 8772cd439ca04a25860ddf8c47e4f836c7d46606
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 15582f2a034419114a327c990326a08db2455f1c0f4a30eb8e3cb15bcfbd89e677d93598566eae20754316a8c4d7cf887703f23e05acf33f29f7bf77b4f25f6c
|
7
|
+
data.tar.gz: 5c47dd449c711afce6e558b69e5fe752a331ce55e0374e19b2f7ee3e2bf6745050a613d7bdb44feeb7884c898963be8db8aa99c4783b976d0237868469bca6e4
|
data/lib/numerousapp.rb
CHANGED
@@ -38,7 +38,22 @@ require 'json'
|
|
38
38
|
require 'net/http'
|
39
39
|
require 'uri'
|
40
40
|
|
41
|
+
#
|
42
|
+
# NumerousError exceptions indicate errors from the server
|
43
|
+
#
|
41
44
|
class NumerousError < StandardError
|
45
|
+
#
|
46
|
+
# @!attribute msg
|
47
|
+
# human-targeted error message as a string
|
48
|
+
#
|
49
|
+
# @!attribute code
|
50
|
+
# HTTP error code (e.g., 404)
|
51
|
+
#
|
52
|
+
# @!attribute details
|
53
|
+
# hash containing more ad-hoc information, most usefully :id which
|
54
|
+
# is the URL that was used in the request to the server
|
55
|
+
#
|
56
|
+
|
42
57
|
def initialize(msg, code, details)
|
43
58
|
super(msg)
|
44
59
|
@code = code
|
@@ -48,20 +63,47 @@ class NumerousError < StandardError
|
|
48
63
|
attr_accessor :code, :details
|
49
64
|
end
|
50
65
|
|
66
|
+
#
|
67
|
+
# A NumerousAuthError occurs when the server rejects your credentials,
|
68
|
+
# Usually means your apiKey is (or has become) bad.
|
69
|
+
#
|
51
70
|
class NumerousAuthError < NumerousError
|
52
71
|
end
|
53
72
|
|
73
|
+
#
|
74
|
+
# A NumerousMetricConflictError occurs when you write to a metric
|
75
|
+
# and specified "only if changed" and your value
|
76
|
+
# was (already) the current value
|
77
|
+
#
|
54
78
|
class NumerousMetricConflictError < NumerousError
|
55
79
|
end
|
56
80
|
|
57
81
|
#
|
58
|
-
#
|
59
|
-
#
|
60
|
-
# of talking to the
|
82
|
+
# == NumerousClientInternals
|
83
|
+
#
|
84
|
+
# Handles details of talking to the numerousapp.com server, including
|
85
|
+
# the (basic) authentication, handling chunked APIs, json vs multipart APIs,
|
86
|
+
# fixing up a few server response quirks, and so forth. It is not meant
|
87
|
+
# for use outside of Numerous and NumerousMetric.
|
61
88
|
#
|
62
89
|
class NumerousClientInternals
|
63
90
|
|
91
|
+
#
|
92
|
+
# @param apiKey [String] API authentication key
|
93
|
+
# @param server [String] Optional (keyword arg). Server name.
|
94
|
+
#
|
95
|
+
# @!attribute agentString
|
96
|
+
# @return [String] User agent string sent to the server.
|
97
|
+
#
|
98
|
+
# @!attribute [r] serverName
|
99
|
+
# @return [String] FQDN of the target NumerousApp server.
|
100
|
+
#
|
101
|
+
# @!attribute [r] debugLevel
|
102
|
+
# @return [Fixnum] Current debugging level; use debug() method to change.
|
103
|
+
#
|
64
104
|
def initialize(apiKey, server:'api.numerousapp.com')
|
105
|
+
|
106
|
+
|
65
107
|
@serverName = server
|
66
108
|
@auth = { user: apiKey, password: "" }
|
67
109
|
u = URI.parse("https://"+server)
|
@@ -74,7 +116,13 @@ class NumerousClientInternals
|
|
74
116
|
@debugLevel = 0
|
75
117
|
end
|
76
118
|
attr_accessor :agentString
|
119
|
+
attr_reader :serverName, :debugLevel
|
77
120
|
|
121
|
+
# Set the debug level
|
122
|
+
#
|
123
|
+
# @param [Fixnum] lvl
|
124
|
+
# The desired debugging level. Greater than zero turns on debugging.
|
125
|
+
# @return [Fixnum] the previous debugging level.
|
78
126
|
def debug(lvl=1)
|
79
127
|
prev = @debugLevel
|
80
128
|
@debugLevel = lvl
|
@@ -88,7 +136,7 @@ class NumerousClientInternals
|
|
88
136
|
|
89
137
|
protected
|
90
138
|
|
91
|
-
VersionString = '
|
139
|
+
VersionString = '20141224.1'
|
92
140
|
|
93
141
|
MethMap = {
|
94
142
|
GET: Net::HTTP::Get,
|
@@ -96,7 +144,7 @@ class NumerousClientInternals
|
|
96
144
|
PUT: Net::HTTP::Put,
|
97
145
|
DELETE: Net::HTTP::Delete
|
98
146
|
}
|
99
|
-
|
147
|
+
private_constant :MethMap
|
100
148
|
|
101
149
|
#
|
102
150
|
# This gathers all the relevant information for a given API
|
@@ -298,6 +346,9 @@ class NumerousClientInternals
|
|
298
346
|
|
299
347
|
# generic iterator for chunked APIs
|
300
348
|
def chunkedIterator(info, subs={}, block)
|
349
|
+
# if you didn't specify a block... there's no point in doing anything
|
350
|
+
if not block; return nil; end
|
351
|
+
|
301
352
|
api = makeAPIcontext(info, :GET, subs)
|
302
353
|
list = []
|
303
354
|
nextURL = api[:basePath]
|
@@ -323,12 +374,47 @@ class NumerousClientInternals
|
|
323
374
|
end
|
324
375
|
end
|
325
376
|
|
377
|
+
#
|
378
|
+
# == Numerous
|
379
|
+
#
|
380
|
+
# Primary class for accessing the numerousapp server.
|
381
|
+
#
|
382
|
+
# === Constructor
|
383
|
+
#
|
384
|
+
# You must supply an API key:
|
385
|
+
# nr = Numerous.new('nmrs_3xblahblah')
|
386
|
+
#
|
387
|
+
# You can optionally override the built-in server name
|
388
|
+
# nr = Numerous.new('nmrs_3xblahblah', server:'test.server.com')
|
389
|
+
#
|
390
|
+
# === Server return values
|
391
|
+
#
|
392
|
+
# For most operations the NumerousApp server returns a JSON representation
|
393
|
+
# of the current or modified object state. This is converted to a ruby
|
394
|
+
# Hash of <string-key, value> pairs and returned from the appropriate methods.
|
395
|
+
#
|
396
|
+
# For some operations the server returns only a success/failure code.
|
397
|
+
# In those cases there is no useful return value from the method; the
|
398
|
+
# method succeeds or else raises an exception (containing the failure code).
|
399
|
+
#
|
400
|
+
# For the collection operations the server returns a JSON array of dictionary
|
401
|
+
# representations, possibly "chunked" into multiple request/response operations.
|
402
|
+
# The enumerator methods (e.g., "metrics") implement lazy-fetch and hide
|
403
|
+
# the details of the chunking from you. They simply yield each individual
|
404
|
+
# item (string-key Hash) to your block.
|
405
|
+
#
|
406
|
+
# === Exception handling
|
407
|
+
#
|
408
|
+
# Almost every API that interacts with the server will potentially
|
409
|
+
# raise a {NumerousError} (or subclass thereof). This is not noted specifically
|
410
|
+
# in the doc for each method unless it might be considered a "surprise"
|
411
|
+
# (e.g., ping always returns true else raises an exception). Rescue as needed.
|
412
|
+
#
|
326
413
|
|
327
414
|
class Numerous < NumerousClientInternals
|
328
415
|
|
329
416
|
# path info for the server-level APIs: create a metric, get user info, etc
|
330
|
-
|
331
|
-
APIInfo = {
|
417
|
+
APIInfo = {
|
332
418
|
# POST to this to create a metric
|
333
419
|
create: {
|
334
420
|
path: '/v1/metrics',
|
@@ -363,57 +449,116 @@ class Numerous < NumerousClientInternals
|
|
363
449
|
# no entry needed for GET because no special codes etc
|
364
450
|
}
|
365
451
|
}
|
452
|
+
private_constant :APIInfo
|
366
453
|
|
367
|
-
|
368
|
-
#
|
454
|
+
#
|
455
|
+
# Obtain user attributes
|
456
|
+
#
|
457
|
+
# @param [String] userId
|
458
|
+
# optional - numeric id (represented as a string) of user
|
459
|
+
# @return [Hash] user representation (string-key hash)
|
460
|
+
#
|
369
461
|
def user(userId:nil)
|
370
462
|
api = makeAPIcontext(APIInfo[:user], :GET, {userId: userId})
|
371
463
|
return simpleAPI(api)
|
372
464
|
end
|
373
465
|
|
374
|
-
# set the user's photo
|
375
|
-
# imageDataOrReadable is the raw binary image data OR
|
376
|
-
# an object with a read method (e.g., an open file)
|
377
|
-
# mimeType defaults to image/jpeg but you can specify as needed
|
378
466
|
#
|
379
|
-
#
|
380
|
-
#
|
467
|
+
# Set the user's photo
|
468
|
+
#
|
469
|
+
# @note the server enforces an undocumented maximum data size.
|
470
|
+
# Exceeding the limit will raise a NumerousError (HTTP 413 / Too Large)
|
471
|
+
# @param [String,#read] imageDataOrReadable
|
472
|
+
# Either a binary-data string of the image data or an object
|
473
|
+
# with a "read" method. The entire data stream will be read.
|
474
|
+
# @param [String] mimeType
|
475
|
+
# Optional(keyword arg). Mime type.
|
476
|
+
# @return [Hash] updated user representation (string-key hash)
|
477
|
+
#
|
381
478
|
def userPhoto(imageDataOrReadable, mimeType:'image/jpeg')
|
382
479
|
api = makeAPIcontext(APIInfo[:user], :photo)
|
383
480
|
mpart = { :f => imageDataOrReadable, :mimeType => mimeType }
|
384
481
|
return simpleAPI(api, multipart: mpart)
|
385
482
|
end
|
386
483
|
|
387
|
-
#
|
388
|
-
#
|
389
|
-
|
390
|
-
#
|
484
|
+
#
|
485
|
+
# All metrics for the given user (default is your own)
|
486
|
+
#
|
487
|
+
# @param [String] userId
|
488
|
+
# optional - numeric id (represented as a string) of user
|
489
|
+
# @yield [m] metrics
|
490
|
+
# @yieldparam m [Hash] String-key representation of one metric
|
491
|
+
# @return self
|
492
|
+
#
|
391
493
|
def metrics(userId:nil, &block)
|
392
494
|
chunkedIterator(APIInfo[:metricsCollection], { userId: userId }, block)
|
393
495
|
return self
|
394
496
|
end
|
395
497
|
|
396
|
-
#
|
498
|
+
#
|
499
|
+
# All subscriptions for the given user (default is your own)
|
500
|
+
#
|
501
|
+
# @param [String] userId
|
502
|
+
# optional - numeric id (represented as a string) of user
|
503
|
+
# @yield [s] subscriptions
|
504
|
+
# @yieldparam s [Hash] String-key representation of one subscription
|
505
|
+
# @return self
|
506
|
+
#
|
397
507
|
def subscriptions(userId:nil, &block)
|
398
508
|
chunkedIterator(APIInfo[:subscriptions], { userId: userId }, block)
|
399
509
|
return self
|
400
510
|
end
|
401
511
|
|
402
512
|
|
403
|
-
|
404
|
-
#
|
513
|
+
#
|
514
|
+
# Obtain array of the "most popular" metrics.
|
515
|
+
#
|
516
|
+
# @note this returns the array; it is not an Enumerator
|
517
|
+
#
|
518
|
+
# @param [Fixnum] count
|
519
|
+
# optional - number of metrics to return
|
520
|
+
# @return [Array] Array of hashes (metric string dicts). Each element
|
521
|
+
# represents a particular popular metric.
|
522
|
+
#
|
405
523
|
def mostPopular(count:nil)
|
406
524
|
api = makeAPIcontext(APIInfo[:popular], :GET, {count: count})
|
407
525
|
return simpleAPI(api)
|
408
526
|
end
|
409
527
|
|
410
|
-
#
|
528
|
+
#
|
529
|
+
# Verify connectivity to the server
|
530
|
+
#
|
531
|
+
# @return [true] Always returns true connectivity if ok.
|
532
|
+
# Raises an exception otherwise.
|
533
|
+
# @raise [NumerousAuthError] Your credentials are no good.
|
534
|
+
# @raise [NumerousError] Other server (or network) errors.
|
535
|
+
#
|
411
536
|
def ping
|
412
537
|
ignored = user()
|
413
|
-
return true # errors
|
538
|
+
return true # errors raise exceptions
|
414
539
|
end
|
415
540
|
|
541
|
+
#
|
542
|
+
# Create a brand new metric on the server.
|
543
|
+
#
|
544
|
+
# @param label [String] Required. Label for the metric.
|
545
|
+
# @param value [Fixnum,Float] Optional (keyword arg). Initial value.
|
546
|
+
# @param attrs [Hash] Optional (keyword arg). Initial attributes.
|
547
|
+
# @return [NumerousMetric]
|
548
|
+
#
|
549
|
+
# @example Create a metric with label 'bozo' and set value to 17
|
550
|
+
# nr = Numerous.new('nmrs_3vblahblah')
|
551
|
+
# m = nr.createMetric('bozo')
|
552
|
+
# m.write(17)
|
553
|
+
#
|
554
|
+
# @example Same example using the value keyword argument.
|
555
|
+
# m = nr.createMetric('bozo', value:17)
|
556
|
+
#
|
557
|
+
# @example Same example but also setting the description attribute
|
558
|
+
# m = nr.createMetric('bozo', value:17, attrs:{"description" => "a clown"})
|
559
|
+
#
|
416
560
|
def createMetric(label, value:nil, attrs:{})
|
561
|
+
|
417
562
|
api = makeAPIcontext(APIInfo[:create], :POST)
|
418
563
|
|
419
564
|
j = attrs.clone
|
@@ -425,29 +570,74 @@ class Numerous < NumerousClientInternals
|
|
425
570
|
return metric(v['id'])
|
426
571
|
end
|
427
572
|
|
428
|
-
#
|
429
|
-
#
|
430
|
-
#
|
573
|
+
#
|
574
|
+
# Instantiate a metric object to access a metric from the server.
|
575
|
+
# @return [NumerousMetric] metric object
|
576
|
+
# @param id [String]
|
577
|
+
# Required. Metric ID (something like '2319923751024'). NOTE: If id
|
578
|
+
# is bogus this will still "work" but (of course) errors will be
|
579
|
+
# raised when you do something with the metric.
|
580
|
+
# @see #createMetric createMetric
|
581
|
+
# @see NumerousMetric#validate validate
|
582
|
+
#
|
431
583
|
def metric(id)
|
432
584
|
return NumerousMetric.new(id, self)
|
433
585
|
end
|
434
586
|
|
435
587
|
end
|
436
588
|
|
589
|
+
#
|
590
|
+
# == NumerousMetric
|
591
|
+
#
|
592
|
+
# Class for individual Numerous metrics
|
593
|
+
#
|
594
|
+
# You instantiate these hanging off of a particular Numerous connection:
|
595
|
+
# nr = Numerous.new('nmrs_3xblahblah')
|
596
|
+
# m = nr.metric('754623094815712984')
|
597
|
+
#
|
598
|
+
# For most operations the NumerousApp server returns a JSON representation
|
599
|
+
# of the current or modified object state. This is converted to a ruby
|
600
|
+
# Hash of <string-key, value> pairs and returned from the appropriate methods.
|
601
|
+
# A few of the methods return only one item from the Hash (e.g., read
|
602
|
+
# will return just the naked number unless you ask it for the entire dictionary)
|
603
|
+
#
|
604
|
+
# For some operations the server returns only a success/failure code.
|
605
|
+
# In those cases there is no useful return value from the method; the
|
606
|
+
# method succeeds or else raises an exception (containing the failure code).
|
607
|
+
#
|
608
|
+
# For the collection operations the server returns a JSON array of dictionary
|
609
|
+
# representations, possibly "chunked" into multiple request/response operations.
|
610
|
+
# The enumerator methods (e.g., "events") implement lazy-fetch and hide
|
611
|
+
# the details of the chunking from you. They simply yield each individual
|
612
|
+
# item (string-key Hash) to your block.
|
613
|
+
#
|
437
614
|
|
438
615
|
class NumerousMetric < NumerousClientInternals
|
616
|
+
#
|
617
|
+
# @!attribute [r] id
|
618
|
+
# @return [String] The metric ID string.
|
619
|
+
#
|
620
|
+
|
621
|
+
# Constructor for a NumerousMetric
|
622
|
+
#
|
623
|
+
# @param [String] id The metric ID string.
|
624
|
+
# @param [Numerous] nr
|
625
|
+
# The {Numerous} object that will be used to access this metric.
|
439
626
|
def initialize(id, nr)
|
440
627
|
@id = id
|
441
628
|
@nr = nr
|
442
629
|
end
|
443
|
-
|
630
|
+
attr_reader :id
|
444
631
|
|
445
|
-
#
|
632
|
+
#
|
633
|
+
# Obtain the {Numerous} server object associated with a metric.
|
634
|
+
# @return [Numerous]
|
635
|
+
#
|
446
636
|
def getServer()
|
447
637
|
return @nr
|
448
638
|
end
|
449
639
|
|
450
|
-
APIInfo = {
|
640
|
+
APIInfo = {
|
451
641
|
# read/update/delete a metric
|
452
642
|
metric: {
|
453
643
|
path: '/v1/metrics/%{metricId}' ,
|
@@ -538,6 +728,7 @@ class NumerousMetric < NumerousClientInternals
|
|
538
728
|
}
|
539
729
|
|
540
730
|
}
|
731
|
+
private_constant :APIInfo
|
541
732
|
|
542
733
|
# small wrapper to always supply the metricId substitution
|
543
734
|
def getAPI(a, mx, args={})
|
@@ -545,7 +736,14 @@ class NumerousMetric < NumerousClientInternals
|
|
545
736
|
end
|
546
737
|
private :getAPI
|
547
738
|
|
548
|
-
|
739
|
+
#
|
740
|
+
# Read the current value of a metric
|
741
|
+
# @param [Boolean] dictionary
|
742
|
+
# If true the entire metric will be returned as a string-key Hash;
|
743
|
+
# else (false/default) a bare number (Fixnum or Float) is returned.
|
744
|
+
# @return [Fixnum|Float] if dictionary is false (or defaulted).
|
745
|
+
# @return [Hash] if dictionary is true.
|
746
|
+
#
|
549
747
|
def read(dictionary: false)
|
550
748
|
api = getAPI(:metric, :GET)
|
551
749
|
v = @nr.simpleAPI(api)
|
@@ -555,12 +753,22 @@ class NumerousMetric < NumerousClientInternals
|
|
555
753
|
# "Validate" a metric object.
|
556
754
|
# There really is no way to do this in any way that carries much weight.
|
557
755
|
# However, if a user gives you a metricId and you'd like to know if
|
558
|
-
# that actually IS a metricId, this might be useful.
|
559
|
-
#
|
756
|
+
# that actually IS a metricId, this might be useful.
|
757
|
+
#
|
758
|
+
# @example
|
759
|
+
# someId = ... get a metric ID from someone ...
|
760
|
+
# m = nr.metric(someId)
|
761
|
+
# if not m.validate
|
762
|
+
# puts "#{someId} is not a valid metric"
|
763
|
+
# end
|
764
|
+
#
|
765
|
+
# Realize that even a valid metric can be deleted asynchronously
|
766
|
+
# and thus become invalid after being validated by this method.
|
560
767
|
#
|
561
768
|
# Reads the metric, catches the specific exceptions that occur for
|
562
769
|
# invalid metric IDs, and returns True/False. Other exceptions mean
|
563
770
|
# something else went awry (server down, bad authentication, etc).
|
771
|
+
# @return [Boolean] validity of the metric
|
564
772
|
def validate
|
565
773
|
begin
|
566
774
|
ignored = read()
|
@@ -578,44 +786,107 @@ class NumerousMetric < NumerousClientInternals
|
|
578
786
|
end
|
579
787
|
|
580
788
|
|
581
|
-
#
|
582
|
-
#
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
789
|
+
#
|
790
|
+
# So I had a really nifty %w/.each define_method hack here to generate
|
791
|
+
# these methods that follow a simple pattern. Then trying to figure out
|
792
|
+
# how to YARD document them was daunting. If it's easy someone needs to
|
793
|
+
# show me (I get the impression it's possible with some run time magic
|
794
|
+
# but it's just too hard to figure out for now). So, here we go instead...
|
795
|
+
#
|
796
|
+
|
797
|
+
# Enumerate the events of a metric. Events are value updates.
|
798
|
+
#
|
799
|
+
# @yield [e] events
|
800
|
+
# @yieldparam e [Hash] String-key representation of one metric.
|
801
|
+
# @return [NumerousMetric] self
|
802
|
+
def events(&block)
|
803
|
+
@nr.chunkedIterator(APIInfo[:events], {metricId:@id}, block)
|
804
|
+
return self
|
588
805
|
end
|
589
806
|
|
590
|
-
#
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
807
|
+
# Enumerate the stream of a metric. The stream is events and
|
808
|
+
# interactions merged together into a time-ordered stream.
|
809
|
+
#
|
810
|
+
# @yield [s] stream
|
811
|
+
# @yieldparam s [Hash] String-key representation of one stream item.
|
812
|
+
# @return [NumerousMetric] self
|
813
|
+
def stream(&block)
|
814
|
+
@nr.chunkedIterator(APIInfo[:stream], {metricId:@id}, block)
|
815
|
+
return self
|
816
|
+
end
|
817
|
+
|
818
|
+
# Enumerate the interactions (like/comment/error) of a metric.
|
819
|
+
#
|
820
|
+
# @yield [i] interactions
|
821
|
+
# @yieldparam i [Hash] String-key representation of one interaction.
|
822
|
+
# @return [NumerousMetric] self
|
823
|
+
def interactions(&block)
|
824
|
+
@nr.chunkedIterator(APIInfo[:interactions], {metricId:@id}, block)
|
825
|
+
return self
|
826
|
+
end
|
827
|
+
|
828
|
+
# Enumerate the subscriptions of a metric.
|
829
|
+
#
|
830
|
+
# @yield [s] subscriptions
|
831
|
+
# @yieldparam s [Hash] String-key representation of one subscription.
|
832
|
+
# @return [NumerousMetric] self
|
833
|
+
def subscriptions(&block)
|
834
|
+
@nr.chunkedIterator(APIInfo[:subscriptions], {metricId:@id}, block)
|
835
|
+
return self
|
836
|
+
end
|
837
|
+
|
838
|
+
|
839
|
+
# Obtain a specific metric event from the server
|
840
|
+
#
|
841
|
+
# @param [String] eId The specific event ID
|
842
|
+
# @return [Hash] The string-key hash of the event
|
843
|
+
# @raise [NumerousError] Not found (.code will be 404)
|
844
|
+
#
|
845
|
+
def event(eId)
|
846
|
+
api = getAPI(:event, :GET, {eventID:eId})
|
847
|
+
return @nr.simpleAPI(api)
|
596
848
|
end
|
597
849
|
|
850
|
+
# Obtain a specific metric interaction from the server
|
851
|
+
#
|
852
|
+
# @param [String] iId The specific interaction ID
|
853
|
+
# @return [Hash] The string-key hash of the interaction
|
854
|
+
# @raise [NumerousError] Not found (.code will be 404)
|
855
|
+
#
|
856
|
+
def interaction(iId)
|
857
|
+
api = getAPI(:interaction, :GET, {item:iId})
|
858
|
+
return @nr.simpleAPI(api)
|
859
|
+
end
|
598
860
|
|
599
|
-
#
|
600
|
-
#
|
601
|
-
#
|
602
|
-
# the userId parameter
|
861
|
+
# Obtain your subscription parameters on a given metric
|
862
|
+
#
|
863
|
+
# Note that normal users cannot see other user's subscriptions.
|
864
|
+
# Thus the "userId" parameter is somewhat pointless; you can only
|
865
|
+
# ever see your own.
|
866
|
+
# @param [String] userId
|
867
|
+
# @return [Hash] your subscription attributes
|
603
868
|
def subscription(userId=nil)
|
604
869
|
api = getAPI(:subscription, :GET, {userId: userId})
|
605
870
|
return @nr.simpleAPI(api)
|
606
871
|
end
|
607
872
|
|
608
|
-
# Subscribe to a metric.
|
873
|
+
# Subscribe to a metric.
|
874
|
+
#
|
875
|
+
# See the NumerousApp API docs for what should be
|
609
876
|
# in the dict. This function will fetch the current parameters
|
610
877
|
# and update them with the ones you supply (because the server
|
611
878
|
# does not like you supplying an incomplete dictionary here).
|
612
|
-
#
|
613
|
-
# it doesn't really matter because how often do you do this...
|
614
|
-
# You can, however, stop that with overwriteAll=True
|
879
|
+
# You can prevent the fetch/merge via overwriteAll:true
|
615
880
|
#
|
616
|
-
#
|
617
|
-
#
|
618
|
-
|
881
|
+
# Normal users cannot set other user's subscriptions.
|
882
|
+
# @param [Hash] dict
|
883
|
+
# string-key hash of subscription parameters
|
884
|
+
# @param [String] userId
|
885
|
+
# Optional (keyword arg). UserId to subscribe.
|
886
|
+
# @param [Boolean] overwriteAll
|
887
|
+
# Optional (keyword arg). If true, dict is sent without reading
|
888
|
+
# the current parameters and merging them.
|
889
|
+
def subscribe(dict, userId:nil, overwriteAll:false)
|
619
890
|
if overwriteAll
|
620
891
|
params = {}
|
621
892
|
else
|
@@ -628,12 +899,33 @@ class NumerousMetric < NumerousClientInternals
|
|
628
899
|
return @nr.simpleAPI(api, jdict:params)
|
629
900
|
end
|
630
901
|
|
631
|
-
#
|
902
|
+
# Write a value to a metric.
|
903
|
+
#
|
904
|
+
# @param [Fixnum|Float] newval Required. Value to be written.
|
905
|
+
#
|
906
|
+
# @param [Boolean] onlyIf
|
907
|
+
# Optional (keyword arg). Only creates an event at the server
|
908
|
+
# if the newval is different from the current value. Raises
|
909
|
+
# NumerousMetricConflictError if there is no change in value.
|
910
|
+
# WARNING: Not atomic at the server; it is still possible to get
|
911
|
+
# a success code result from this even if your value was already
|
912
|
+
# the current value (under simultaneous-update scenarios).
|
913
|
+
#
|
914
|
+
# @param [Boolean] add
|
915
|
+
# Optional (keyword arg). Sends the "action: ADD" attribute which
|
916
|
+
# causes the server to ADD newval to the current metric value.
|
917
|
+
# WARNING: As of Dec 2014 there is still a bug in the server that
|
918
|
+
# causes ADD operations to not be atomic. Until that bug is fixed
|
919
|
+
# the semantics of ADD are no different than if you did a read/write
|
920
|
+
# (as two separate operations) yourself.
|
921
|
+
# @param [Boolean] dictionary
|
922
|
+
# If true the entire metric will be returned as a string-key Hash;
|
923
|
+
# else (false/default) the bare number (Fixnum or Float) for the
|
924
|
+
# resulting new value is returned.
|
925
|
+
# @return [Fixnum|Float] if dictionary is false (or defaulted). The new
|
926
|
+
# value of the metric is returned as a bare number.
|
927
|
+
# @return [Hash] if dictionary is true the entire new metric is returned.
|
632
928
|
#
|
633
|
-
# onlyIf=true sends the "only if it changed" feature of the NumerousAPI.
|
634
|
-
# -- throws NumerousMetricConflictError if no change
|
635
|
-
# add=true sends the "action: ADD" (the value is added to the metric)
|
636
|
-
# dictionary=true returns the full dictionary results.
|
637
929
|
def write(newval, onlyIf:false, add:false, dictionary:false)
|
638
930
|
j = { 'value' => newval }
|
639
931
|
if onlyIf
|
@@ -651,7 +943,7 @@ class NumerousMetric < NumerousClientInternals
|
|
651
943
|
# if onlyIf was specified and the error is "conflict"
|
652
944
|
# (meaning: no change), raise ConflictError specifically
|
653
945
|
if onlyIf and e.code == 409
|
654
|
-
raise NumerousMetricConflictError.new(
|
946
|
+
raise NumerousMetricConflictError.new("No Change", 0, e.details)
|
655
947
|
else
|
656
948
|
raise e # never mind, plain NumerousError is fine
|
657
949
|
end
|
@@ -660,6 +952,20 @@ class NumerousMetric < NumerousClientInternals
|
|
660
952
|
return (if dictionary then v else v['value'] end)
|
661
953
|
end
|
662
954
|
|
955
|
+
# Update parameters of a metric (such as "description", "label", etc).
|
956
|
+
# Not to be used (won't work) to update a metric's value.
|
957
|
+
#
|
958
|
+
# @param [Hash] dict
|
959
|
+
# string-key Hash of the parameters to be updated.
|
960
|
+
# @param [Boolean] overwriteAll
|
961
|
+
# Optional (keyword arg). If false (default), this method will first
|
962
|
+
# read the current metric parameters from the server and merge them
|
963
|
+
# with your updates before writing them back. If true your supplied
|
964
|
+
# dictionary will become the entirety of the metric's parameters, and
|
965
|
+
# any parameters you did not include in your dictionary will revert to
|
966
|
+
# their default values.
|
967
|
+
# @return [Hash] string-key Hash of the new metric parameters.
|
968
|
+
#
|
663
969
|
def update(dict, overwriteAll:false)
|
664
970
|
newParams = (if overwriteAll then {} else read(dictionary:true) end)
|
665
971
|
dict.each { |k, v| newParams[k] = v }
|
@@ -679,6 +985,8 @@ class NumerousMetric < NumerousClientInternals
|
|
679
985
|
#
|
680
986
|
# "Like" a metric
|
681
987
|
#
|
988
|
+
# @return [String] The ID of the resulting interaction (the "like")
|
989
|
+
#
|
682
990
|
def like
|
683
991
|
# a like is written as an interaction
|
684
992
|
return writeInteraction({ 'kind' => 'like' })
|
@@ -687,6 +995,9 @@ class NumerousMetric < NumerousClientInternals
|
|
687
995
|
#
|
688
996
|
# Write an error to a metric
|
689
997
|
#
|
998
|
+
# @param [String] errText The error text to write.
|
999
|
+
# @return [String] The ID of the resulting interaction (the "error")
|
1000
|
+
#
|
690
1001
|
def sendError(errText)
|
691
1002
|
# an error is written as an interaction thusly:
|
692
1003
|
# (commentBody is used for the error text)
|
@@ -695,7 +1006,10 @@ class NumerousMetric < NumerousClientInternals
|
|
695
1006
|
end
|
696
1007
|
|
697
1008
|
#
|
698
|
-
#
|
1009
|
+
# Comment on a metric
|
1010
|
+
#
|
1011
|
+
# @param [String] ctext The comment text to write.
|
1012
|
+
# @return [String] The ID of the resulting interaction (the "comment")
|
699
1013
|
#
|
700
1014
|
def comment(ctext)
|
701
1015
|
j = { 'kind' => 'comment' , 'commentBody' => ctext }
|
@@ -703,50 +1017,68 @@ class NumerousMetric < NumerousClientInternals
|
|
703
1017
|
end
|
704
1018
|
|
705
1019
|
# set the background image for a metric
|
706
|
-
#
|
707
|
-
#
|
708
|
-
#
|
1020
|
+
# @note the server enforces an undocumented maximum data size.
|
1021
|
+
# Exceeding the limit will raise a NumerousError (HTTP 413 / Too Large)
|
1022
|
+
# @param [String,#read] imageDataOrReadable
|
1023
|
+
# Either a binary-data string of the image data or an object
|
1024
|
+
# with a "read" method. The entire data stream will be read.
|
1025
|
+
# @param [String] mimeType
|
1026
|
+
# Optional(keyword arg). Mime type.
|
1027
|
+
# @return [Hash] updated metric representation (string-key hash)
|
709
1028
|
#
|
710
|
-
# NOTE: The server enforces a size limit (I don't know it)
|
711
|
-
# and you will get an HTTP "Too Large" error if you exceed it
|
712
1029
|
def photo(imageDataOrReadable, mimeType:'image/jpeg')
|
713
1030
|
api = getAPI(:photo, :POST)
|
714
1031
|
mpart = { :f => imageDataOrReadable, :mimeType => mimeType }
|
715
1032
|
return @nr.simpleAPI(api, multipart: mpart)
|
716
1033
|
end
|
717
1034
|
|
718
|
-
#
|
719
|
-
#
|
720
|
-
#
|
721
|
-
#
|
722
|
-
# that the thing should be gone after this invocation is, in fact, true).
|
723
|
-
# Nevertheless, I let the exception come through to you in case you
|
724
|
-
# want to know if this happened. This note applies to all deletion methods.
|
725
|
-
|
1035
|
+
# Delete the metric's photo
|
1036
|
+
# @note Deleting a photo that isn't there will raise a NumerousError
|
1037
|
+
# but the error code will be 200/OK.
|
1038
|
+
# @return [nil]
|
726
1039
|
def photoDelete
|
727
1040
|
api = getAPI(:photo, :DELETE)
|
728
1041
|
v = @nr.simpleAPI(api)
|
729
1042
|
return nil
|
730
1043
|
end
|
731
1044
|
|
1045
|
+
# Delete an event (a value update)
|
1046
|
+
# @note Deleting an event that isn't there will raise a NumerousError
|
1047
|
+
# but the error code will be 200/OK.
|
1048
|
+
# @param [String] evID ID (string) of the event to be deleted.
|
1049
|
+
# @return [nil]
|
732
1050
|
def eventDelete(evID)
|
733
1051
|
api = getAPI(:event, :DELETE, {eventID:evID})
|
734
1052
|
v = @nr.simpleAPI(api)
|
735
1053
|
return nil
|
736
1054
|
end
|
737
1055
|
|
1056
|
+
# Delete an interaction (a like/comment/error)
|
1057
|
+
# @note Deleting an interaction that isn't there will raise a NumerousError
|
1058
|
+
# but the error code will be 200/OK.
|
1059
|
+
# @param [String] interID ID (string) of the interaction to be deleted.
|
1060
|
+
# @return [nil]
|
738
1061
|
def interactionDelete(interID)
|
739
1062
|
api = getAPI(:interaction, :DELETE, {item:interID})
|
740
1063
|
v = @nr.simpleAPI(api)
|
741
1064
|
return nil
|
742
1065
|
end
|
743
1066
|
|
744
|
-
#
|
745
|
-
#
|
1067
|
+
# Obtain the underlying photoURL for a metric.
|
1068
|
+
#
|
1069
|
+
# The photoURL is available in the metrics parameters so you could
|
1070
|
+
# just read(dictionary:true) and obtain it that way. However this goes
|
1071
|
+
# one step further ... the URL in the metric itself still requires
|
1072
|
+
# authentication to fetch (it then redirects to the "real" underlying
|
746
1073
|
# static photo URL). This function goes one level deeper and
|
747
|
-
# returns you an actual, publicly-fetchable, photo URL.
|
748
|
-
#
|
749
|
-
#
|
1074
|
+
# returns you an actual, publicly-fetchable, photo URL.
|
1075
|
+
#
|
1076
|
+
# IMPLEMENTATION NOTE: Fetches (and discards) the entire underlying photo,
|
1077
|
+
# because that was the easiest way to tease out the target URL from
|
1078
|
+
# the Net:HTTP library.
|
1079
|
+
#
|
1080
|
+
# @return [String, nil] URL. If there is no photo returns nil.
|
1081
|
+
#
|
750
1082
|
def photoURL
|
751
1083
|
v = read(dictionary:true)
|
752
1084
|
begin
|
@@ -762,17 +1094,27 @@ class NumerousMetric < NumerousClientInternals
|
|
762
1094
|
# some convenience functions ... but all these do is query the
|
763
1095
|
# server (read the metric) and return the given field... you could
|
764
1096
|
# do the very same yourself. So I only implemented a few useful ones.
|
1097
|
+
|
1098
|
+
# Get the label of a metric.
|
1099
|
+
#
|
1100
|
+
# @return [String] The metric label.
|
765
1101
|
def label
|
766
1102
|
v = read(dictionary:true)
|
767
1103
|
return v['label']
|
768
1104
|
end
|
769
1105
|
|
1106
|
+
# Get the URL for the metric's web representation
|
1107
|
+
#
|
1108
|
+
# @return [String] URL.
|
770
1109
|
def webURL
|
771
1110
|
v = read(dictionary:true)
|
772
1111
|
return v['links']['web']
|
773
1112
|
end
|
774
1113
|
|
775
|
-
#
|
1114
|
+
# Delete a metric (permanently). Be 100% you want this, because there
|
1115
|
+
# is absolutely no undo.
|
1116
|
+
#
|
1117
|
+
# @return [nil]
|
776
1118
|
def crushKillDestroy
|
777
1119
|
api = getAPI(:metric, :DELETE)
|
778
1120
|
v = @nr.simpleAPI(api)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: numerousapp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.9.
|
4
|
+
version: 0.9.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Neil Webber
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-12-
|
11
|
+
date: 2014-12-24 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Classes implementing the NumerousApp REST APIs for metrics. Requires
|
14
14
|
Ruby 2.x
|
@@ -43,3 +43,4 @@ signing_key:
|
|
43
43
|
specification_version: 4
|
44
44
|
summary: NumerousApp API
|
45
45
|
test_files: []
|
46
|
+
has_rdoc:
|