numerousapp 1.0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. checksums.yaml +4 -4
  2. data/lib/numerousapp.rb +195 -69
  3. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e3a77c2d75609a18065360ad1de49b131a8ce75e
4
- data.tar.gz: 476884887a6b37b9cf3c4f9bafc37124e62d996a
3
+ metadata.gz: 5bc753ba4ac5af22bec9d355a0450327a4c0415c
4
+ data.tar.gz: e5e47a6f1f2a339338bdb5fe60a66366f33fa610
5
5
  SHA512:
6
- metadata.gz: 83f23c276bbe85ae08b7f39a0b7654c3f3d27b113297e697fc59b0a060b818f1ba5f21f5bd912073bcd78649499db51124bc5944d177657c84b1072e0f34148c
7
- data.tar.gz: c7f931711ca03bbc166010e95073b95984c211f5d5e25a3ea511dab521ed03d3641a8eb92a6f0eed394d6b1d356ec52a6ddc108a247cc514ed385477d9a93fb2
6
+ metadata.gz: aba6414701c7c0ddf0e33379f3dc35a3b48070587e535be8e57ceea8247f564cc2a8a9429ac67c32dd0a028faf2ee313d548c41f18a1dbb3943ff17e9df775d1
7
+ data.tar.gz: bfbae46781b680556c3cec93a0f380d7a765443c54b9d672a6d0ca54bf4c71b4ca6bce24241a431e0b626a1f2522a31f49f0ef8f9aaf0136f853d65df149cdf1
data/lib/numerousapp.rb CHANGED
@@ -9,10 +9,10 @@
9
9
  # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
10
  # copies of the Software, and to permit persons to whom the Software is
11
11
  # furnished to do so, subject to the following conditions:
12
- #
12
+ #
13
13
  # The above copyright notice and this permission notice shall be included in
14
14
  # all copies or substantial portions of the Software.
15
- #
15
+ #
16
16
  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
17
  # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
18
  # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
@@ -26,7 +26,7 @@
26
26
  # Numerous
27
27
  # -- server-level operations: get user parameters, create metric, etc
28
28
  #
29
- # NumerousMetric
29
+ # NumerousMetric
30
30
  # -- individual metrics: read/write/like/comment, etc
31
31
  #
32
32
  # NumerousClientInternals
@@ -72,31 +72,29 @@ end
72
72
 
73
73
  #
74
74
  # A NumerousMetricConflictError occurs when you write to a metric
75
- # and specified "only if changed" and your value
75
+ # and specified "only if changed" and your value
76
76
  # was (already) the current value
77
- #
77
+ #
78
78
  class NumerousMetricConflictError < NumerousError
79
79
  end
80
80
 
81
81
  #
82
- # == NumerousClientInternals
82
+ # == NumerousClientInternals
83
83
  #
84
84
  # Handles details of talking to the numerousapp.com server, including
85
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
86
+ # fixing up a few server response quirks, and so forth. It is not meant
87
87
  # for use outside of Numerous and NumerousMetric.
88
88
  #
89
89
  class NumerousClientInternals
90
90
 
91
-
92
-
93
91
  #
94
92
  # @param apiKey [String] API authentication key
95
93
  # @param server [String] Optional (keyword arg). Server name.
96
94
  # @param throttle [Proc] Optional throttle policy
97
95
  # @param throttleData [Any] Optional data for throttle
98
96
  #
99
- # @!attribute agentString
97
+ # @!attribute agentString
100
98
  # @return [String] User agent string sent to the server.
101
99
  #
102
100
  # @!attribute [r] serverName
@@ -114,7 +112,7 @@ class NumerousClientInternals
114
112
 
115
113
  @serverName = server
116
114
  @auth = { user: apiKey, password: "" }
117
- u = URI.parse("https://"+server)
115
+ u = URI.parse("https://"+server)
118
116
  @http = Net::HTTP.new(server, u.port)
119
117
  @http.use_ssl = true # always required by NumerousApp
120
118
 
@@ -135,7 +133,12 @@ class NumerousClientInternals
135
133
  # and the default policy uses the "data" (40) as the voluntary backoff point
136
134
  #
137
135
  @arbitraryMaximumTries = 10
138
- @throttlePolicy = [ThrottleDefault, 40, nil]
136
+ voluntary = { voluntary: 40}
137
+ # you can keep the dflt throttle but just alter the voluntary param, this way:
138
+ if throttleData and not throttle
139
+ voluntary = throttleData
140
+ end
141
+ @throttlePolicy = [ThrottleDefault, voluntary, nil]
139
142
  if throttle
140
143
  @throttlePolicy = [throttle, throttleData, @throttlePolicy]
141
144
  end
@@ -148,9 +151,14 @@ class NumerousClientInternals
148
151
  attr_reader :serverName, :debugLevel
149
152
  attr_reader :statistics
150
153
 
154
+ def to_s()
155
+ oid = (2 * self.object_id).to_s(16)
156
+ return "<Numerous {#{@serverName}} @ 0x#{oid}>"
157
+ end
158
+
151
159
  # Set the debug level
152
160
  #
153
- # @param [Fixnum] lvl
161
+ # @param [Fixnum] lvl
154
162
  # The desired debugging level. Greater than zero turns on debugging.
155
163
  # @return [Fixnum] the previous debugging level.
156
164
  def debug(lvl=1)
@@ -174,7 +182,7 @@ class NumerousClientInternals
174
182
 
175
183
  protected
176
184
 
177
- VersionString = '20150125-1.0.1'
185
+ VersionString = '20150215-1.0.2'
178
186
 
179
187
  MethMap = {
180
188
  GET: Net::HTTP::Get,
@@ -251,7 +259,7 @@ class NumerousClientInternals
251
259
 
252
260
 
253
261
  # ALL api exchanges with the Numerous server go through here except
254
- # for getRedirect() which is a special case (hack) for photo URLs
262
+ # for getRedirect() which is a special case (hack) for photo URLs
255
263
  #
256
264
  # Any single request/response uses this; chunked APIs use
257
265
  # the iterator classes (which in turn come back and use this repeatedly)
@@ -270,7 +278,7 @@ class NumerousClientInternals
270
278
  # in a url and it will take precedence over the basePath if any is present
271
279
  #
272
280
  # You can pass in a dictionary jdict which will be json-ified
273
- # and sent as Content-Type: application/json. Or you can pass in
281
+ # and sent as Content-Type: application/json. Or you can pass in
274
282
  # a multipart dictionary ... this is used for posting photos
275
283
  # You should not specify both jdict and multipart
276
284
  #
@@ -289,7 +297,7 @@ class NumerousClientInternals
289
297
  # need to add logic. XXX TODO XXX
290
298
  path = URI.parse(url).request_uri
291
299
  end
292
-
300
+
293
301
  rq = MethMap[api[:httpMethod]].new(path)
294
302
  rq.basic_auth(@auth[:user], @auth[:password])
295
303
  rq['user-agent'] = @agentString
@@ -333,7 +341,19 @@ class NumerousClientInternals
333
341
  @arbitraryMaximumTries.times do |attempt|
334
342
 
335
343
  @statistics[:serverRequests] += 1
344
+ t0 = Time.now
336
345
  resp = @http.request(rq)
346
+ et = Time.now - t0
347
+ # We report the elapsed round-trip time, either as a scalar (default)
348
+ # OR if you preset the :serverResponseTimes to be an array of length N
349
+ # then we keep the last N response times, thusly:
350
+ begin
351
+ times = @statistics[:serverResponseTimes]
352
+ times.insert(0, et)
353
+ times.pop()
354
+ rescue NoMethodError # just a scalar
355
+ @statistics[:serverResponseTimes] = et
356
+ end
337
357
 
338
358
  if @debugLevel > 0
339
359
  puts "Response headers:\n"
@@ -377,7 +397,7 @@ class NumerousClientInternals
377
397
  # On some requests that return "nothing" the server
378
398
  # returns {} ... on others it literally returns nothing.
379
399
  if (not resp.body) or resp.body.length == 0
380
- rj = {}
400
+ rj = {}
381
401
  else
382
402
  # this isn't supposed to happen... server bug?
383
403
  raise e
@@ -460,13 +480,13 @@ class NumerousClientInternals
460
480
  filterInfo[:prev] = filterInfo[:current]
461
481
  filterInfo[:current] = {}
462
482
  end
463
-
483
+
464
484
  list = v[api[:list]]
465
485
  nextURL = v[api[:next]]
466
486
 
467
487
  # hand them out
468
488
  if list # can be nil for a variety of reasons
469
- list.each do |i|
489
+ list.each do |i|
470
490
 
471
491
  # A note about duplicate filtering
472
492
  #
@@ -563,6 +583,9 @@ class NumerousClientInternals
563
583
 
564
584
  if attempt > 0
565
585
  stats[:throttleMultipleAttempts] += 1
586
+ if attempt > stats[:throttleMaxAttempt]
587
+ stats[:throttleMaxAttempt] = attempt
588
+ end
566
589
  end
567
590
 
568
591
  backarray = [ 2, 5, 15, 30, 60 ]
@@ -586,12 +609,12 @@ class NumerousClientInternals
586
609
  # note that some errors don't communicate rateleft so we have to
587
610
  # check for that as well (will be -1 here if wasn't sent to us)
588
611
  #
589
- # at constructor time our "throttle data" (td) was set up as the
612
+ # at constructor time our "throttle data" (td) was set up with the
590
613
  # voluntary arbitrary limit
591
- if rateleft >= 0 and rateleft < td
614
+ if rateleft >= 0 and rateleft < td[:voluntary]
592
615
  stats[:throttleVoluntaryBackoff] += 1
593
616
  # arbitrary .. 1 second if more than half left, 3 seconds if less
594
- if (rateleft*2) > td
617
+ if (rateleft*2) > td[:voluntary]
595
618
  sleep(1)
596
619
  else
597
620
  sleep(3)
@@ -631,18 +654,18 @@ end
631
654
  # Hash of <string-key, value> pairs and returned from the appropriate methods.
632
655
  #
633
656
  # For some operations the server returns only a success/failure code.
634
- # In those cases there is no useful return value from the method; the
657
+ # In those cases there is no useful return value from the method; the
635
658
  # method succeeds or else raises an exception (containing the failure code).
636
659
  #
637
660
  # For the collection operations the server returns a JSON array of dictionary
638
661
  # representations, possibly "chunked" into multiple request/response operations.
639
- # The enumerator methods (e.g., "metrics") implement lazy-fetch and hide
662
+ # The enumerator methods (e.g., "metrics") implement lazy-fetch and hide
640
663
  # the details of the chunking from you. They simply yield each individual
641
664
  # item (string-key Hash) to your block.
642
665
  #
643
666
  # === Exception handling
644
667
  #
645
- # Almost every API that interacts with the server will potentially
668
+ # Almost every API that interacts with the server will potentially
646
669
  # raise a {NumerousError} (or subclass thereof). This is not noted specifically
647
670
  # in the doc for each method unless it might be considered a "surprise"
648
671
  # (e.g., ping always returns true else raises an exception). Rescue as needed.
@@ -651,15 +674,15 @@ end
651
674
  class Numerous < NumerousClientInternals
652
675
 
653
676
  # path info for the server-level APIs: create a metric, get user info, etc
654
- APIInfo = {
677
+ APIInfo = {
655
678
  # POST to this to create a metric
656
- create: {
679
+ create: {
657
680
  path: '/v1/metrics',
658
681
  POST: { successCodes: [ 201 ] }
659
682
  },
660
683
 
661
684
  # GET a users metric collection
662
- metricsCollection: {
685
+ metricsCollection: {
663
686
  path: '/v2/users/%{userId}/metrics',
664
687
  defaults: { userId: 'me' },
665
688
  GET: { next: 'nextURL', list: 'metrics' }
@@ -721,7 +744,7 @@ class Numerous < NumerousClientInternals
721
744
  #
722
745
  # All metrics for the given user (default is your own)
723
746
  #
724
- # @param [String] userId
747
+ # @param [String] userId
725
748
  # optional - numeric id (represented as a string) of user
726
749
  # @yield [m] metrics
727
750
  # @yieldparam m [Hash] String-key representation of one metric
@@ -735,7 +758,7 @@ class Numerous < NumerousClientInternals
735
758
  #
736
759
  # All subscriptions for the given user (default is your own)
737
760
  #
738
- # @param [String] userId
761
+ # @param [String] userId
739
762
  # optional - numeric id (represented as a string) of user
740
763
  # @yield [s] subscriptions
741
764
  # @yieldparam s [Hash] String-key representation of one subscription
@@ -744,7 +767,7 @@ class Numerous < NumerousClientInternals
744
767
  def subscriptions(userId:nil, &block)
745
768
  chunkedIterator(APIInfo[:subscriptions], { userId: userId }, block)
746
769
  return self
747
- end
770
+ end
748
771
 
749
772
 
750
773
  #
@@ -754,7 +777,7 @@ class Numerous < NumerousClientInternals
754
777
  #
755
778
  # @param [Fixnum] count
756
779
  # optional - number of metrics to return
757
- # @return [Array] Array of hashes (metric string dicts). Each element
780
+ # @return [Array] Array of hashes (metric string dicts). Each element
758
781
  # represents a particular popular metric.
759
782
  #
760
783
  def mostPopular(count:nil)
@@ -765,7 +788,7 @@ class Numerous < NumerousClientInternals
765
788
  #
766
789
  # Verify connectivity to the server
767
790
  #
768
- # @return [true] Always returns true connectivity if ok.
791
+ # @return [true] Always returns true connectivity if ok.
769
792
  # Raises an exception otherwise.
770
793
  # @raise [NumerousAuthError] Your credentials are no good.
771
794
  # @raise [NumerousError] Other server (or network) errors.
@@ -781,10 +804,10 @@ class Numerous < NumerousClientInternals
781
804
  # @param label [String] Required. Label for the metric.
782
805
  # @param value [Fixnum,Float] Optional (keyword arg). Initial value.
783
806
  # @param attrs [Hash] Optional (keyword arg). Initial attributes.
784
- # @return [NumerousMetric]
807
+ # @return [NumerousMetric]
785
808
  #
786
809
  # @example Create a metric with label 'bozo' and set value to 17
787
- # nr = Numerous.new('nmrs_3vblahblah')
810
+ # nr = Numerous.new('nmrs_3vblahblah')
788
811
  # m = nr.createMetric('bozo')
789
812
  # m.write(17)
790
813
  #
@@ -810,7 +833,7 @@ class Numerous < NumerousClientInternals
810
833
  #
811
834
  # Instantiate a metric object to access a metric from the server.
812
835
  # @return [NumerousMetric] metric object
813
- # @param id [String]
836
+ # @param id [String]
814
837
  # Required. Metric ID (something like '2319923751024'). NOTE: If id
815
838
  # is bogus this will still "work" but (of course) errors will be
816
839
  # raised when you do something with the metric.
@@ -827,9 +850,8 @@ class Numerous < NumerousClientInternals
827
850
  # instead of an ID, and can even process it as a regexp.
828
851
  #
829
852
  # @param [String] labelspec The name (label) or regexp
830
- # @param [String] matchType 'FIRST' or 'BEST' or 'ONE' or 'STRING' (not regexp)
831
- # @param [Numerous] nr
832
- # The {Numerous} object that will be used to access this metric.
853
+ # @param [String] matchType 'FIRST','BEST','ONE','STRING' or 'ID'
854
+ #
833
855
  def metricByLabel(labelspec, matchType:'FIRST')
834
856
  def raiseConflict(s1,s2)
835
857
  raise NumerousMetricConflictError.new("Multiple matches", 409, [s1, s2])
@@ -840,10 +862,22 @@ class Numerous < NumerousClientInternals
840
862
  if not matchType
841
863
  matchType = 'FIRST'
842
864
  end
843
- if not ['FIRST', 'BEST', 'ONE', 'STRING'].include?(matchType)
865
+ if not ['FIRST', 'BEST', 'ONE', 'STRING', 'ID'].include?(matchType)
844
866
  raise ArgumentError
845
867
  end
846
868
 
869
+ # Having 'ID' as an option simplifies some automated use cases
870
+ # (e.g., test vectors) because you can just pair ids and matchTypes
871
+ # and simply always call ByLabel even for native (nonlabel) IDs
872
+ # We add the semantics that the result is validated as an actual ID
873
+ if matchType == 'ID'
874
+ rv = metric(labelspec)
875
+ if not rv.validate()
876
+ rv = nil
877
+ end
878
+ return rv
879
+ end
880
+
847
881
  # if you specified STRING and sent a regexp... or vice versa
848
882
  if matchType == 'STRING' and labelspec.instance_of?(Regexp)
849
883
  labelspec = labelspec.source
@@ -962,7 +996,7 @@ class Numerous < NumerousClientInternals
962
996
  # replace() bcs there might be a trailing newline on naked creds
963
997
  # (usually happens with a file or stdin)
964
998
  j[credsAPIKey] = s.sub("\n",'')
965
- end
999
+ end
966
1000
 
967
1001
  return j[credsAPIKey]
968
1002
  end
@@ -985,12 +1019,12 @@ end
985
1019
  # will return just the naked number unless you ask it for the entire dictionary)
986
1020
  #
987
1021
  # For some operations the server returns only a success/failure code.
988
- # In those cases there is no useful return value from the method; the
1022
+ # In those cases there is no useful return value from the method; the
989
1023
  # method succeeds or else raises an exception (containing the failure code).
990
1024
  #
991
1025
  # For the collection operations the server returns a JSON array of dictionary
992
1026
  # representations, possibly "chunked" into multiple request/response operations.
993
- # The enumerator methods (e.g., "events") implement lazy-fetch and hide
1027
+ # The enumerator methods (e.g., "events") implement lazy-fetch and hide
994
1028
  # the details of the chunking from you. They simply yield each individual
995
1029
  # item (string-key Hash) to your block.
996
1030
  #
@@ -1004,7 +1038,7 @@ class NumerousMetric < NumerousClientInternals
1004
1038
  # Constructor for a NumerousMetric
1005
1039
  #
1006
1040
  # @param [String] id The metric ID string.
1007
- # @param [Numerous] nr
1041
+ # @param [Numerous] nr
1008
1042
  # The {Numerous} object that will be used to access this metric.
1009
1043
  #
1010
1044
  # "id" should normally be the naked metric id (as a string).
@@ -1027,29 +1061,54 @@ class NumerousMetric < NumerousClientInternals
1027
1061
  # the presence of a '/' indicates a non-naked metric ID. This
1028
1062
  # seems a reasonable assumption given that IDs have to go into URLs
1029
1063
  #
1064
+ # "id" can be a hash representing a metric or a subscription.
1065
+ # We will take (in order) key 'metricId' or key 'id' as the id.
1066
+ # This is convenient when using the metrics() or subscriptions() iterators.
1067
+ #
1068
+ # "id" can be an integer representing a metric ID. Not recommended
1069
+ # though it's handy sometimes in cut/paste interactive testing/use.
1070
+ #
1030
1071
 
1031
1072
  def initialize(id, nr)
1073
+ actualId = nil
1032
1074
  begin
1033
- if id.include? '/'
1034
- fields = id.split('/')
1035
- if fields[-2] == "m"
1036
- id = fields[-1].to_i(36)
1037
- else
1038
- id = fields[-1]
1039
- end
1075
+ fields = id.split('/')
1076
+ if fields.length() == 1
1077
+ actualId = fields[0]
1078
+ elsif fields[-2] == "m"
1079
+ actualId = fields[-1].to_i(36)
1080
+ else
1081
+ actualId = fields[-1]
1040
1082
  end
1041
1083
  rescue NoMethodError
1042
- id = id.to_s
1043
1084
  end
1044
1085
 
1045
- @id = id
1046
- @nr = nr
1086
+ if not actualId
1087
+ # it's not a string, see if it's a hash
1088
+ actualId = id['metricId'] || id['id']
1089
+ end
1090
+
1091
+ if not actualId
1092
+ # well, see if it looks like an int
1093
+ i = id.to_i # allow this to raise exception if id bogus type here
1094
+ if i == id
1095
+ actualId = i.to_s
1096
+ end
1097
+ end
1098
+
1099
+ if not actualId
1100
+ raise ArgumentError("invalid id")
1101
+ else
1102
+ @id = actualId.to_s # defensive in case bad fmt in hash
1103
+ @nr = nr
1104
+ @cachedHash = nil
1105
+ end
1047
1106
  end
1048
1107
  attr_reader :id
1049
1108
  attr_reader :nr
1050
1109
 
1051
1110
 
1052
- APIInfo = {
1111
+ APIInfo = {
1053
1112
  # read/update/delete a metric
1054
1113
  metric: {
1055
1114
  path: '/v1/metrics/%{metricId}' ,
@@ -1151,6 +1210,67 @@ class NumerousMetric < NumerousClientInternals
1151
1210
  end
1152
1211
  private :getAPI
1153
1212
 
1213
+ # for things, such as [ ], that need a cached copy of the metric's values
1214
+ def ensureCache()
1215
+ begin
1216
+ if not @cachedHash
1217
+ ignored = read() # just reading brings cache into being
1218
+ end
1219
+ rescue NumerousError => x
1220
+ raise x # definitely pass these along
1221
+ rescue => x # anything else turn into a NumerousError
1222
+ # this is usually going to be all sorts of random low-level
1223
+ # network problems (if the network fails at the exact wrong time)
1224
+ details = { exception: x }
1225
+ raise NumerousError.new("Could not obtain metric state", 0, details)
1226
+ end
1227
+ end
1228
+ private :ensureCache
1229
+
1230
+ # access cached copy of metric via [ ]
1231
+ def [](idx)
1232
+ ensureCache()
1233
+ return @cachedHash[idx]
1234
+ end
1235
+
1236
+ # enumerator metric.each { |k, v| ... }
1237
+ def each()
1238
+ ensureCache()
1239
+ @cachedHash.each { |k, v| yield(k, v) }
1240
+ end
1241
+
1242
+ # produce the keys of a metric as an array
1243
+ def keys()
1244
+ ensureCache()
1245
+ return @cachedHash.keys
1246
+ end
1247
+
1248
+ # string representation of a metric
1249
+ def to_s()
1250
+ # there's nothing important/magic about the object id displayed; however
1251
+ # to make it match the native to_s we (believe it or not) need to multiply
1252
+ # the object_id return value by 2. This is obviously implementation specific
1253
+ # (and makes no difference to anyone; but this way it "looks right" to humans)
1254
+ objid = (2*self.object_id).to_s(16) # XXX wow lol
1255
+ rslt = "<NumerousMetric @ 0x#{objid}: "
1256
+ begin
1257
+ ensureCache()
1258
+ lbl = self['label']
1259
+ val = self['value']
1260
+ rslt += "'#{self['label']}' [#@id] = #{val}>"
1261
+ rescue NumerousError => x
1262
+ puts(x.code)
1263
+ if x.code == 400
1264
+ rslt += "**INVALID-ID** '#@id'>"
1265
+ elsif x.code == 404
1266
+ rslt += "**ID NOT FOUND** '#@id'>"
1267
+ else
1268
+ rslt += "**SERVER-ERROR** '#{x.message}'>"
1269
+ end
1270
+ end
1271
+ return rslt
1272
+ end
1273
+
1154
1274
  #
1155
1275
  # Read the current value of a metric
1156
1276
  # @param [Boolean] dictionary
@@ -1162,13 +1282,14 @@ class NumerousMetric < NumerousClientInternals
1162
1282
  def read(dictionary: false)
1163
1283
  api = getAPI(:metric, :GET)
1164
1284
  v = @nr.simpleAPI(api)
1285
+ @cachedHash = v.clone
1165
1286
  return (if dictionary then v else v['value'] end)
1166
1287
  end
1167
1288
 
1168
1289
  # "Validate" a metric object.
1169
1290
  # There really is no way to do this in any way that carries much weight.
1170
1291
  # However, if a user gives you a metricId and you'd like to know if
1171
- # that actually IS a metricId, this might be useful.
1292
+ # that actually IS a metricId, this might be useful.
1172
1293
  #
1173
1294
  # @example
1174
1295
  # someId = ... get a metric ID from someone ...
@@ -1180,7 +1301,7 @@ class NumerousMetric < NumerousClientInternals
1180
1301
  # Realize that even a valid metric can be deleted asynchronously
1181
1302
  # and thus become invalid after being validated by this method.
1182
1303
  #
1183
- # Reads the metric, catches the specific exceptions that occur for
1304
+ # Reads the metric, catches the specific exceptions that occur for
1184
1305
  # invalid metric IDs, and returns True/False. Other exceptions mean
1185
1306
  # something else went awry (server down, bad authentication, etc).
1186
1307
  # @return [Boolean] validity of the metric
@@ -1189,7 +1310,7 @@ class NumerousMetric < NumerousClientInternals
1189
1310
  ignored = read()
1190
1311
  return true
1191
1312
  rescue NumerousError => e
1192
- # bad request (400) is a completely bogus metric ID whereas
1313
+ # bad request (400) is a completely bogus metric ID whereas
1193
1314
  # not found (404) is a well-formed ID that simply does not exist
1194
1315
  if e.code == 400 or e.code == 404
1195
1316
  return false
@@ -1230,7 +1351,7 @@ class NumerousMetric < NumerousClientInternals
1230
1351
  return self
1231
1352
  end
1232
1353
 
1233
- # Enumerate the interactions (like/comment/error) of a metric.
1354
+ # Enumerate the interactions (like/comment/error) of a metric.
1234
1355
  #
1235
1356
  # @yield [i] interactions
1236
1357
  # @yieldparam i [Hash] String-key representation of one interaction.
@@ -1276,7 +1397,7 @@ class NumerousMetric < NumerousClientInternals
1276
1397
  # Obtain your subscription parameters on a given metric
1277
1398
  #
1278
1399
  # Note that normal users cannot see other user's subscriptions.
1279
- # Thus the "userId" parameter is somewhat pointless; you can only
1400
+ # Thus the "userId" parameter is somewhat pointless; you can only
1280
1401
  # ever see your own.
1281
1402
  # @param [String] userId
1282
1403
  # @return [Hash] your subscription attributes
@@ -1285,7 +1406,7 @@ class NumerousMetric < NumerousClientInternals
1285
1406
  return @nr.simpleAPI(api)
1286
1407
  end
1287
1408
 
1288
- # Subscribe to a metric.
1409
+ # Subscribe to a metric.
1289
1410
  #
1290
1411
  # See the NumerousApp API docs for what should be
1291
1412
  # in the dict. This function will fetch the current parameters
@@ -1309,7 +1430,7 @@ class NumerousMetric < NumerousClientInternals
1309
1430
  end
1310
1431
 
1311
1432
  dict.each { |k, v| params[k] = v }
1312
-
1433
+ @cachedHash = nil # bcs the subscriptions count changes
1313
1434
  api = getAPI(:subscription, :PUT, { userId: userId })
1314
1435
  return @nr.simpleAPI(api, jdict:params)
1315
1436
  end
@@ -1318,7 +1439,7 @@ class NumerousMetric < NumerousClientInternals
1318
1439
  #
1319
1440
  # @param [Fixnum|Float] newval Required. Value to be written.
1320
1441
  #
1321
- # @param [Boolean] onlyIf
1442
+ # @param [Boolean] onlyIf
1322
1443
  # Optional (keyword arg). Only creates an event at the server
1323
1444
  # if the newval is different from the current value. Raises
1324
1445
  # NumerousMetricConflictError if there is no change in value.
@@ -1345,6 +1466,7 @@ class NumerousMetric < NumerousClientInternals
1345
1466
  j['action'] = 'ADD'
1346
1467
  end
1347
1468
 
1469
+ @cachedHash = nil # will need to refresh cache on next access
1348
1470
  api = getAPI(:events, :POST)
1349
1471
  begin
1350
1472
  v = @nr.simpleAPI(api, jdict:j)
@@ -1373,7 +1495,7 @@ class NumerousMetric < NumerousClientInternals
1373
1495
  # with your updates before writing them back. If true your supplied
1374
1496
  # dictionary will become the entirety of the metric's parameters, and
1375
1497
  # any parameters you did not include in your dictionary will revert to
1376
- # their default values.
1498
+ # their default values.
1377
1499
  # @return [Hash] string-key Hash of the new metric parameters.
1378
1500
  #
1379
1501
  def update(dict, overwriteAll:false)
@@ -1381,7 +1503,8 @@ class NumerousMetric < NumerousClientInternals
1381
1503
  dict.each { |k, v| newParams[k] = v }
1382
1504
 
1383
1505
  api = getAPI(:metric, :PUT)
1384
- return @nr.simpleAPI(api, jdict:newParams)
1506
+ @cachedHash = @nr.simpleAPI(api, jdict:newParams)
1507
+ return @cachedHash.clone
1385
1508
  end
1386
1509
 
1387
1510
  # common code for writing interactions
@@ -1439,7 +1562,8 @@ class NumerousMetric < NumerousClientInternals
1439
1562
  def photo(imageDataOrReadable, mimeType:'image/jpeg')
1440
1563
  api = getAPI(:photo, :POST)
1441
1564
  mpart = { :f => imageDataOrReadable, :mimeType => mimeType }
1442
- return @nr.simpleAPI(api, multipart: mpart)
1565
+ @cachedHash = @nr.simpleAPI(api, multipart: mpart)
1566
+ return @cachedHash.clone()
1443
1567
  end
1444
1568
 
1445
1569
  # Delete the metric's photo
@@ -1447,6 +1571,7 @@ class NumerousMetric < NumerousClientInternals
1447
1571
  # but the error code will be 200/OK.
1448
1572
  # @return [nil]
1449
1573
  def photoDelete
1574
+ @cachedHash = nil # I suppose we could have just deleted the photoURL
1450
1575
  api = getAPI(:photo, :DELETE)
1451
1576
  v = @nr.simpleAPI(api)
1452
1577
  return nil
@@ -1476,9 +1601,9 @@ class NumerousMetric < NumerousClientInternals
1476
1601
 
1477
1602
  # Obtain the underlying photoURL for a metric.
1478
1603
  #
1479
- # The photoURL is available in the metrics parameters so you could
1604
+ # The photoURL is available in the metrics parameters so you could
1480
1605
  # just read(dictionary:true) and obtain it that way. However this goes
1481
- # one step further ... the URL in the metric itself still requires
1606
+ # one step further ... the URL in the metric itself still requires
1482
1607
  # authentication to fetch (it then redirects to the "real" underlying
1483
1608
  # static photo URL). This function goes one level deeper and
1484
1609
  # returns you an actual, publicly-fetchable, photo URL.
@@ -1525,6 +1650,7 @@ class NumerousMetric < NumerousClientInternals
1525
1650
  #
1526
1651
  # @return [nil]
1527
1652
  def crushKillDestroy
1653
+ @cachedHash = nil
1528
1654
  api = getAPI(:metric, :DELETE)
1529
1655
  v = @nr.simpleAPI(api)
1530
1656
  return nil
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: 1.0.1
4
+ version: 1.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Neil Webber
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-01-25 00:00:00.000000000 Z
11
+ date: 2015-02-15 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