Class: NumerousMetric
- Inherits:
-
NumerousClientInternals
- Object
- NumerousClientInternals
- NumerousMetric
- Defined in:
- lib/numerousapp.rb
Overview
NumerousMetric
Class for individual Numerous metrics
You instantiate these hanging off of a particular Numerous connection:
nr = Numerous.new('nmrs_3xblahblah')
m = nr.metric('754623094815712984')
For most operations the NumerousApp server returns a JSON representation of the current or modified object state. This is converted to a ruby Hash of <string-key, value> pairs and returned from the appropriate methods. A few of the methods return only one item from the Hash (e.g., read will return just the naked number unless you ask it for the entire dictionary)
For some operations the server returns only a success/failure code. In those cases there is no useful return value from the method; the method succeeds or else raises an exception (containing the failure code).
For the collection operations the server returns a JSON array of dictionary representations, possibly “chunked” into multiple request/response operations. The enumerator methods (e.g., “events”) implement lazy-fetch and hide the details of the chunking from you. They simply yield each individual item (string-key Hash) to your block.
Instance Attribute Summary collapse
-
#id ⇒ String
readonly
The metric ID string.
-
#nr ⇒ Object
readonly
Returns the value of attribute nr.
Attributes inherited from NumerousClientInternals
#agentString, #debugLevel, #serverName, #statistics
Instance Method Summary collapse
-
#[](idx) ⇒ Object
access cached copy of metric via [ ].
-
#appURL ⇒ String
the phone application generates a nmrs:// URL as a way to link to the application view of a metric (vs a web view).
-
#comment(ctext) ⇒ String
Comment on a metric.
-
#crushKillDestroy ⇒ nil
Delete a metric (permanently).
-
#each ⇒ Object
enumerator metric.each { |k, v| … }.
-
#event(eId) ⇒ Hash
Obtain a specific metric event from the server.
-
#eventDelete(evID) ⇒ nil
Delete an event (a value update).
-
#events {|e| ... } ⇒ NumerousMetric
Enumerate the events of a metric.
-
#initialize(id, nr = nil) ⇒ NumerousMetric
constructor
Constructor for a NumerousMetric.
-
#interaction(iId) ⇒ Hash
Obtain a specific metric interaction from the server.
-
#interactionDelete(interID) ⇒ nil
Delete an interaction (a like/comment/error).
-
#interactions {|i| ... } ⇒ NumerousMetric
Enumerate the interactions (like/comment/error) of a metric.
-
#keys ⇒ Object
produce the keys of a metric as an array.
-
#label ⇒ String
Get the label of a metric.
-
#like ⇒ String
“Like” a metric.
-
#photo(imageDataOrReadable, mimeType: 'image/jpeg') ⇒ Hash
set the background image for a metric.
-
#photoDelete ⇒ nil
Delete the metric’s photo.
-
#photoURL ⇒ String?
Obtain the underlying photoURL for a metric.
-
#read(dictionary: false) ⇒ Fixnum|Float, Hash
Read the current value of a metric.
-
#sendError(errText) ⇒ String
Write an error to a metric.
-
#stream {|s| ... } ⇒ NumerousMetric
Enumerate the stream of a metric.
-
#subscribe(dict, userId: nil, overwriteAll: false) ⇒ Object
Subscribe to a metric.
-
#subscription(userId = nil) ⇒ Hash
Obtain your subscription parameters on a given metric.
-
#subscriptions {|s| ... } ⇒ NumerousMetric
Enumerate the subscriptions of a metric.
-
#to_s ⇒ Object
string representation of a metric.
-
#update(dict, overwriteAll: false) ⇒ Hash
Update parameters of a metric (such as “description”, “label”, etc).
-
#validate ⇒ Boolean
“Validate” a metric object.
-
#webURL ⇒ String
Get the URL for the metric’s web representation.
-
#write(newval, onlyIf: false, add: false, dictionary: false, updated: nil) ⇒ Fixnum|Float, Hash
Write a value to a metric.
Methods inherited from NumerousClientInternals
Constructor Details
#initialize(id, nr = nil) ⇒ NumerousMetric
Constructor for a NumerousMetric
“id” should normally be the naked metric id (as a string).
It can also be a nmrs: URL, e.g.:
nmrs://metric/2733614827342384
Or a ‘self’ link from the API:
https://api.numerousapp.com/metrics/2733614827342384
in either case we get the ID in the obvious syntactic way.
It can also be a metric’s web link, e.g.:
http://n.numerousapp.com/m/1x8ba7fjg72d
in which case we “just know” that the tail is a base36 encoding of the ID.
The decoding logic here makes the specific assumption that the presence of a ‘/’ indicates a non-naked metric ID. This seems a reasonable assumption given that IDs have to go into URLs
“id” can be a hash representing a metric or a subscription. We will take (in order) key ‘metricId’ or key ‘id’ as the id. This is convenient when using the metrics() or subscriptions() iterators.
“id” can be an integer representing a metric ID. Not recommended though it’s handy sometimes in cut/paste interactive testing/use.
1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 |
# File 'lib/numerousapp.rb', line 1111 def initialize(id, nr=nil) # If you don't specify a Numerous we'll make one for you. # For this to work, NUMEROUSAPIKEY environment variable must exist. # m = NumerousMetric.new('234234234') is ok for simple one-shots # but note it makes a private Numerous for every metric. nr ||= Numerous.new(nil) actualId = nil begin fields = id.split('/') if fields.length() == 1 actualId = fields[0] elsif fields[-2] == "m" actualId = fields[-1].to_i(36) else actualId = fields[-1] end rescue NoMethodError end if not actualId # it's not a string, see if it's a hash actualId = id['metricId'] || id['id'] end if not actualId # well, see if it looks like an int i = id.to_i # allow this to raise exception if id bogus type here if i == id actualId = i.to_s end end if not actualId raise ArgumentError("invalid id") else @id = actualId.to_s # defensive in case bad fmt in hash @nr = nr @cachedHash = nil end end |
Instance Attribute Details
#id ⇒ String (readonly)
Returns The metric ID string.
|
# File 'lib/numerousapp.rb', line 1072
|
#nr ⇒ Object (readonly)
Returns the value of attribute nr.
1155 1156 1157 |
# File 'lib/numerousapp.rb', line 1155 def nr @nr end |
Instance Method Details
#[](idx) ⇒ Object
access cached copy of metric via [ ]
1280 1281 1282 1283 |
# File 'lib/numerousapp.rb', line 1280 def [](idx) ensureCache() return @cachedHash[idx] end |
#appURL ⇒ String
the phone application generates a nmrs:// URL as a way to link to the application view of a metric (vs a web view). This makes one of those for you so you don’t have to “know” the format of it.
1716 1717 1718 |
# File 'lib/numerousapp.rb', line 1716 def appURL return "nmrs://metric/" + @id end |
#comment(ctext) ⇒ String
Comment on a metric
1610 1611 1612 1613 |
# File 'lib/numerousapp.rb', line 1610 def comment(ctext) j = { 'kind' => 'comment' , 'commentBody' => ctext } return writeInteraction(j) end |
#crushKillDestroy ⇒ nil
Delete a metric (permanently). Be 100% you want this, because there is absolutely no undo.
1724 1725 1726 1727 1728 1729 |
# File 'lib/numerousapp.rb', line 1724 def crushKillDestroy @cachedHash = nil api = getAPI(:metric, :DELETE) v = @nr.simpleAPI(api) return nil end |
#each ⇒ Object
enumerator metric.each { |k, v| … }
1286 1287 1288 1289 |
# File 'lib/numerousapp.rb', line 1286 def each() ensureCache() @cachedHash.each { |k, v| yield(k, v) } end |
#event(eId) ⇒ Hash
Obtain a specific metric event from the server
1430 1431 1432 1433 |
# File 'lib/numerousapp.rb', line 1430 def event(eId) api = getAPI(:event, :GET, {eventID:eId}) return @nr.simpleAPI(api) end |
#eventDelete(evID) ⇒ nil
Deleting an event that isn’t there will raise a NumerousError but the error code will be 200/OK.
Delete an event (a value update)
1648 1649 1650 1651 1652 |
# File 'lib/numerousapp.rb', line 1648 def eventDelete(evID) api = getAPI(:event, :DELETE, {eventID:evID}) v = @nr.simpleAPI(api) return nil end |
#events {|e| ... } ⇒ NumerousMetric
Enumerate the events of a metric. Events are value updates.
1387 1388 1389 1390 |
# File 'lib/numerousapp.rb', line 1387 def events(&block) @nr.chunkedIterator(APIInfo[:events], {metricId:@id}, block) return self end |
#interaction(iId) ⇒ Hash
Obtain a specific metric interaction from the server
1441 1442 1443 1444 |
# File 'lib/numerousapp.rb', line 1441 def interaction(iId) api = getAPI(:interaction, :GET, {item:iId}) return @nr.simpleAPI(api) end |
#interactionDelete(interID) ⇒ nil
Deleting an interaction that isn’t there will raise a NumerousError but the error code will be 200/OK.
Delete an interaction (a like/comment/error)
1659 1660 1661 1662 1663 |
# File 'lib/numerousapp.rb', line 1659 def interactionDelete(interID) api = getAPI(:interaction, :DELETE, {item:interID}) v = @nr.simpleAPI(api) return nil end |
#interactions {|i| ... } ⇒ NumerousMetric
Enumerate the interactions (like/comment/error) of a metric.
1408 1409 1410 1411 |
# File 'lib/numerousapp.rb', line 1408 def interactions(&block) @nr.chunkedIterator(APIInfo[:interactions], {metricId:@id}, block) return self end |
#keys ⇒ Object
produce the keys of a metric as an array
1292 1293 1294 1295 |
# File 'lib/numerousapp.rb', line 1292 def keys() ensureCache() return @cachedHash.keys end |
#label ⇒ String
Get the label of a metric.
1698 1699 1700 1701 |
# File 'lib/numerousapp.rb', line 1698 def label v = read(dictionary:true) return v['label'] end |
#like ⇒ String
“Like” a metric
1586 1587 1588 1589 |
# File 'lib/numerousapp.rb', line 1586 def like # a like is written as an interaction return writeInteraction({ 'kind' => 'like' }) end |
#photo(imageDataOrReadable, mimeType: 'image/jpeg') ⇒ Hash
the server enforces an undocumented maximum data size. Exceeding the limit will raise a NumerousError (HTTP 413 / Too Large)
set the background image for a metric
1625 1626 1627 1628 1629 1630 |
# File 'lib/numerousapp.rb', line 1625 def photo(imageDataOrReadable, mimeType:'image/jpeg') api = getAPI(:photo, :POST) mpart = { :f => imageDataOrReadable, :mimeType => mimeType } @cachedHash = @nr.simpleAPI(api, multipart: mpart) return @cachedHash.clone() end |
#photoDelete ⇒ nil
Deleting a photo that isn’t there will raise a NumerousError but the error code will be 200/OK.
Delete the metric’s photo
1636 1637 1638 1639 1640 1641 |
# File 'lib/numerousapp.rb', line 1636 def photoDelete @cachedHash = nil # I suppose we could have just deleted the photoURL api = getAPI(:photo, :DELETE) v = @nr.simpleAPI(api) return nil end |
#photoURL ⇒ String?
Fetches (and discards) the entire underlying photo, because that was the easiest way to find the target URL using net/http
Obtain the underlying photoURL for a metric.
The photoURL is available in the metrics parameters so you could just read(dictionary:true) and obtain it that way. However this goes one step further … the URL in the metric itself still requires authentication to fetch (it then redirects to the “real” underlying static photo URL). This function goes one level deeper and returns you an actual, publicly-fetchable, photo URL.
1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 |
# File 'lib/numerousapp.rb', line 1679 def photoURL v = read(dictionary:true) begin phurl = v.fetch('photoURL') return @nr.getRedirect(phurl) rescue KeyError return nil end # never reached return nil end |
#read(dictionary: false) ⇒ Fixnum|Float, Hash
Read the current value of a metric
1331 1332 1333 1334 1335 1336 |
# File 'lib/numerousapp.rb', line 1331 def read(dictionary: false) api = getAPI(:metric, :GET) v = @nr.simpleAPI(api) @cachedHash = v.clone return (if dictionary then v else v['value'] end) end |
#sendError(errText) ⇒ String
Write an error to a metric
1597 1598 1599 1600 1601 1602 |
# File 'lib/numerousapp.rb', line 1597 def sendError(errText) # an error is written as an interaction thusly: # (commentBody is used for the error text) j = { 'kind' => 'error' , 'commentBody' => errText } return writeInteraction(j) end |
#stream {|s| ... } ⇒ NumerousMetric
Enumerate the stream of a metric. The stream is events and interactions merged together into a time-ordered stream.
1398 1399 1400 1401 |
# File 'lib/numerousapp.rb', line 1398 def stream(&block) @nr.chunkedIterator(APIInfo[:stream], {metricId:@id}, block) return self end |
#subscribe(dict, userId: nil, overwriteAll: false) ⇒ Object
Subscribe to a metric.
See the NumerousApp API docs for what should be in the dict. This function will fetch the current parameters and update them with the ones you supply (because the server does not like you supplying an incomplete dictionary here). You can prevent the fetch/merge via overwriteAll:true
Normal users cannot set other user’s subscriptions.
1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 |
# File 'lib/numerousapp.rb', line 1474 def subscribe(dict, userId:nil, overwriteAll:false) if overwriteAll params = {} else params = subscription(userId) end dict.each { |k, v| params[k] = v } @cachedHash = nil # bcs the subscriptions count changes api = getAPI(:subscription, :PUT, { userId: userId }) return @nr.simpleAPI(api, jdict:params) end |
#subscription(userId = nil) ⇒ Hash
Obtain your subscription parameters on a given metric
Note that normal users cannot see other user’s subscriptions. Thus the “userId” parameter is somewhat pointless; you can only ever see your own.
1453 1454 1455 1456 |
# File 'lib/numerousapp.rb', line 1453 def subscription(userId=nil) api = getAPI(:subscription, :GET, {userId: userId}) return @nr.simpleAPI(api) end |
#subscriptions {|s| ... } ⇒ NumerousMetric
Enumerate the subscriptions of a metric.
1418 1419 1420 1421 |
# File 'lib/numerousapp.rb', line 1418 def subscriptions(&block) @nr.chunkedIterator(APIInfo[:subscriptions], {metricId:@id}, block) return self end |
#to_s ⇒ Object
string representation of a metric
1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 |
# File 'lib/numerousapp.rb', line 1298 def to_s() # there's nothing important/magic about the object id displayed; however # to make it match the native to_s we (believe it or not) need to multiply # the object_id return value by 2. This is obviously implementation specific # (and makes no difference to anyone; but this way it "looks right" to humans) objid = (2*self.object_id).to_s(16) # XXX wow lol rslt = "<NumerousMetric @ 0x#{objid}: " begin ensureCache() lbl = self['label'] val = self['value'] rslt += "'#{self['label']}' [#@id] = #{val}>" rescue NumerousError => x puts(x.code) if x.code == 400 rslt += "**INVALID-ID** '#@id'>" elsif x.code == 404 rslt += "**ID NOT FOUND** '#@id'>" else rslt += "**SERVER-ERROR** '#{x.}'>" end end return rslt end |
#update(dict, overwriteAll: false) ⇒ Hash
Update parameters of a metric (such as “description”, “label”, etc). Not to be used (won’t work) to update a metric’s value.
1564 1565 1566 1567 1568 1569 1570 1571 |
# File 'lib/numerousapp.rb', line 1564 def update(dict, overwriteAll:false) newParams = (if overwriteAll then {} else read(dictionary:true) end) dict.each { |k, v| newParams[k] = v } api = getAPI(:metric, :PUT) @cachedHash = @nr.simpleAPI(api, jdict:newParams) return @cachedHash.clone end |
#validate ⇒ Boolean
“Validate” a metric object. There really is no way to do this in any way that carries much weight. However, if a user gives you a metricId and you’d like to know if that actually IS a metricId, this might be useful.
Realize that even a valid metric can be deleted asynchronously and thus become invalid after being validated by this method.
Reads the metric, catches the specific exceptions that occur for invalid metric IDs, and returns True/False. Other exceptions mean something else went awry (server down, bad authentication, etc).
1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 |
# File 'lib/numerousapp.rb', line 1357 def validate begin ignored = read() return true rescue NumerousError => e # bad request (400) is a completely bogus metric ID whereas # not found (404) is a well-formed ID that simply does not exist if e.code == 400 or e.code == 404 return false else # anything else is a "real" error; figure out yourself raise e end end return false # this never happens end |
#webURL ⇒ String
Get the URL for the metric’s web representation
1706 1707 1708 1709 |
# File 'lib/numerousapp.rb', line 1706 def webURL v = read(dictionary:true) return v['links']['web'] end |
#write(newval, onlyIf: false, add: false, dictionary: false, updated: nil) ⇒ Fixnum|Float, Hash
Write a value to a metric.
1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 |
# File 'lib/numerousapp.rb', line 1520 def write(newval, onlyIf:false, add:false, dictionary:false, updated:nil) j = { 'value' => newval } if onlyIf j['onlyIfChanged'] = true end if add j['action'] = 'ADD' end if updated j['updated'] = updated end @cachedHash = nil # will need to refresh cache on next access api = getAPI(:events, :POST) begin v = @nr.simpleAPI(api, jdict:j) rescue NumerousError => e # if onlyIf was specified and the error is "conflict" # (meaning: no change), raise ConflictError specifically if onlyIf and e.code == 409 raise NumerousMetricConflictError.new("No Change", e.details) else raise e # never mind, plain NumerousError is fine end end return (if dictionary then v else v['value'] end) end |