riq 1.0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2e861c8df701fcf3bc680f3061da48295ab332d2
4
- data.tar.gz: 27ab3509e83f59d86fd018b0a6f9f852812d8e75
3
+ metadata.gz: 6763599ea332a7fe04b54aab09334f95b74ddeec
4
+ data.tar.gz: 165b2617d0f2beb63947893e90c5389c9d62f63e
5
5
  SHA512:
6
- metadata.gz: 1f5c66f3151336e1e93d6e56170a7aaedabf1c1140fb8b888f0a421952d247c2e2863004d3f5766e33c72e4d06d84dbe743179e26447fe11e1edb91ac591fa08
7
- data.tar.gz: 240b0c53d2bae6c5ed73a169721cef3b51bf72e27ddf15e5e68ca48d1940e107a429cce1a959e111901e65baf8cb439c343caa9b810b09870ae544e65df30fd7
6
+ metadata.gz: 959c78bf06de627ce564b1fa1de491219dcd9abab31f523a04207bd8d6658f09da5a764894b00b0e4559b238c722a3c89f26b740bd7c57acc6d12035ce865fa3
7
+ data.tar.gz: 5722f9d6c18ec6329a7bccdb5a547aee635fb6bf4358f2f9a0e379b4323467dc6ca868faac8959ec5aed77efad3ccec1a7c84dc8d7a52f5cf66e51a05f051b03
@@ -0,0 +1,42 @@
1
+ # Created by https://www.gitignore.io
2
+
3
+ ### Ruby ###
4
+ *.gem
5
+ *.rbc
6
+ /.config
7
+ /coverage/
8
+ /InstalledFiles
9
+ /pkg/
10
+ /spec/reports/
11
+ /test/tmp/
12
+ /test/version_tmp/
13
+ /tmp/
14
+
15
+ ## Specific to RubyMotion:
16
+ .dat*
17
+ .repl_history
18
+ build/
19
+
20
+ ## Documentation cache and generated files:
21
+ /.yardoc/
22
+ /_yardoc/
23
+ /doc/
24
+ /rdoc/
25
+ Gemfile.lock
26
+
27
+ ## Environment normalisation:
28
+ /.bundle/
29
+ /vendor/bundle
30
+ /lib/bundler/man/
31
+
32
+ # for a library or gem, you might want to ignore these files since the code is
33
+ # intended to run in multiple environments; otherwise, check them in:
34
+ # Gemfile.lock
35
+ # .ruby-version
36
+ # .ruby-gemset
37
+
38
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
39
+ .rvmrc
40
+
41
+ # test file
42
+ t.rb
@@ -0,0 +1,2 @@
1
+ lib/**/*.rb
2
+ --title RelateIQ
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
3
+
4
+ group :development do
5
+ gem 'pry'
6
+ end
7
+
8
+ gem 'httparty', '0.13.3'
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 RelateIQ
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,33 @@
1
+ # RelateIQ Ruby SDK
2
+
3
+ A full featured API interface for interacting with the [RelateIQ](https://relateiq.com) API.
4
+
5
+ ## Overview
6
+
7
+ <!--[![Gem Version](https://badge.fury.io/rb/riq.svg)](http://badge.fury.io/rb/riq)-->
8
+ [![Gem Version](https://img.shields.io/gem/v/riq.svg)](http://badge.fury.io/rb/riq)
9
+ <!--[![MIT license](http://img.shields.io/badge/license-MIT-blue.svg)](http://opensource.org/licenses/MIT)-->
10
+
11
+ ## Code Examples
12
+
13
+ require 'riq'
14
+ RIQ.init(ENV['RIQ_API_KEY'], ENV['RIQ_API_SECRET'])
15
+
16
+ # Contacts
17
+ RIQ.contacts.each do |c|
18
+ # do something
19
+ puts c.name
20
+ end
21
+ # => Bruce Wayne
22
+ # => Malcolm Reynolds
23
+ # => Michael Bluth
24
+ # => Tony Stark
25
+ ...
26
+
27
+ # Etc
28
+
29
+
30
+ ## Helpful Links
31
+
32
+ * [Full ruby docs](http://www.rubydoc.info/gems/riq)
33
+ * [Examples and API docs](https://api.relateiq.com/#/ruby)
@@ -0,0 +1,103 @@
1
+ # The namespace from which all magic springs
2
+ module RIQ
3
+ # Could fetch defaults or something here
4
+ end
5
+
6
+ # Monkeypatches
7
+ # cry about it, nerd
8
+ module RIQExtensions
9
+ refine Symbol do
10
+ def to_cam
11
+ temp = self.to_s.split('_').map(&:capitalize).join
12
+ (temp[0].downcase + temp[1..-1]).to_sym
13
+ end
14
+
15
+ def to_snake
16
+ a = self.to_s.split('')
17
+ n = []
18
+ a.each do |l|
19
+ n << '_' if l.is_upper_char?
20
+ n << l.downcase
21
+ end
22
+ n = n[1..-1] if n.first == '_'
23
+ n.join.to_sym
24
+ end
25
+ end
26
+
27
+ refine String do
28
+ def is_upper_char?
29
+ !self[/[A-Z]/].nil? && self.length == 1
30
+ end
31
+
32
+ def is_lower_char?
33
+ !self[/[a-z]/].nil? && self.length == 1
34
+ end
35
+ end
36
+
37
+ refine Fixnum do
38
+ def cut_milis
39
+ self.to_s[0...-3].to_i
40
+ end
41
+
42
+ def to_sym
43
+ self.to_s.to_sym
44
+ end
45
+ end
46
+
47
+ refine Hash do
48
+ # Converts to RIQ API's [{raw: "VALUE"}] format
49
+ def to_raw
50
+ return {} if self.empty?
51
+ o = {}
52
+ self.each do |k, v|
53
+ o[k.to_cam] = [{raw: v}]
54
+ end
55
+ o
56
+ end
57
+
58
+ # Converts from RIQ API's [{raw: "VALUE"}] format
59
+ def from_raw
60
+ return {} if self.empty?
61
+ o = {}
62
+ self.each do |k,v|
63
+ if v.is_a?(Array) && v.first.include?(:raw)
64
+ o[k.to_sym.to_snake] = v.first[:raw]
65
+ else
66
+ o[k.to_sym.to_snake] = v
67
+ end
68
+ end
69
+ o
70
+ end
71
+
72
+ def to_cam
73
+ o = {}
74
+ self.each do |k,v|
75
+ o[k.to_cam] = v
76
+ end
77
+ o
78
+ end
79
+ end
80
+
81
+ refine Object do
82
+ def symbolize
83
+ return self unless self.respond_to? :keys
84
+ o = {}
85
+ self.each do |k, v|
86
+ if v.respond_to? :keys
87
+ o[k.to_sym.to_snake] = v.symbolize
88
+ else
89
+ if v.respond_to? :each
90
+ v.map! do |i|
91
+ i.symbolize
92
+ end
93
+ end
94
+ o[k.to_sym.to_snake] = v
95
+ end
96
+ end
97
+ o
98
+ end
99
+ end
100
+ end
101
+
102
+ # Base file from which everything else is included
103
+ Dir[__dir__ + '/riq/*.rb'].each {|file| require file }
@@ -0,0 +1,82 @@
1
+ require_relative 'riq_obj'
2
+ using RIQExtensions
3
+
4
+ module RIQ
5
+ # Accounts represent companies (or other entities).
6
+ class Account < RIQObject
7
+ attr_accessor :name
8
+ attr_accessor :field_values
9
+
10
+ # (see RIQObject#node)
11
+ def node
12
+ "accounts/#{@id}"
13
+ end
14
+
15
+ # (see #node)
16
+ def self.node(arg = nil)
17
+ "accounts"
18
+ end
19
+
20
+ # (see RIQObject#data)
21
+ def data
22
+ {
23
+ id: @id,
24
+ name: @name,
25
+ field_values: @field_values
26
+ }
27
+ end
28
+
29
+ # (see RIQObject#payload)
30
+ def payload
31
+ # TODO: find more elegant way to do this
32
+ pld = data
33
+ pld['fieldValues'] = @field_values.to_raw
34
+ pld.delete(:field_values)
35
+ pld.to_json
36
+ end
37
+
38
+ # @overload field_value(key)
39
+ # @param key [String, Integer]
40
+ # @return [Array] Value of key
41
+ # @overload field_value(key, value)
42
+ # Sets key to value
43
+ # @param key [String, Integer] Key to set
44
+ # @param value [#to_s] Sets key to value
45
+ def field_value(key, value = nil)
46
+ # TODO: double check that this works with arrays of stuff
47
+ # or, have a format function that casts ints to string on save
48
+ if value.nil?
49
+ @field_values.fetch(key.to_sym, nil)
50
+ else
51
+ @field_values[key.to_sym] = value.to_s
52
+ {key.to_sym => value.to_s}
53
+ end
54
+ end
55
+
56
+ private
57
+ def init(obj = nil)
58
+ unless obj.nil?
59
+ @id = obj[:id]
60
+ @name = obj[:name]
61
+ @field_values = obj[:field_values] ? obj[:field_values].from_raw : {}
62
+ @modified_date = obj[:modified_date].cut_milis if obj[:modified_date]
63
+ else
64
+ @id = nil
65
+ @name = nil
66
+ @field_values = {}
67
+ end
68
+ self
69
+ end
70
+
71
+ end
72
+
73
+ class << self
74
+ # Convenience method to create new Accounts
75
+ # @param id [String, nil] create a blank Account object or
76
+ # fetch an existing one by id.
77
+ # @return [Account]
78
+ def account(id = nil)
79
+ Account.new(id)
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,133 @@
1
+ using RIQExtensions
2
+
3
+ module RIQ
4
+ # Manages caching and fetching for a certain type of child object.
5
+ class BatchManager
6
+ # @return [Hash] current fetch options
7
+ attr_reader :fetch_options
8
+
9
+ # @param klass [RIQObject] The child class that's being fetched, such as {Account} or {List}
10
+ # @param opts [Hash] fetch options
11
+ def initialize(klass, opts = {})
12
+ @klass = klass
13
+ begin
14
+ raise unless @klass.ancestors.include? RIQ::RIQObject
15
+ rescue
16
+ raise RIQError, 'Must pass a RIQ Class'
17
+ end
18
+ @fetch_options = {}
19
+ reset_cache
20
+
21
+ # cause otherwise it's a variable
22
+ self.send(:fetch_options=, opts)
23
+ @client = RIQ.client
24
+ end
25
+
26
+ # Iterator for each item in the manager. Pass a block to it!
27
+ # @example
28
+ # RIQ.lists.each do |l|
29
+ # puts l
30
+ # end
31
+ def each(&blok)
32
+ reset_cache
33
+ loop do
34
+ x = next_item
35
+ if x
36
+ blok.call(x)
37
+ else
38
+ return
39
+ end
40
+ end
41
+ end
42
+
43
+ # Returns the first child object, mainly used for testing
44
+ # @return [RIQObject]
45
+ def first
46
+ reset_cache
47
+ fetch_page.first
48
+ end
49
+
50
+ # Set fetch options
51
+ # @param opts [Hash] Where values are either strings or arrays
52
+ def fetch_options=(opts = {})
53
+ valid_keys = [:_ids, :_start, :_limit, :contactIds, :accountIds, :modifiedDate]
54
+ reset_cache
55
+ options = {}
56
+ opts.each do |k, v|
57
+ # some args are comma-separated arrays, some are just args
58
+ warn("[WARN] key #{k} is not one of #{valid_keys} and won't do anything") unless valid_keys.include?(k.to_sym)
59
+ if v.is_a? Array
60
+ options[k.to_sym] = v.join(',')
61
+ else
62
+ options[k.to_sym] = v
63
+ end
64
+ end
65
+ # ruby says that nil is an argument and page_size was getting set to 0
66
+ options[:_limit] ||= 200
67
+ @fetch_options.merge! options#.to_cam
68
+ end
69
+
70
+ private
71
+ def reset_cache
72
+ # shouldn't reset options, that's silly
73
+ # @fetch_options = {}
74
+ @cache = []
75
+ @cache_index = 0
76
+ @page_index = 0
77
+ end
78
+
79
+ def next_item
80
+ # I believe this is resetting opts somehow/not respecting limit
81
+ if @cache_index == @cache.size
82
+ return nil if ![0, @fetch_options[:_limit]].include? @cache.size
83
+ # puts "\n=== fetching page! (#{@page_index}) (#{@fetch_options[:_limit]})"
84
+ @cache = fetch_page(@page_index)
85
+ @page_index += @cache.size
86
+ @cache_index = 0
87
+ end
88
+
89
+ if @cache_index < @cache.size
90
+ obj = @cache[@cache_index]
91
+ @cache_index += 1
92
+ obj
93
+ else
94
+ nil
95
+ end
96
+ end
97
+
98
+ def fetch_page(index = 0)
99
+ @fetch_options.merge!({_start: index})
100
+
101
+ # all class#node functions can take an arg, so far, only list items need it.
102
+ # having list_id usually be nil (that is, undefined) is fine. probably.
103
+ data = @client.get(@klass.node(@list_id), options: @fetch_options)
104
+
105
+ objects = []
106
+ data.fetch('objects', []).each do |obj|
107
+ objects << @klass.new(obj)
108
+ end
109
+ objects
110
+ end
111
+ end
112
+
113
+ class << self
114
+ # @!macro [new] conv
115
+ # Returns an iterator for all $0
116
+ # @return [BatchManager]
117
+
118
+ # @macro conv
119
+ def lists(opts = {})
120
+ BatchManager.new(RIQ::List, opts)
121
+ end
122
+
123
+ # @macro conv
124
+ def contacts(opts = {})
125
+ BatchManager.new(RIQ::Contact, opts)
126
+ end
127
+
128
+ # @macro conv
129
+ def accounts(opts = {})
130
+ BatchManager.new(RIQ::Account, opts)
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,130 @@
1
+ require 'httparty'
2
+ require 'json'
3
+ require_relative 'error'
4
+
5
+ module RIQ
6
+ # HTTP client responsible for actually handling the HTTP requests.
7
+ # @note Utility class to perform HTTP requests. This shouldn't be
8
+ # instantiated directly but used by children of RIQObject instead.
9
+ class Client
10
+ attr_accessor :cache
11
+
12
+ def initialize(key, secret)
13
+ raise 'Missing credentials' if key.nil? || secret.nil?
14
+
15
+ @root_url = 'https://api.relateiq.com/v2'
16
+
17
+ @auth = {username: key, password: secret}
18
+ @headers = {
19
+ 'Content-type' => 'application/json',
20
+ 'Accept' => 'application/json'
21
+ }
22
+ @cache = {}
23
+ end
24
+
25
+ # for caching, used with #next ?
26
+ # def store(endpoint, objects)
27
+ # @cache[endpoint] = objects unless objects.nil?
28
+ # end
29
+
30
+ # Makes a GET request, used for fetching existing objects.
31
+ # @return (see #request)
32
+ def get(endpoint, options: nil)
33
+ request(endpoint, :get, nil, options)
34
+ end
35
+
36
+ # Makes a POST request, used for creating new objects.
37
+ # @return (see #request)
38
+ def post(endpoint, body, options: nil)
39
+ request(endpoint, :post, body, options)
40
+ end
41
+
42
+ # Makes a PUT request, used for updating existing objects.
43
+ # @return (see #request)
44
+ def put(endpoint, body, options: nil)
45
+ request(endpoint, :put, body, options)
46
+ end
47
+
48
+ # Makes a DELETE request, used for deleting existing objects.
49
+ # @return (see #request)
50
+ def delete(endpoint, options: nil)
51
+ request(endpoint, :delete, nil, options)
52
+ end
53
+
54
+ private
55
+
56
+ # actually does the requesting
57
+ # @return (see #process_response)
58
+ def request(endpoint, method = :get, body = nil, options = nil)
59
+ # there may be a better way to do this, but this is close
60
+ url = "#{@root_url}/#{endpoint}"
61
+ # pp "#{method} request to #{url} with body: #{body} and options: #{options}"
62
+ if [:get, :delete].include? method
63
+ resp = HTTParty.method(method).call(
64
+ url,
65
+ headers: @headers,
66
+ basic_auth: @auth,
67
+ query: options
68
+ )
69
+ elsif [:post, :put].include? method
70
+ resp = HTTParty.method(method).call(
71
+ url,
72
+ headers: @headers,
73
+ basic_auth: @auth,
74
+ query: options,
75
+ body: body
76
+ )
77
+ else
78
+ # this shouldn't ever get hit?
79
+ raise RIQError, 'Invalid method'
80
+ end
81
+
82
+ # HTTParty response object
83
+ process_response(resp)
84
+ end
85
+
86
+ def process_response(resp)
87
+ # pp "processing #{resp}, code: #{resp.code}"
88
+ if resp.code == 503
89
+ raise NotImplementedError, 'This function is not currently supported by RelateIQ'
90
+ elsif resp.code == 404
91
+ raise NotFoundError, 'Object Not Found'
92
+ elsif resp.code >= 400
93
+ # Record Response Data
94
+ raise HTTPError, resp
95
+ end
96
+ # 404 Object not found
97
+ # if List, have them check if it is shared
98
+ # 500
99
+ # 422
100
+ # 400 Bad Request - pass on internal message
101
+ # 502 Bad Gateway - Reattempt
102
+
103
+ # if resp.include? 'objects'
104
+ # resp['objects']
105
+ # else
106
+ resp.parsed_response
107
+ # end
108
+ end
109
+ end
110
+
111
+ class << self
112
+ # @param key [String] RelateIQ API key
113
+ # @param secret [String] RelateIQ API secret
114
+ # @return [RIQ] The module, in case you want it.
115
+ def init(key = nil, secret = nil)
116
+ key ||= ENV['RIQ_TEST_API_KEY']
117
+ secret ||= ENV['RIQ_TEST_API_SECRET']
118
+
119
+ @@client = Client.new(key, secret)
120
+ self
121
+ end
122
+
123
+ # Always use RIQ.client to retrieve the client object.
124
+ # @return [Client] The client object
125
+ def client
126
+ raise RIQError, 'Client not initialized' unless @@client
127
+ @@client
128
+ end
129
+ end
130
+ end