Class: Numerous

Inherits:
NumerousClientInternals show all
Defined in:
lib/numerousapp.rb

Overview

Numerous

Primary class for accessing the numerousapp server.

Constructor

You must supply an API key:
   nr = Numerous.new('nmrs_3xblahblah')

You can optionally override the built-in server name
   nr = Numerous.new('nmrs_3xblahblah', server:'test.server.com')

Server return values

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.

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., “metrics”) implement lazy-fetch and hide the details of the chunking from you. They simply yield each individual item (string-key Hash) to your block.

Exception handling

Almost every API that interacts with the server will potentially raise a NumerousError (or subclass thereof). This is not noted specifically in the doc for each method unless it might be considered a “surprise” (e.g., ping always returns true else raises an exception). Rescue as needed.

Instance Attribute Summary

Attributes inherited from NumerousClientInternals

#agentString, #debugLevel, #serverName, #statistics

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from NumerousClientInternals

#debug, #initialize, #setBogusDupFilter, #to_s

Constructor Details

This class inherits a constructor from NumerousClientInternals

Class Method Details

.numerousKey(s: nil, credsAPIKey: 'NumerousAPIKey') ⇒ String

find an apikey from various default places

Parameters:

  • s (String) (defaults to: nil)

    See documentation for details; a file name or a key or a “readable” object.

  • credsAPIKey (String) (defaults to: 'NumerousAPIKey')

    Key to use in accessing json dict if one is found.

Returns:

  • (String)

    the API key.



1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
# File 'lib/numerousapp.rb', line 1061

def self.numerousKey(s:nil, credsAPIKey:'NumerousAPIKey')

  if not s
      # try to get from environment
      s = ENV['NUMEROUSAPIKEY']
      if not s
          return nil
        end
    end

  closeThis = nil

  if s == "@-"             # creds coming from stdin
      s = STDIN

  # see if they are in a file
  else
      begin
          if s.length() > 0         # is it a string or a file object?
              # it's stringy - if it looks like a file open it or fail
              begin
           if s.length() > 1 and s[0] == '@'
               s = open(s[1..-1])
               closeThis = s
           elsif s[0] == '/' or s[0] == '.'
               s = open(s)
               closeThis = s
                    end
              rescue
           return nil
                end
            end
      rescue NoMethodError     # it wasn't stringy, presumably it's a "readable"
      end
    end

  # well, see if whatever it is, is readable, and go with that if it is
  begin
      v = s.read()
      if closeThis
          closeThis.close()
        end
      s = v
  rescue NoMethodError
  end

  # at this point s is either a JSON or a naked cred (or bogus)
  begin
      j = JSON.parse(s)
  rescue TypeError, JSON::ParserError
      j = {}
    end


  #
  # This is kind of a hack and might hide some errors on your part
  #
  if not j.include? credsAPIKey  # this is how the naked case happens
      # replace() bcs there might be a trailing newline on naked creds
      # (usually happens with a file or stdin)
      j[credsAPIKey] = s.sub("\n",'')
    end

  return j[credsAPIKey]
end

Instance Method Details

#createMetric(label, value: nil, attrs: {}) ⇒ NumerousMetric

Create a brand new metric on the server.

Examples:

Create a metric with label ‘bozo’ and set value to 17

nr = Numerous.new('nmrs_3vblahblah')
m = nr.createMetric('bozo')
m.write(17)

Same example using the value keyword argument.

m = nr.createMetric('bozo', value:17)

Same example but also setting the description attribute

m = nr.createMetric('bozo', value:17, attrs:{"description" => "a clown"})

Parameters:

  • label (String)

    Required. Label for the metric.

  • value (Fixnum, Float) (defaults to: nil)

    Optional (keyword arg). Initial value.

  • attrs (Hash) (defaults to: {})

    Optional (keyword arg). Initial attributes.

Returns:



935
936
937
938
939
940
941
942
943
944
945
946
# File 'lib/numerousapp.rb', line 935

def createMetric(label, value:nil, attrs:{})

    api = makeAPIcontext(APIInfo[:create], :POST)

    j = attrs.clone
    j['label'] = label
    if value
        j['value'] = value
    end
    v = simpleAPI(api, jdict:j)
    return metric(v['id'])
end

#metric(id) ⇒ NumerousMetric

Instantiate a metric object to access a metric from the server.

Parameters:

  • id (String)

    Required. Metric ID (something like ‘2319923751024’). NOTE: If id is bogus this will still “work” but (of course) errors will be raised when you do something with the metric.

Returns:

See Also:



958
959
960
# File 'lib/numerousapp.rb', line 958

def metric(id)
    return NumerousMetric.new(id, self)
end

#metricByLabel(labelspec, matchType: 'FIRST') ⇒ Object

Version of metric() that accepts a name (label) instead of an ID, and can even process it as a regexp.

Parameters:

  • labelspec (String)

    The name (label) or regexp

  • matchType (String) (defaults to: 'FIRST')

    ‘FIRST’,‘BEST’,‘ONE’,‘STRING’ or ‘ID’



975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
# File 'lib/numerousapp.rb', line 975

def metricByLabel(labelspec, matchType:'FIRST')

    bestMatch = [ nil, 0 ]

    if not matchType
        matchType = 'FIRST'
    end
    if not ['FIRST', 'BEST', 'ONE', 'STRING', 'ID'].include?(matchType)
        raise ArgumentError
    end

    # Having 'ID' as an option simplifies some automated use cases
    # (e.g., test vectors) because you can just pair ids and matchTypes
    # and simply always call ByLabel even for native (nonlabel) IDs
    # We add the semantics that the result is validated as an actual ID
    if matchType == 'ID'
        rv = metric(labelspec)
        if not rv.validate()
            rv = nil
        end
        return rv
    end

    # if you specified STRING and sent a regexp... or vice versa
    if matchType == 'STRING' and labelspec.instance_of?(Regexp)
        labelspec = labelspec.source
    elsif matchType != 'STRING' and not labelspec.instance_of?(Regexp)
        labelspec = /#{labelspec}/
    end

    self.metrics do |m|
        if matchType == 'STRING'        # exact full match required
            if m['label'] == labelspec
                if bestMatch[0]
                    RaiseConflict.call(bestMatch[0]['label'], m['label'])
                end
                bestMatch = [ m, 1 ]   # the length is irrelevant with STRING
            end
        else
            mm = labelspec.match(m['label'])
            if mm
                if matchType == 'FIRST'
                    return self.metric(m['id'])
                elsif (matchType == 'ONE') and (bestMatch[1] > 0)
                    RaiseConflict.call(bestMatch[0]['label'], m['label'])
                end
                if mm[0].length > bestMatch[1]
                    bestMatch = [ m, mm[0].length ]
                end
            end
        end
    end
    rv = nil
    if bestMatch[0]
        rv = self.metric(bestMatch[0]['id'])
    end
end

#metrics(userId: nil) {|m| ... } ⇒ Object

All metrics for the given user (default is your own)

Parameters:

  • userId (String) (defaults to: nil)

    optional - numeric id (represented as a string) of user

Yields:

  • (m)

    metrics

Yield Parameters:

  • m (Hash)

    String-key representation of one metric

Returns:

  • self



868
869
870
871
# File 'lib/numerousapp.rb', line 868

def metrics(userId:nil, &block)
    chunkedIterator(APIInfo[:metricsCollection], { userId: userId }, block)
    return self
end

#mostPopular(count: nil) ⇒ Array

Note:

this returns the array; it is not an Enumerator

Obtain array of the “most popular” metrics.

Parameters:

  • count (Fixnum) (defaults to: nil)

    optional - number of metrics to return

Returns:

  • (Array)

    Array of hashes (metric string dicts). Each element represents a particular popular metric.



898
899
900
901
# File 'lib/numerousapp.rb', line 898

def mostPopular(count:nil)
    api = makeAPIcontext(APIInfo[:popular], :GET, {count: count})
    return simpleAPI(api)
end

#pingtrue

Verify connectivity to the server

Returns:

  • (true)

    Always returns true connectivity if ok. Raises an exception otherwise.

Raises:



911
912
913
914
# File 'lib/numerousapp.rb', line 911

def ping
    ignored = user()
    return true      # errors raise exceptions
end

#subscriptions(userId: nil) {|s| ... } ⇒ Object

All subscriptions for the given user (default is your own)

Parameters:

  • userId (String) (defaults to: nil)

    optional - numeric id (represented as a string) of user

Yields:

  • (s)

    subscriptions

Yield Parameters:

  • s (Hash)

    String-key representation of one subscription

Returns:

  • self



882
883
884
885
# File 'lib/numerousapp.rb', line 882

def subscriptions(userId:nil, &block)
    chunkedIterator(APIInfo[:subscriptions], { userId: userId }, block)
    return self
end

#user(userId: nil) ⇒ Hash

Obtain user attributes

Parameters:

  • userId (String) (defaults to: nil)

    optional - numeric id (represented as a string) of user

Returns:

  • (Hash)

    user representation (string-key hash)



836
837
838
839
# File 'lib/numerousapp.rb', line 836

def user(userId:nil)
    api = makeAPIcontext(APIInfo[:user], :GET, {userId: userId})
    return simpleAPI(api)
end

#userPhoto(imageDataOrReadable, mimeType: 'image/jpeg') ⇒ Hash

Note:

the server enforces an undocumented maximum data size. Exceeding the limit will raise a NumerousError (HTTP 413 / Too Large)

Set the user’s photo

Parameters:

  • imageDataOrReadable (String, #read)

    Either a binary-data string of the image data or an object with a “read” method. The entire data stream will be read.

  • mimeType (String) (defaults to: 'image/jpeg')

    Optional(keyword arg). Mime type.

Returns:

  • (Hash)

    updated user representation (string-key hash)



853
854
855
856
857
# File 'lib/numerousapp.rb', line 853

def userPhoto(imageDataOrReadable, mimeType:'image/jpeg')
    api = makeAPIcontext(APIInfo[:user], :photo)
    mpart = { :f => imageDataOrReadable, :mimeType => mimeType }
    return simpleAPI(api, multipart: mpart)
end