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.



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
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
# File 'lib/numerousapp.rb', line 977

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:



851
852
853
854
855
856
857
858
859
860
861
862
# File 'lib/numerousapp.rb', line 851

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:



874
875
876
# File 'lib/numerousapp.rb', line 874

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’



891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
# File 'lib/numerousapp.rb', line 891

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



784
785
786
787
# File 'lib/numerousapp.rb', line 784

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.



814
815
816
817
# File 'lib/numerousapp.rb', line 814

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:



827
828
829
830
# File 'lib/numerousapp.rb', line 827

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



798
799
800
801
# File 'lib/numerousapp.rb', line 798

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)



752
753
754
755
# File 'lib/numerousapp.rb', line 752

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)



769
770
771
772
773
# File 'lib/numerousapp.rb', line 769

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