riq 0.8.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 34a510c0f7cad37904ababa9279e498c71e0bb9c
4
+ data.tar.gz: 661d369470ad7fa0be4588036ab57206f41a4e55
5
+ SHA512:
6
+ metadata.gz: 71590ee05218590ebb4adc49aebeb462958bf843448d025c466f0b8a53b20ce4b0893d8bf85509790321e8deafc996eda3700ad9a5ad5aa1d8025625d956f386
7
+ data.tar.gz: c3c788129202330ad448cff886a0981b1a594d4808e806f4f444815aefd5929d990aee56936c4b03b143c7f955b03779f49aace5f6645544ce9a6f2e1aec5e48
@@ -0,0 +1,41 @@
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
+
26
+ ## Environment normalisation:
27
+ /.bundle/
28
+ /vendor/bundle
29
+ /lib/bundler/man/
30
+
31
+ # for a library or gem, you might want to ignore these files since the code is
32
+ # intended to run in multiple environments; otherwise, check them in:
33
+ # Gemfile.lock
34
+ # .ruby-version
35
+ # .ruby-gemset
36
+
37
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
38
+ .rvmrc
39
+
40
+ # test file
41
+ 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,0 +1,3 @@
1
+ # Base file from which everything else is included
2
+
3
+ Dir[__dir__ + '/relateiq/*.rb'].each {|file| require file }
@@ -0,0 +1,79 @@
1
+ require_relative 'riq_obj'
2
+
3
+ module RIQ
4
+ class Account < RIQObject
5
+ attr_accessor :name
6
+ attr_accessor :field_values
7
+
8
+ # (see RIQObject#node)
9
+ def node
10
+ "accounts/#{@id}"
11
+ end
12
+
13
+ # (see #node)
14
+ def self.node(arg = nil)
15
+ "accounts"
16
+ end
17
+
18
+ # (see RIQObject#data)
19
+ def data
20
+ {
21
+ id: @id,
22
+ name: @name,
23
+ field_values: @field_values
24
+ }
25
+ end
26
+
27
+ # (see RIQObject#payload)
28
+ def payload
29
+ # TODO: find more elegant way to do this
30
+ pld = data
31
+ pld['fieldValues'] = to_raw(@field_values)
32
+ pld.delete(:field_values)
33
+ pld.to_json
34
+ end
35
+
36
+ # @overload field_value(key)
37
+ # @param key [String, Integer]
38
+ # @return [Array] Value of key
39
+ # @overload field_value(key, value)
40
+ # Sets key to value
41
+ # @param key [String, Integer] Key to set
42
+ # @param value [#to_s] Sets key to value
43
+ def field_value(key, value = nil)
44
+ # TODO: double check that this works with arrays of stuff
45
+ # or, have a format function that casts ints to string on save
46
+ if value.nil?
47
+ @field_values.fetch(key.to_s, nil)
48
+ else
49
+ @field_values[key.to_s] = value.to_s
50
+ {key.to_s => value.to_s}
51
+ end
52
+ end
53
+
54
+ private
55
+ def init(obj = nil)
56
+ unless obj.nil?
57
+ @id = obj['id']
58
+ @name = obj['name']
59
+ @field_values = from_raw(obj['fieldValues'])
60
+ else
61
+ @id = nil
62
+ @name = nil
63
+ @field_values = {}
64
+ end
65
+ self
66
+ end
67
+
68
+ end
69
+
70
+ class << self
71
+ # Convenince method to create new Accounts
72
+ # @param id [String, nil] create a blank Account object or
73
+ # fetch an existing one by id.
74
+ # @return [RIQ::Account]
75
+ def account(id = nil)
76
+ Account.new(id)
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,187 @@
1
+ module RIQ
2
+ # Manages caching and fetching for a certain type of child
3
+ class BatchManager
4
+ attr_reader :fetch_options
5
+
6
+ # @param klass [RIQObject] The child class that's being fetched, such as {Account} or {ListItem}
7
+ # @param page_length [Integer] The number of items per page
8
+ # @!macro [new] krass
9
+ # is a $1
10
+ def initialize(klass, page_length = 200, fetch_options: {})
11
+ @klass = klass
12
+ begin
13
+ raise unless @klass.ancestors.include? RIQ::RIQObject
14
+ rescue
15
+ raise RIQError, 'Must pass a RIQ Class'
16
+ end
17
+
18
+ reset_cache
19
+ # ruby says that nil is an argument and page_size was getting set to 0
20
+ @page_length = page_length.nil? ? 200 : page_length
21
+ @fetch_options = fetch_options
22
+ @client = RIQ.client
23
+ end
24
+
25
+ # Iterator for each item in the manager. Pass a block to it!
26
+ def each(&blok)
27
+ reset_cache
28
+ loop do
29
+ x = next_item
30
+ if x
31
+ blok.call(x)
32
+ else
33
+ return
34
+ end
35
+ end
36
+ end
37
+
38
+ # @return [@macro klass]
39
+ def first
40
+ reset_cache
41
+ fetch_page(0, 1).first
42
+ end
43
+
44
+ def fetch_options=(opts = {})
45
+ reset_cache
46
+ options = {}
47
+ opts.each do |k, v|
48
+ # some args are comma-separated arrays, some are just args
49
+ if v.is_a? Array
50
+ options[k] = v.join(',')
51
+ else
52
+ options[k] = v
53
+ end
54
+ end
55
+ @fetch_options = options
56
+ end
57
+
58
+ private
59
+
60
+ def next_item
61
+ if @cache_index == @cache.size
62
+ return nil if ![0, @page_length].include? @cache.size
63
+ # puts "\n=== fetching page! (#{@page_index}) (#{@page_length})"
64
+ @cache = fetch_page(@page_index, @page_length)
65
+ @page_index += @cache.size
66
+ @cache_index = 0
67
+ end
68
+
69
+ if @cache_index < @cache.size
70
+ obj = @cache[@cache_index]
71
+ @cache_index += 1
72
+ obj
73
+ else
74
+ nil
75
+ end
76
+ end
77
+
78
+ # def fetch_batch(param, values, max_size)
79
+ # objects = []
80
+ # chunks = []
81
+ # (0...values.size).step(max_size) do |i|
82
+ # chunks << values[i...i+max_size]
83
+ # end
84
+ # chunks.each_with_index do |obj, i|
85
+ # begin
86
+ # set_fetch_options({param: chunk.join(',')})
87
+ # objects << fetch_page
88
+ # rescue HTTPError => e
89
+ # # max url length is 8192 (2^13). If longer, will give a 414 error.
90
+ # if [413, 414].include? e.code
91
+ # objects << fetch_batch(param, chunk, (max_size/2.0).ceil)
92
+ # else
93
+ # # raise a certain error?
94
+ # raise e
95
+ # end
96
+ # end
97
+ # end
98
+ # objects
99
+ # end
100
+
101
+ def fetch_page(index = 0, limit = nil)
102
+ limit = @page_length if limit.nil?
103
+
104
+ @fetch_options[:_start] = index
105
+ @fetch_options[:_limit] = limit
106
+
107
+ # all class#node functions can take an arg, so far, only list items need it.
108
+ # having list_id usually be nil (that is, undefined) is fine. probably.
109
+ data = @client.get(@klass.node(@list_id), options: @fetch_options)
110
+
111
+ objects = []
112
+ data.fetch('objects', []).each do |obj|
113
+ objects << @klass.new(obj)
114
+ end
115
+ objects
116
+ end
117
+
118
+ def reset_cache
119
+ # shouldn't reset options, that's silly
120
+ # @fetch_options = {}
121
+ @cache = []
122
+ @cache_index = 0
123
+ @page_index = 0
124
+ end
125
+
126
+ # these used to be 2 methods, now they're one
127
+ def batch(objs, mode)
128
+ return_objs = {}
129
+ while objs.size > 0
130
+ chunk_size = objs.size > @page_length ? @page_length : objs.size
131
+ chunk = objs[0...chunk_size]
132
+ objs = chunk_size < objs.size ? objs[chunk_size..-1] : []
133
+
134
+ batch_objs = batch_helper(mode, chunk)
135
+ return_objs['success_objects'] = batch_objs.fetch('success_objects', [])
136
+ return_objs['error_objects'] = batch_objs.fetch('error_objects', [])
137
+ end
138
+ return_objs
139
+ end
140
+
141
+ def batch_helper(mode, objs)
142
+ payloads = []
143
+ objs.each {|o| payloads << o.payload}
144
+
145
+ if mode == :create
146
+ batch_results = @client.post('/createBatch', {objects: payloads})
147
+ elsif mode == :update
148
+ batch_results = @client.put('/updateBatch', {objects: payloads})
149
+ else
150
+ raise RIQError, 'Invalid Batch Mode'
151
+ end
152
+
153
+ batch_results['success_objects'] = []
154
+ batch_results['successObjects'].each do |o|
155
+ batch_results['success_objects'] << self.class(o)
156
+ end
157
+
158
+ batch_results['error_objects'] = []
159
+ batch_results['errorObjects'].each do |o|
160
+ batch_results['error_objects'] << self.class(o)
161
+ end
162
+
163
+ batch_results
164
+ end
165
+ end
166
+
167
+ class << self
168
+ # @!macro [new] conv
169
+ # Returns an iterator for all $0
170
+ # @return [BatchManager]
171
+
172
+ # @macro conv
173
+ def lists(page_size = nil)
174
+ BatchManager.new(RIQ::List, page_size)
175
+ end
176
+
177
+ # @macro conv
178
+ def contacts(page_size = nil)
179
+ BatchManager.new(RIQ::Contact, page_size)
180
+ end
181
+
182
+ # @macro conv
183
+ def accounts(page_size = nil)
184
+ BatchManager.new(RIQ::Account, page_size)
185
+ end
186
+ end
187
+ end
@@ -0,0 +1,152 @@
1
+ require 'httparty'
2
+ require 'pry'
3
+ require 'json'
4
+
5
+ require_relative 'error'
6
+
7
+ module RIQ
8
+ # HTTP client responsible for actually handling the HTTP requests.
9
+ # @note Utility class to perform http requests. This shouldn't be
10
+ # instantiated directy but used by children of RIQObject instead.
11
+ class Client
12
+ attr_accessor :cache
13
+
14
+ def initialize(key, secret)
15
+ raise 'Missing credentials' if key.nil? || secret.nil?
16
+
17
+ @root_url = 'https://api.relateiq.com/v2'
18
+
19
+ @auth = {username: key, password: secret}
20
+ @headers = {
21
+ 'Content-type' => 'application/json',
22
+ 'Accept' => 'application/json'
23
+ }
24
+ @cache = {}
25
+ end
26
+
27
+ # for caching, used with #next ?
28
+ # def store(endpoint, objects)
29
+ # @cache[endpoint] = objects unless objects.nil?
30
+ # end
31
+
32
+ # Makes a GET request, used for fetching existing objects.
33
+ # @return (see #request)
34
+ def get(endpoint, options: nil)
35
+ request(endpoint, :get, nil, options)
36
+ end
37
+
38
+ # Makes a POST request, used for creating new objects.
39
+ # @return (see #request)
40
+ def post(endpoint, body, options: nil)
41
+ request(endpoint, :post, body, options)
42
+ end
43
+
44
+ # Makes a PUT request, used for updating existing objects.
45
+ # @return (see #request)
46
+ def put(endpoint, body, options: nil)
47
+ request(endpoint, :put, body, options)
48
+ end
49
+
50
+ # Makes a DELETE request, used for deleting existing objects.
51
+ # @return (see #request)
52
+ def delete(endpoint, options: nil)
53
+ request(endpoint, :delete, nil, options)
54
+ end
55
+
56
+ def fetch(endpoint = nil, options: {})
57
+ # i'm not sure why this would return an empty object instead of raising a notfound
58
+ # or really, why it exists at all
59
+ # puts "fetching #{endpoint} with options #{options}"
60
+ # begin
61
+ get(endpoint, options: options)
62
+ # rescue NotFoundError
63
+ # {}
64
+ # end
65
+ end
66
+
67
+ private
68
+
69
+ # actually does the requesting
70
+ # @return (see #process_response)
71
+ def request(endpoint, method = :get, body = nil, options = nil)
72
+ # there may be a better way to do this, but this is close
73
+ url = "#{@root_url}/#{endpoint}"
74
+ # pp "#{method} request to #{url} with body: #{body} and options: #{options}"
75
+ if [:get, :delete].include? method
76
+ resp = HTTParty.method(method).call(
77
+ url,
78
+ headers: @headers,
79
+ basic_auth: @auth,
80
+ query: options
81
+ )
82
+ elsif [:post, :put].include? method
83
+ resp = HTTParty.method(method).call(
84
+ url,
85
+ headers: @headers,
86
+ basic_auth: @auth,
87
+ query: options,
88
+ body: body
89
+ )
90
+ else
91
+ # this shouldn't ever get hit?
92
+ raise RIQError, 'Invalid method'
93
+ end
94
+
95
+ # HTTParty response object
96
+ process_response(resp)
97
+ # resp
98
+ end
99
+
100
+ # Checks for errors and returns the parsed object
101
+ # @return [RIQObject] The result of the request
102
+ def process_response(resp)
103
+ # pp "processing #{resp}, code: #{resp.code}"
104
+ # binding.pry
105
+ if resp.code == 503
106
+ raise NotImplementedError, 'This function is not currently supported by RelateIQ'
107
+ elsif resp.code == 404
108
+ raise NotFoundError, 'Object Not Found'
109
+ elsif resp.code >= 400
110
+ # Record Response Data
111
+ raise HTTPError, resp
112
+ end
113
+ # 404 Object not found
114
+ # if List, have them check if it is shared
115
+ # 500
116
+ # 422
117
+ # 400 Bad Request - pass on internal message
118
+ # 502 Bad Gateway - Reattempt
119
+
120
+ # if resp.include? 'objects'
121
+ # resp['objects']
122
+ # else
123
+ resp.parsed_response
124
+ # end
125
+ end
126
+
127
+ # Configuration Methods
128
+ # def fetchConfig
129
+ # return get('configs')
130
+ # end
131
+ end
132
+
133
+ class << self
134
+ # A singleton client for use by methods in Object.
135
+ # Always use RIQ.client to retrieve the client object.
136
+ # @param key [String] RelateIQ API key
137
+ # @param secret [String] RelateIQ API secret
138
+ # @return [RIQ] The module, in case you want it.
139
+ def init(key = nil, secret = nil)
140
+ key ||= ENV['RIQ_TEST_API_KEY']
141
+ secret ||= ENV['RIQ_TEST_API_SECRET']
142
+
143
+ @@client = Client.new(key, secret)
144
+ self
145
+ end
146
+
147
+ def client
148
+ raise RIQError, 'Client not initialized' unless @@client
149
+ @@client
150
+ end
151
+ end
152
+ end