giftbit 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,60 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: get
5
+ uri: https://testbedapp.giftbit.com/papi/v1/marketplace/vendors
6
+ body:
7
+ encoding: US-ASCII
8
+ string: ''
9
+ headers:
10
+ Accept:
11
+ - application/json
12
+ Accept-Encoding:
13
+ - gzip, deflate
14
+ User-Agent:
15
+ - rest-client/2.0.0 (darwin16.3.0 x86_64) ruby/2.4.0p0
16
+ Authorization:
17
+ - Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJTSEEyNTYifQ==.TFdWYU5VTVhzK3JFaVhHT2p5VDRHNFRsUk1ybnk2V2E4TDNDSW5meEdXdTVERUJNeHlDTEU1K216Qk1hb3Q4QlZaTVJvWndjR2ZoS01xc3gzdnZMUHdQckN1Z0kreG9rYVF3c1JjUjNkNlNLVmROQTlJb0hvMmpHdkx2REh3NXE=.cu7N3bPxGg8fPxFsIUcjyOISwn+29YpB0l7YDHwkMDg=
18
+ Content-Type:
19
+ - application/json
20
+ Host:
21
+ - testbedapp.giftbit.com
22
+ response:
23
+ status:
24
+ code: 200
25
+ message: OK
26
+ headers:
27
+ Content-Type:
28
+ - application/json;charset=UTF-8
29
+ Content-Length:
30
+ - '6443'
31
+ Connection:
32
+ - keep-alive
33
+ Cache-Control:
34
+ - no-cache="set-cookie"
35
+ - private, max-age=60
36
+ Date:
37
+ - Tue, 14 Feb 2017 19:41:58 GMT
38
+ Expires:
39
+ - Tue, 14 Feb 2017 19:42:59 GMT
40
+ Last-Modified:
41
+ - Tue, 14 Feb 2017 19:41:59 GMT
42
+ P3p:
43
+ - CP="Giftbit"
44
+ Server:
45
+ - Apache-Coyote/1.1
46
+ X-Frame-Options:
47
+ - SAMEORIGIN
48
+ X-Cache:
49
+ - Miss from cloudfront
50
+ Via:
51
+ - 1.1 84760d59a7d324aa7adc2bf9d8f7fd4b.cloudfront.net (CloudFront)
52
+ X-Amz-Cf-Id:
53
+ - eB12IajU_EKqKyvMESo_-rMEv8hflBnPBkHRf6R1iGvGRN3O1IRR-g==
54
+ body:
55
+ encoding: ASCII-8BIT
56
+ string: !binary |-
57
+ eyJ2ZW5kb3JzIjpbeyJpZCI6ODYsIm5hbWUiOiJBbWF6b24uY2EiLCJpbWFnZV91cmwiOiJodHRwczovL3VwbG9hZGVkaW1hZ2VzdGVzdGJlZC5naWZ0Yml0LmNvbS8xNDEvYW1hem9uY2EtOWNiMTRjZWYtOGI3OC00YzE1LWJmZDYtMmRhNDFiNDk1Y2E0LmpwZyIsImRpc2NsYWltZXIiOiIqQW1hem9uLmNhIEdpZnQgQ2VydGlmaWNhdGVzIChcIkdDc1wiKSBzb2xkIGJ5IEtpaW5kIEluYy4gREJBIEdpZnRiaXQsIGFuIGF1dGhvcml6ZWQgYW5kIGluZGVwZW5kZW50IHJlc2VsbGVyIG9mIEFtYXpvbi5jYSBHaWZ0IENlcnRpZmljYXRlcy4gR0NzIGFyZSByZWRlZW1hYmxlIG9ubHkgZm9yIGVsaWdpYmxlIHByb2R1Y3RzIG9uIEFtYXpvbi5jYS4gUmV0dXJuIHBvbGljaWVzIGZvciBwcm9kdWN0cyBhcmUgYXZhaWxhYmxlIG9uIEFtYXpvbi5jYS4gRXhjZXB0IGFzIHJlcXVpcmVkIGJ5IGxhdywgR0NzIGNhbm5vdCBiZSByZWxvYWRlZCwgcmVzb2xkLCB0cmFuc2ZlcnJlZCBmb3IgdmFsdWUsIHJlZGVlbWVkIGZvciBjYXNoIG9yIGFwcGxpZWQgdG8gYW55IG90aGVyIGFjY291bnQuIFRvIHZpZXcgYSBHQyBiYWxhbmNlIG9yIGZvciBtb3JlIGluZm9ybWF0aW9uIGFib3V0IHlvdXIgR0MsIHZpc2l0IFwiWW91ciBBY2NvdW50XCIgb24gQW1hem9uLmNhIG9yIGNvbnRhY3QgdXMgYXQgd3d3LmFtYXpvbi5jYS9jb250YWN0LXVzLiBHQ3MgY2Fubm90IGJlIHVzZWQgdG8gcHVyY2hhc2Ugb3RoZXIgR0NzLiBBbWF6b24gaXMgbm90IHJlc3BvbnNpYmxlIGlmIGEgR0MgaXMgbG9zdCwgc3RvbGVuLCBkZXN0cm95ZWQgb3IgdXNlZCB3aXRob3V0IHBlcm1pc3Npb24uIEZvciBjb21wbGV0ZSB0ZXJtcyBhbmQgY29uZGl0aW9ucywgc2VlIHd3dy5hbWF6b24uY2EvZ2MtbGVnYWwuIEdDcyBhcmUgaXNzdWVkIGJ5IEFtYXpvbi5jb20uY2EsIEluYy4sIGEgRGVsYXdhcmUgY29ycG9yYXRpb24uIEFsbCBBbWF6b24gwq4sIOKEoiAmIMKpIGFyZSBJUCBvZiBBbWF6b24uY29tLCBJbmMuIG9yIGl0cyBhZmZpbGlhdGVzLiBObyBleHBpcmF0aW9uIGRhdGUgb3Igc2VydmljZSBmZWVzLiJ9LHsiaWQiOjc5LCJuYW1lIjoiQW1hem9uIiwiaW1hZ2VfdXJsIjoiaHR0cHM6Ly91cGxvYWRlZGltYWdlc3Rlc3RiZWQuZ2lmdGJpdC5jb20vODAvYW1hem9uLTMwZWM4ZjdhLTYxNmEtNDZhYS1hYTNmLWNhZmJjYTEwYmRkYy5qcGciLCJkaXNjbGFpbWVyIjoiQW1hem9uLmNvbSBHaWZ0IENhcmRzIChcIkdDc1wiKSBzb2xkIGJ5IEtpaW5kIEluYy4sIGFuIGF1dGhvcml6ZWQgYW5kIGluZGVwZW5kZW50IHJlc2VsbGVyIG9mIEFtYXpvbi5jb20gR2lmdCBDYXJkcy4gRXhjZXB0IGFzIHJlcXVpcmVkIGJ5IGxhdywgR0NzIGNhbm5vdCBiZSB0cmFuc2ZlcnJlZCBmb3IgdmFsdWUgb3IgcmVkZWVtZWQgZm9yIGNhc2guIEdDcyBtYXkgYmUgdXNlZCBvbmx5IGZvciBwdXJjaGFzZXMgb2YgZWxpZ2libGUgZ29vZHMgYXQgQW1hem9uLmNvbSBvciBjZXJ0YWluIG9mIGl0cyBhZmZpbGlhdGVkIHdlYnNpdGVzLiBGb3IgY29tcGxldGUgdGVybXMgYW5kIGNvbmRpdGlvbnMsIHNlZSB3d3cuYW1hem9uLmNvbS9nYy1sZWdhbC4gR0NzIGFyZSBpc3N1ZWQgYnkgQUNJIEdpZnQgQ2FyZHMsIEluYy4sIGEgV2FzaGluZ3RvbiBjb3Jwb3JhdGlvbi4gwqkswq4s4oSiIEFtYXpvbi5jb20gSW5jLiBhbmQvb3IgaXRzIGFmZmlsaWF0ZXMsIDIwMTMuIE5vIGV4cGlyYXRpb24gZGF0ZSBvciBzZXJ2aWNlIGZlZXMuIn0seyJpZCI6MTMxLCJuYW1lIjoiV2FsbWFydCIsImltYWdlX3VybCI6Imh0dHBzOi8vdXBsb2FkZWRpbWFnZXN0ZXN0YmVkLmdpZnRiaXQuY29tLzIyNi9XYWxtYXJ0bG9nby04MzIwZWJlMS1hNWJhLTQxYmItYTVlMC0wYjM4OGMwZDY4MzMucG5nIn0seyJpZCI6ODAsIm5hbWUiOiJpVHVuZXMgQ2FuYWRhIiwiaW1hZ2VfdXJsIjoiaHR0cHM6Ly91cGxvYWRlZGltYWdlc3Rlc3RiZWQuZ2lmdGJpdC5jb20vMTA1L2lUdW5lczItZTU3MmVhY2ItZTU1MC00MmMxLTk5MWItZGU0YmJiNjQ3NWYwLmpwZyIsImRpc2NsYWltZXIiOiJNYWMgYW5kIE9TIFggYXJlIHRyYWRlbWFya3Mgb2YgQXBwbGUgSW5jLiwgcmVnaXN0ZXJlZCBpbiB0aGUgVS5TLiBhbmQgb3RoZXIgY291bnRyaWVzLiBpVHVuZXMgaXMgYSByZWdpc3RlcmVkIHRyYWRlbWFyayBvZiBBcHBsZSBJbmMuLCBBbGwgcmlnaHRzIHJlc2VydmVkLiJ9LHsiaWQiOjgxLCJuYW1lIjoiaVR1bmVzIFVTQSIsImltYWdlX3VybCI6Imh0dHBzOi8vdXBsb2FkZWRpbWFnZXN0ZXN0YmVkLmdpZnRiaXQuY29tLzEwNi9pVHVuZXMyLTQ4OTdjODJhLTViZGQtNDI3Ni04NTUyLTU0MzQzOGU3MDkxYy5qcGciLCJkaXNjbGFpbWVyIjoiTWFjIGFuZCBPUyBYIGFyZSB0cmFkZW1hcmtzIG9mIEFwcGxlIEluYy4sIHJlZ2lzdGVyZWQgaW4gdGhlIFUuUy4gYW5kIG90aGVyIGNvdW50cmllcy4gaVR1bmVzIGlzIGEgcmVnaXN0ZXJlZCB0cmFkZW1hcmsgb2YgQXBwbGUgSW5jLiwgQWxsIHJpZ2h0cyByZXNlcnZlZC4ifSx7ImlkIjoxMDgsIm5hbWUiOiJUYXJnZXQiLCJpbWFnZV91cmwiOiJodHRwczovL3VwbG9hZGVkaW1hZ2VzdGVzdGJlZC5naWZ0Yml0LmNvbS8xNjEvdGFyZ2V0LTNhOTlmYjJkLTAwZTgtNDg0ZC1hMjJmLTRlOTY2MTZjMDM0Yi5QTkciLCJkaXNjbGFpbWVyIjoiVGhlIEJ1bGxzZXllIERlc2lnbiwgVGFyZ2V0IGFuZCBUYXJnZXQgR2lmdENhcmRzwq4gYXJlIHJlZ2lzdGVyZWQgdHJhZGVtYXJrcyBvZiBUYXJnZXQgQnJhbmRzLCBJbmMuIFRlcm1zIGFuZCBjb25kaXRpb25zIGFyZSBhcHBsaWVkIHRvIGdpZnQgY2FyZHMuIFRhcmdldCBpcyBub3QgYSBwYXJ0aWNpcGF0aW5nIHBhcnRuZXIgaW4gb3Igc3BvbnNvciBvZiB0aGlzIG9mZmVyLiJ9LHsiaWQiOjY5LCJuYW1lIjoiVGhlIEdhcCIsImltYWdlX3VybCI6Imh0dHBzOi8vdXBsb2FkZWRpbWFnZXN0ZXN0YmVkLmdpZnRiaXQuY29tLzcwL2dhcC1iODI0ZmEyMC04MzMwLTQ5NjEtYTIxNy0xOTA4NjY0MWUxYzMucG5nIn0seyJpZCI6MSwibmFtZSI6IjIlIEphenogQ29mZmVlIiwiaW1hZ2VfdXJsIjoiaHR0cHM6Ly91cGxvYWRlZGltYWdlc3Rlc3RiZWQuZ2lmdGJpdC5jb20vMi8ycGVyZ2lmdGRmYTIwMDVmNmFiYzQ5ODVhZGIxODllMmZhNDBjOGEyLTRmM2JlMzllLTQyMGEtNDgwMy04Y2U3LWJlNmZiNDZkOTk0Zi5wbmcifSx7ImlkIjoxMDEsIm5hbWUiOiJNb250YW5hJ3MgQ29va2hvdXNlIiwiaW1hZ2VfdXJsIjoiaHR0cHM6Ly91cGxvYWRlZGltYWdlc3Rlc3RiZWQuZ2lmdGJpdC5jb20vMTU1L21vbnRhbmFzLThiNWRmNmUzLWNhOWUtNDFiNi1hZTliLWUxZjc4MWUxYTFjNS5wbmcifSx7ImlkIjo5OSwibmFtZSI6IktlbHNleSdzIiwiaW1hZ2VfdXJsIjoiaHR0cHM6Ly91cGxvYWRlZGltYWdlc3Rlc3RiZWQuZ2lmdGJpdC5jb20vMTUwL0tlbHNleXNsb2dvLWFhMWIyMzM3LTUzZjItNDk4ZS1hYTI1LWE0OTY2MmZiN2JjNy5wbmcifSx7ImlkIjo5NiwibmFtZSI6IkhhcnZleSdzIiwiaW1hZ2VfdXJsIjoiaHR0cHM6Ly91cGxvYWRlZGltYWdlc3Rlc3RiZWQuZ2lmdGJpdC5jb20vMTUxL0hhcnZleXNMb2dvLTMzYjk4MTJjLWQ5MjMtNDU2MC04ODJkLWY1ZjhmM2JmZWIxMC5wbmcifSx7ImlkIjoxNDAsIm5hbWUiOiJUZXN0IE1lcmNoYW50IiwiaW1hZ2VfdXJsIjoiaHR0cHM6Ly91cGxvYWRlZGltYWdlc3Rlc3RiZWQuZ2lmdGJpdC5jb20vTWVyY2gvTWVyY2hhbnRJY29uLWUwNjBmNGQxLWMyMGUtNDc0Ni1iYzc1LTQ5NjgwMDY3MDAzMS5wbmcifSx7ImlkIjo5MCwibmFtZSI6IkNpbmVwbGV4IE9kZW4iLCJpbWFnZV91cmwiOiJodHRwczovL3VwbG9hZGVkaW1hZ2VzdGVzdGJlZC5naWZ0Yml0LmNvbS8xNTYvQ2luZXBsZXgzMDBieTMwMC0yZWE4MWY2Ni0zNTBkLTQ0YWEtYjM0NC0zYzRhNjIwZWRiODMucG5nIn0seyJpZCI6MTM1LCJuYW1lIjoiVmlzYSIsImltYWdlX3VybCI6Imh0dHBzOi8vdXBsb2FkZWRpbWFnZXN0ZXN0YmVkLmdpZnRiaXQuY29tLzMxMS9WaXJ0dWFsUmV3YXJkMWY3MzA1YTViY2NmNTQ4NmFhMTJiZGM2OTllMTBkMDU5LWNiN2M1ODg1LTZjYzctNDEzZi1hZjg1LTYwNDhhNmEzNjg0Yy5wbmcifSx7ImlkIjo4NCwibmFtZSI6IlN0YXJidWNrcyIsImltYWdlX3VybCI6Imh0dHBzOi8vdXBsb2FkZWRpbWFnZXN0ZXN0YmVkLmdpZnRiaXQuY29tLzEyMS9zdGFyYnVja3MtZjIyZGI1NjQtYWMzYS00YWUxLTljODgtOThlNzlmN2U1NzAyLnBuZyJ9LHsiaWQiOjEyMSwibmFtZSI6IlNvY2lsb2dpY2EgUmV0YWlsZXIxIiwiaW1hZ2VfdXJsIjoiaHR0cHM6Ly91cGxvYWRlZGltYWdlc3Rlc3RiZWQuZ2lmdGJpdC5jb20vMTkyL3lvdXJsb2dvaGVyZTI3LWEyNDhmMTE2LWZkMzUtNDg5NS04YjllLWVlYTUzNjk2ZTI4ZC5wbmciLCJkaXNjbGFpbWVyIjoiVGhpcyBpcyBhIGRpc2NsYWltZXIgZm9yIGEgdGVzdGJlZCBnaWZ0In0seyJpZCI6NzYsIm5hbWUiOiJTcGEgV2VlayIsImltYWdlX3VybCI6Imh0dHBzOi8vdXBsb2FkZWRpbWFnZXN0ZXN0YmVkLmdpZnRiaXQuY29tLzc3L3NwYXdlZWstOTBmZjQ4ZTQtNTYwMS00ZmRlLWIwYTUtYWIzNjlhNDkzODNhLnBuZyJ9LHsiaWQiOjUwLCJuYW1lIjoiUGFwYSBHaW5vJ3MgUGl6emVyaWEiLCJpbWFnZV91cmwiOiJodHRwczovL3VwbG9hZGVkaW1hZ2VzdGVzdGJlZC5naWZ0Yml0LmNvbS81MS9wYXBhZ2lub3MtYzIyY2M3M2QtYzE1Yy00NjMwLWI3OTctYTc0NWQzNTUzOWUzLnBuZyJ9LHsiaWQiOjEwMiwibmFtZSI6Ik5pa2UiLCJpbWFnZV91cmwiOiJodHRwczovL3VwbG9hZGVkaW1hZ2VzdGVzdGJlZC5naWZ0Yml0LmNvbS8xNTgvbmlrZS0yZmZmMTQwNC0xMWVjLTQxMTEtYjgwNi0xZmM1ODEzZjQ5OTcucG5nIiwiZGlzY2xhaW1lciI6IlRoZSBTd29vc2ggZGVzaWduIGlzIGEgcmVnaXN0ZXJlZCB0cmFkZW1hcmsgb2YgTklLRS4gQWxsIHJpZ2h0cyByZXNlcnZlZC4gTklLRSBpcyBub3QgYSBwYXJ0aWNpcGFudCBpbiBvciBzcG9uc29yIG9mIHRoaXMgcHJvbW90aW9uLiJ9LHsiaWQiOjEwMCwibmFtZSI6Ik1pbGVzdG9uZXMgR3JpbGwgYW5kIEJhciIsImltYWdlX3VybCI6Imh0dHBzOi8vdXBsb2FkZWRpbWFnZXN0ZXN0YmVkLmdpZnRiaXQuY29tLzE1NC9taWxlc3RvbmVzMS00OTk4NzZkYS03NjZmLTQyZGMtYTZmZi1iZDNmYjVhNzBkZjAuanBnIn0seyJpZCI6OTcsIm5hbWUiOiJIb21lIERlcG90IiwiaW1hZ2VfdXJsIjoiaHR0cHM6Ly91cGxvYWRlZGltYWdlc3Rlc3RiZWQuZ2lmdGJpdC5jb20vMTUyL2hvbWVkZXBvdC1hOTMwZjhiNy03NGIxLTQ3N2QtODk1Ni0xNjlhOGYxMzcxOTYucG5nIn0seyJpZCI6OTUsIm5hbWUiOiJHbG9iYWwgSG90ZWwgQ2FyZCBwb3dlcmVkIGJ5IE9yYml0eiIsImltYWdlX3VybCI6Imh0dHBzOi8vdXBsb2FkZWRpbWFnZXN0ZXN0YmVkLmdpZnRiaXQuY29tLzE0Ni9naGNwb3Nob3Jpem9udGFsLTIwNzJiOWY5LTQ0YjgtNDliMi1iNGQ0LWQ0MjE4OThjMmQ4ZS5wbmcifSx7ImlkIjoxMzcsIm5hbWUiOiJNYXN0ZXJDYXJkIiwiaW1hZ2VfdXJsIjoiaHR0cHM6Ly91cGxvYWRlZGltYWdlc3Rlc3RiZWQuZ2lmdGJpdC5jb20vMzI2L21hc3RlcmNhcmRsb2dvMjk3NjRjOTU2OTRhOWNkYWI0NzA2OTBhMDE2NWE3MTExMDViZS1lYzZiY2JmMC1jODBjLTQzZDctYWU0Ny1mYzgyYjY4ZGVkNzkuanBnIiwiZGlzY2xhaW1lciI6IkRpc2NsYWltZXIsIGlmIG5lZWRlZCwgZ29lcyBoZXJlIn0seyJpZCI6MTMyLCJuYW1lIjoiRWFzdCBTaWRlIE1hcmlvJ3MiLCJpbWFnZV91cmwiOiJodHRwczovL3VwbG9hZGVkaW1hZ2VzdGVzdGJlZC5naWZ0Yml0LmNvbS8yNDYvRWFzdFNpZGVNYXJpb3Nsb2dvMjAxMy1mZjc4MThjYS1mZDRlLTQ0MWItOWEyNS00NTVhNjdiMmYxYTQucG5nIn0seyJpZCI6MTI4LCJuYW1lIjoiTWVyY2hhbnQgRGVtbyIsImltYWdlX3VybCI6Imh0dHBzOi8vdXBsb2FkZWRpbWFnZXN0ZXN0YmVkLmdpZnRiaXQuY29tL0dGQlQxMjgvTWVyY2hhbnRJY29uMngtMWM2MzBmZGItYWJmZC00YjAwLTk0OGMtYzRiZGQzYmMwZDZkLnBuZyJ9LHsiaWQiOjExMiwibmFtZSI6IlRhcmdldCIsImltYWdlX3VybCI6Imh0dHBzOi8vdXBsb2FkZWRpbWFnZXN0ZXN0YmVkLmdpZnRiaXQuY29tLzE4NC90YXJnZXQtZGMyN2ViYzAtZWFmMS00NmRiLWIzMmYtNDY1Mjg3N2M1MDczLlBORyJ9LHsiaWQiOjEwNywibmFtZSI6IlN3aXNzIENoYWxldCIsImltYWdlX3VybCI6Imh0dHBzOi8vdXBsb2FkZWRpbWFnZXN0ZXN0YmVkLmdpZnRiaXQuY29tLzE1Ny9zd2lzcy02ZjU4ZWFhZi1hMzljLTQ3N2MtODZlYS0yYTFkZGNlYTMxYjEucG5nIn1dLCJpbmZvIjp7ImNvZGUiOiJJTkZPX01BUktFVFBMQUNFX1JFVFJJRVZFRF9WRU5ET1JTIiwibmFtZSI6Ik1hcmtldHBsYWNlIFZlbmRvcnMgUmV0cmlldmVkIiwibWVzc2FnZSI6IkEgbGlzdCBvZiBtYXJrZXRwbGFjZSB2ZW5kb3JzIGhhcyBiZWVuIHJldHJpZXZlZC4ifX0=
58
+ http_version:
59
+ recorded_at: Tue, 14 Feb 2017 19:41:59 GMT
60
+ recorded_with: VCR 3.0.3
@@ -0,0 +1,28 @@
1
+ lib = File.expand_path('../lib', __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require 'giftbit/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'giftbit'
7
+ spec.version = Giftbit::VERSION
8
+ spec.authors = ['Sean Linsley']
9
+ spec.email = ['sean@modernmsg.com']
10
+ spec.summary = 'A Ruby gem for the Giftbit API'
11
+ spec.homepage = 'https://www.github.com/modernmsg/giftbit'
12
+ spec.license = 'MIT'
13
+
14
+ spec.files = `git ls-files`.split($/)
15
+ spec.test_files = spec.files.grep(/spec\//)
16
+ spec.require_paths = ['lib']
17
+
18
+ spec.required_ruby_version = '>= 2'
19
+
20
+ spec.add_dependency 'rest-client'
21
+ spec.add_dependency 'activesupport'
22
+
23
+ spec.add_development_dependency 'rake'
24
+ spec.add_development_dependency 'rspec'
25
+ spec.add_development_dependency 'pry'
26
+ spec.add_development_dependency 'vcr'
27
+ spec.add_development_dependency 'webmock'
28
+ end
@@ -0,0 +1,78 @@
1
+ require 'giftbit/version'
2
+ require 'giftbit/base'
3
+
4
+ class Giftbit
5
+ extend Base
6
+ include Base
7
+
8
+ # Class-level methods only work if you have a single API account. This lets
9
+ # you instantiate the API for a given account, if you have multiple.
10
+ def initialize(auth:)
11
+ fail 'no auths set' unless auths = self.class.auths
12
+ self.auth = auths.fetch(auth)
13
+ end
14
+
15
+ # This lets you call the same API requests on every account you have.
16
+ # This is useful e.g. to check the status of every gift in every account.
17
+ def self.each_auth
18
+ fail 'no auths set' unless auths
19
+ auths.each do |name, _|
20
+ yield new auth: name
21
+ end
22
+ end
23
+
24
+ def ==(other)
25
+ other.is_a?(Giftbit) && auth == other.auth
26
+ end
27
+
28
+ module Methods
29
+ def account
30
+ get ''
31
+ end
32
+
33
+ def funds
34
+ get 'funds'
35
+ end
36
+
37
+ def marketplace(params = {})
38
+ get 'marketplace', params: params
39
+ end
40
+
41
+ def regions(params = {})
42
+ get 'marketplace/regions', params: params
43
+ end
44
+
45
+ def vendors(params = {})
46
+ get 'marketplace/vendors', params: params
47
+ end
48
+
49
+ def campaign(params = {})
50
+ get 'campaign', params: params
51
+ end
52
+
53
+ def gifts(params = {})
54
+ get 'gifts', params: params
55
+ end
56
+
57
+ def create_gift(body = {})
58
+ body[:expiry] ||= (Date.today + 365).to_s
59
+
60
+ post 'campaign', body: body
61
+ end
62
+
63
+ def delete_gift(gift_uuid)
64
+ delete "gifts/#{gift_uuid}"
65
+ end
66
+
67
+ def resend_gift(gift_uuid)
68
+ put "gifts/#{gift_uuid}", body: {resend: true}
69
+ end
70
+
71
+ def get_links(campaign_id)
72
+ get "links/#{campaign_id}"
73
+ end
74
+ end
75
+
76
+ extend Methods
77
+ include Methods
78
+ end
@@ -0,0 +1,99 @@
1
+ require 'active_support'
2
+ require 'json'
3
+ require 'rest-client'
4
+
5
+ class Giftbit
6
+ module Base
7
+ def self.extended(klass)
8
+ klass.send :cattr_accessor, :endpoint, :auth, :auths
9
+ klass.endpoint = 'https://testbedapp.giftbit.com/papi/v1/'
10
+ end
11
+
12
+ def self.included(klass)
13
+ klass.send :attr_accessor, :auth
14
+ end
15
+
16
+ def get_auth
17
+ fail "auth and auths can't both be set" if inherited_auth && auths?
18
+
19
+ if auth = instance? ? self.auth : inherited_auth
20
+ auth
21
+ elsif auths?
22
+ fail 'you must init a new API instance with the auth you want to use'
23
+ else
24
+ fail 'you must set an auth token at application boot'
25
+ end
26
+ end
27
+
28
+ def inherited_auth
29
+ instance? ? self.class.auth : auth
30
+ end
31
+
32
+ def inherited_endpoint
33
+ instance? ? self.class.endpoint : endpoint
34
+ end
35
+
36
+ def auths?
37
+ auths = instance? ? self.class.auths : self.auths
38
+ auths && auths.any?
39
+ end
40
+
41
+ def instance?
42
+ self.class == Giftbit
43
+ end
44
+
45
+ private
46
+
47
+ def default_resource_options
48
+ {
49
+ headers: {
50
+ "Authorization" => "Bearer #{get_auth}",
51
+ "Accept" => "application/json",
52
+ "Content-Type" => "json"
53
+ }
54
+ }
55
+ end
56
+
57
+ def resource(options = {})
58
+ RestClient::Resource.new inherited_endpoint, default_resource_options.deep_merge(options)
59
+ end
60
+
61
+ def response(method, resource, options = {})
62
+ if body = options.delete(:body)
63
+ json = resource.send(method, JSON.generate(body), options)
64
+ else
65
+ json = resource.send(method, options)
66
+ end
67
+
68
+ JSON.parse json
69
+ rescue RestClient::ExceptionWithResponse => e
70
+ case e.http_code
71
+ when 400..499
72
+ JSON.parse e.response
73
+ when 500..599
74
+ {
75
+ 'status' => e.http_code,
76
+ 'error' => { 'message' => e.response }
77
+ }
78
+ else
79
+ raise
80
+ end
81
+ end
82
+
83
+ def get(path, request_options = {}, resource_options = {})
84
+ response :get, resource(resource_options)[path], request_options
85
+ end
86
+
87
+ def delete(path, request_options = {}, resource_options = {})
88
+ response :delete, resource(resource_options)[path], request_options
89
+ end
90
+
91
+ def post(path, request_options = {}, resource_options = {})
92
+ response :post, resource(resource_options)[path], request_options
93
+ end
94
+
95
+ def put(path, request_options = {}, resource_options = {})
96
+ response :put, resource(resource_options)[path], request_options
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,3 @@
1
+ class Giftbit
2
+ VERSION = '1.0.0'
3
+ end
@@ -0,0 +1,284 @@
1
+ require 'giftbit'
2
+ require 'vcr'
3
+ require 'webmock'
4
+
5
+ VCR.configure do |config|
6
+ config.cassette_library_dir = 'fixtures/vcr_cassettes'
7
+ config.hook_into :webmock
8
+ config.allow_http_connections_when_no_cassette = true
9
+ end
10
+
11
+ def retry_request(cassette)
12
+ if cassette.recording?
13
+ VCR.eject_cassette
14
+
15
+ response = yield
16
+ attempts = 1
17
+
18
+ until response || attempts > 12 do
19
+ sleep 5
20
+ response = yield
21
+ attempts += 1
22
+ end
23
+
24
+ VCR.insert_cassette(cassette.name, record: :new_episodes)
25
+ end
26
+
27
+ yield
28
+ end
29
+
30
+ describe Giftbit do
31
+ let(:auth) { 'eyJ0eXAiOiJKV1QiLCJhbGciOiJTSEEyNTYifQ==.TFdWYU5VTVhzK3JFaVhHT2p5VDRHNFRsUk1ybnk2V2E4TDNDSW5meEdXdTVERUJNeHlDTEU1K216Qk1hb3Q4QlZaTVJvWndjR2ZoS01xc3gzdnZMUHdQckN1Z0kreG9rYVF3c1JjUjNkNlNLVmROQTlJb0hvMmpHdkx2REh3NXE=.cu7N3bPxGg8fPxFsIUcjyOISwn+29YpB0l7YDHwkMDg=' }
32
+ let :data do
33
+ {
34
+ id: "GiftbitGift#{Time.now.utc.to_i}",
35
+ message: 'Thank you for being an awesome person',
36
+ subject: 'Present from Community Rewards',
37
+ contacts: [firstname: 'Sean', lastname: 'Linsley', email: 'sean@modernmsg.com'],
38
+ marketplace_gifts: [id: 1, price_in_cents: 5000]
39
+ }
40
+ end
41
+
42
+ after do
43
+ Giftbit.auth = nil
44
+ Giftbit.auths = nil
45
+ end
46
+
47
+ context 'authentication' do
48
+ it 'errors without auth' do
49
+ expect{
50
+ Giftbit.account
51
+ }.to raise_error 'you must set an auth token at application boot'
52
+ end
53
+
54
+ it 'errors without auth, but auths set' do
55
+ Giftbit.auths = {default: auth}
56
+
57
+ expect{
58
+ Giftbit.account
59
+ }.to raise_error 'you must init a new API instance with the auth you want to use'
60
+ end
61
+
62
+ it 'errors for unregistered auth' do
63
+ Giftbit.auths = {default: auth}
64
+
65
+ expect{
66
+ Giftbit.new(auth: :foo).account
67
+ }.to raise_error KeyError, 'key not found: :foo'
68
+ end
69
+
70
+ it 'errors when both auth and auths are set' do
71
+ Giftbit.auth = auth
72
+ Giftbit.auths = {default: auth}
73
+
74
+ expect{
75
+ Giftbit.account
76
+ }.to raise_error "auth and auths can't both be set"
77
+
78
+ expect{
79
+ Giftbit.new(auth: :default).account
80
+ }.to raise_error "auth and auths can't both be set"
81
+ end
82
+ end
83
+
84
+ describe '.each_auth' do
85
+ it 'returns each auth' do
86
+ Giftbit.auths = {default: auth, custom: 'foo'}
87
+
88
+ times_called = 0
89
+
90
+ Giftbit.each_auth do |api|
91
+ times_called += 1
92
+
93
+ expect(api).to be_an_instance_of Giftbit
94
+ expect([auth, 'foo']).to include api.auth
95
+ end
96
+
97
+ expect(times_called).to eq 2
98
+ end
99
+ end
100
+
101
+ [:class, :instance].each do |variant|
102
+ describe "(#{variant})" do
103
+ before do |example|
104
+ if variant == :class
105
+ Giftbit.auth = example.metadata[:invalid_auth] ? 'notavalidtoken' : auth
106
+ else
107
+ Giftbit.auths = {default: auth}
108
+ Giftbit.auths.merge! invalid: 'notavalidtoken' if example.metadata[:invalid_auth]
109
+ end
110
+ end
111
+
112
+ let :api do |example|
113
+ if variant == :class
114
+ Giftbit
115
+ else
116
+ auth = example.metadata[:invalid_auth] ? :invalid : :default
117
+ Giftbit.new auth: auth
118
+ end
119
+ end
120
+
121
+ context 'requests' do
122
+ it 'parses JSON for a successful request' do
123
+ VCR.use_cassette('request-200') do
124
+ json = api.create_gift(data)
125
+
126
+ expect(json['status']).to eql 200
127
+ end
128
+ end
129
+
130
+ it 'parses JSON for a client error' do
131
+ VCR.use_cassette('request-400') do
132
+ json = api.create_gift
133
+
134
+ expect(json['status']).to eql 422
135
+ expect(json).to have_key 'error'
136
+ end
137
+ end
138
+
139
+ it 'creates JSON for a server error' do
140
+ VCR.use_cassette('request-500') do
141
+ json = api.create_gift
142
+
143
+ expect(json['status']).to eql 503
144
+ expect(json.dig('error', 'message')).to include '<html>'
145
+ end
146
+ end
147
+ end
148
+
149
+ describe '#account' do
150
+ it 'returns welcome message with valid credentials' do
151
+ VCR.use_cassette('account-valid') do
152
+ res = api.account
153
+
154
+ expect(res['info']['name']).to eql 'Credentials are valid'
155
+ end
156
+ end
157
+
158
+ it 'returns unauthorized message with invalid credentials', invalid_auth: true do
159
+ VCR.use_cassette('account-invalid') do
160
+ res = api.account
161
+
162
+ expect(res['text']).to eql 'Unauthorized'
163
+ expect(res['status']).to eql 401
164
+ end
165
+ end
166
+ end
167
+
168
+ describe '#funds' do
169
+ it 'returns funds info' do
170
+ VCR.use_cassette('funds') do
171
+ res = api.funds
172
+ expect(res['info']['name']).to eql 'Fund information retrieved'
173
+ expect(res['fundsbycurrency']['USD']['available_in_cents']). to be >= 0
174
+ end
175
+ end
176
+ end
177
+
178
+ describe '#marketplace' do
179
+ it 'lists all gifts' do
180
+ VCR.use_cassette('marketplace-all') do
181
+ res = api.marketplace
182
+
183
+ expect(res['info']['name']).to eql 'Marketplace Gifts Retrieved'
184
+ end
185
+ end
186
+
187
+ it 'lists all gifts by vendor' do
188
+ VCR.use_cassette('marketplace-by-vendor') do
189
+ res = api.marketplace vendor: 6
190
+
191
+ expect(res['total_count']).to be >= 0
192
+ end
193
+ end
194
+ end
195
+
196
+ describe '#regions' do
197
+ it 'lists all regions' do
198
+ VCR.use_cassette('regions-all') do
199
+ res = api.regions
200
+
201
+ expect(res['regions'].count).to eql 4
202
+ end
203
+ end
204
+ end
205
+
206
+ describe '#vendors' do
207
+ it 'list all vendors' do
208
+ VCR.use_cassette('vendors-all') do
209
+ res = api.vendors
210
+
211
+ expect(res['vendors'].count).to eql 27
212
+ end
213
+ end
214
+ end
215
+
216
+ describe '#campaign' do
217
+ it 'list all campaigns created' do
218
+ VCR.use_cassette('campaign-all') do
219
+ res = api.campaign
220
+
221
+ expect(res['campaigns'].count).to be >= 5
222
+ end
223
+ end
224
+
225
+ it 'can fetch campaign with ID provided' do
226
+ VCR.use_cassette('campaign-by-id') do
227
+ gift = api.create_gift(data)
228
+
229
+ res = api.campaign(id: gift['info']['id'])
230
+
231
+ expect(res['info']['id']).to eql gift['info']['id']
232
+ end
233
+ end
234
+ end
235
+
236
+ describe '#gifts' do
237
+ it 'can fetch the gifts for a given campaign' do
238
+ VCR.use_cassette('gifts') do
239
+ gifts = api.gifts
240
+
241
+ expect(gifts['gifts'].length).to be > 0
242
+ end
243
+ end
244
+ end
245
+
246
+ describe '#create_gift' do
247
+ it 'creates a gift' do
248
+ VCR.use_cassette('create_gift') do
249
+ gift = api.create_gift(data)
250
+
251
+ expect(gift['info']['name']).to eql 'Campaign Created'
252
+ end
253
+ end
254
+ end
255
+
256
+ describe '#resend_gift' do
257
+ it 'can re-send an email for a given campaign gift' do
258
+ VCR.use_cassette('resend_gift') do |cassette|
259
+ gift = api.create_gift(data)
260
+ gift = retry_request(cassette) do
261
+ api.gifts(campaign_uuid: gift['campaign']['uuid'])['gifts'].first
262
+ end
263
+ res = api.resend_gift(gift['uuid'])
264
+
265
+ expect(res['info']['code']).to eql 'INFO_GIFTS_RESENT'
266
+ end
267
+ end
268
+ end
269
+
270
+ describe '#get_links' do
271
+ it 'returns link status' do
272
+ VCR.use_cassette('get_links') do
273
+ data[:delivery_type] = 'SHORTLINK'
274
+ gift = api.create_gift(data)
275
+
276
+ res = api.get_links(gift['campaign']['id'])
277
+
278
+ expect(res['info']['code']).to eql 'INFO_LINKS_GENERATION_IN_PROGRESS'
279
+ end
280
+ end
281
+ end
282
+ end
283
+ end
284
+ end