riaction 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +4 -0
- data/Gemfile.lock +59 -0
- data/README +110 -0
- data/Rakefile +1 -0
- data/lib/riaction.rb +3 -0
- data/lib/riaction/iactionable/api.rb +209 -0
- data/lib/riaction/iactionable/connection.rb +114 -0
- data/lib/riaction/iactionable/error.rb +17 -0
- data/lib/riaction/iactionable/objects.rb +6 -0
- data/lib/riaction/iactionable/objects/achievement.rb +19 -0
- data/lib/riaction/iactionable/objects/awardable.rb +44 -0
- data/lib/riaction/iactionable/objects/challenge.rb +18 -0
- data/lib/riaction/iactionable/objects/goal.rb +19 -0
- data/lib/riaction/iactionable/objects/i_actionable_object.rb +40 -0
- data/lib/riaction/iactionable/objects/identifier.rb +11 -0
- data/lib/riaction/iactionable/objects/leaderboard.rb +10 -0
- data/lib/riaction/iactionable/objects/leaderboard_report.rb +23 -0
- data/lib/riaction/iactionable/objects/level.rb +18 -0
- data/lib/riaction/iactionable/objects/level_type.rb +10 -0
- data/lib/riaction/iactionable/objects/point_type.rb +10 -0
- data/lib/riaction/iactionable/objects/profile_level.rb +16 -0
- data/lib/riaction/iactionable/objects/profile_points.rb +21 -0
- data/lib/riaction/iactionable/objects/profile_summary.rb +24 -0
- data/lib/riaction/iactionable/objects/progress.rb +37 -0
- data/lib/riaction/iactionable/settings.rb +30 -0
- data/lib/riaction/riaction.rb +368 -0
- data/lib/riaction/version.rb +3 -0
- data/lib/tasks/riaction.rake +101 -0
- data/riaction.gemspec +32 -0
- data/spec/api_spec.rb +288 -0
- data/spec/connection_spec.rb +111 -0
- data/spec/riaction_spec.rb +253 -0
- data/spec/settings_spec.rb +52 -0
- data/spec/spec_helper.rb +1 -0
- metadata +153 -0
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
riaction (0.0.1)
|
5
|
+
activerecord
|
6
|
+
activesupport (~> 2.0)
|
7
|
+
faraday
|
8
|
+
faraday-stack
|
9
|
+
resque
|
10
|
+
|
11
|
+
GEM
|
12
|
+
remote: http://rubygems.org/
|
13
|
+
specs:
|
14
|
+
activerecord (2.3.14)
|
15
|
+
activesupport (= 2.3.14)
|
16
|
+
activesupport (2.3.14)
|
17
|
+
addressable (2.2.6)
|
18
|
+
diff-lcs (1.1.3)
|
19
|
+
faraday (0.7.5)
|
20
|
+
addressable (~> 2.2.6)
|
21
|
+
multipart-post (~> 1.1.3)
|
22
|
+
rack (>= 1.1.0, < 2)
|
23
|
+
faraday-stack (0.1.3)
|
24
|
+
faraday (~> 0.6)
|
25
|
+
multi_json (1.0.3)
|
26
|
+
multipart-post (1.1.3)
|
27
|
+
rack (1.3.5)
|
28
|
+
rack-protection (1.1.4)
|
29
|
+
rack
|
30
|
+
redis (2.2.2)
|
31
|
+
redis-namespace (1.0.3)
|
32
|
+
redis (< 3.0.0)
|
33
|
+
resque (1.19.0)
|
34
|
+
multi_json (~> 1.0)
|
35
|
+
redis-namespace (~> 1.0.2)
|
36
|
+
sinatra (>= 0.9.2)
|
37
|
+
vegas (~> 0.1.2)
|
38
|
+
rspec (2.7.0)
|
39
|
+
rspec-core (~> 2.7.0)
|
40
|
+
rspec-expectations (~> 2.7.0)
|
41
|
+
rspec-mocks (~> 2.7.0)
|
42
|
+
rspec-core (2.7.1)
|
43
|
+
rspec-expectations (2.7.0)
|
44
|
+
diff-lcs (~> 1.1.2)
|
45
|
+
rspec-mocks (2.7.0)
|
46
|
+
sinatra (1.3.1)
|
47
|
+
rack (~> 1.3, >= 1.3.4)
|
48
|
+
rack-protection (~> 1.1, >= 1.1.2)
|
49
|
+
tilt (~> 1.3, >= 1.3.3)
|
50
|
+
tilt (1.3.3)
|
51
|
+
vegas (0.1.8)
|
52
|
+
rack (>= 1.0.0)
|
53
|
+
|
54
|
+
PLATFORMS
|
55
|
+
ruby
|
56
|
+
|
57
|
+
DEPENDENCIES
|
58
|
+
riaction!
|
59
|
+
rspec (~> 2.6)
|
data/README
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
## Overview ##
|
2
|
+
|
3
|
+
riaction provides both a ruby wrapper for IActionable's restful API and an "acts-as" style interface for a Rails application's ActiveRecord models to associate them with IActionable profiles and have them drive the logging of game events.
|
4
|
+
|
5
|
+
## API Wrapper ##
|
6
|
+
|
7
|
+
The wrapper for IActionable's API is used internally by the rest of the gem, but also may be used directly if desired. IActionable's API is restful, and this wrapper takes each resource and HTTP verb of that API and wraps them as a method that takes arguments that match to the resource and query or body parameters. Before the wrapper can be instantiated or used it must be pre-initialized with your IActionable credentials and version number (IActionable supports older versions but recommends staying up to date):
|
8
|
+
|
9
|
+
IActionable::Api.init_settings( :app_key => "12345",
|
10
|
+
:api_key => "abcde",
|
11
|
+
:version => 3 )
|
12
|
+
@api = IActionable::Api.new
|
13
|
+
|
14
|
+
IActionable's API speaks in JSON, and here those responses are wrapped in simple objects where nesting and variable names are determined by [IActionable's documentation](http://www.http://iactionable.com/api/). For example, here the wrapper is making a call to load a profile summary:
|
15
|
+
|
16
|
+
profile_summary = @api.get_profile_summary("user", "username", "zortnac", 10)
|
17
|
+
profile_summary.display_name # => "Chris Eberz"
|
18
|
+
profile_summary.identifiers.first # => instance of IActionable::Objects::Identifier
|
19
|
+
profile_summary.identifiers.first.id_type # => "username"
|
20
|
+
profile_summary.identifiers.first.id # => "zortnac"
|
21
|
+
|
22
|
+
## Using riaction In Rails ##
|
23
|
+
|
24
|
+
While the API wrapper in riaction can be used directly (and I ought just pull it out as a separate gem), the rest of riaction consists of an "acts-as" style interface for your application's ActiveRecord models that leverages the API wrapper to associate your models with IActionable profiles and to have IActionable event logging be driven by your models' CRUD actions. riaction relies on Resque for tasking all of the requests made to IActionable's service.
|
25
|
+
|
26
|
+
### Initializing the API Wrapper ###
|
27
|
+
|
28
|
+
Just as above, before the wrapper can be used (either directly or by the riaction interface) it needs to be initialized with your IActionable credentials. This can be done in a small rails initializer:
|
29
|
+
|
30
|
+
I_ACTIONABLE_CREDS = (YAML.load_file("#{::Rails.root.to_s}/config/i_actionable.yml")[::Rails.env]).symbolize_keys!
|
31
|
+
IAction::Api.init_settings(I_ACTIONABLE_CREDS)
|
32
|
+
|
33
|
+
### Declaring A Model As A Profile ###
|
34
|
+
|
35
|
+
Models in your application may declare themselves as profiles that exist on IActionable.
|
36
|
+
|
37
|
+
class User < ActiveRecord::Base
|
38
|
+
riaction :profile, :type => :player, :username => :nickname, :custom => :id
|
39
|
+
end
|
40
|
+
|
41
|
+
# == Schema Information
|
42
|
+
#
|
43
|
+
# Table name: users
|
44
|
+
#
|
45
|
+
# id :integer(4)
|
46
|
+
# nickname :string(255)
|
47
|
+
|
48
|
+
Here, the class User declares itself as a profile of type "player", identifiable by two of IActionable's supported ID types, username and custom, the values of which are the fields (or any symbol that an instance of the class responds to) nickname and id, respectively. When a class declares itself as an riaction profile, an after_create callback will be added to register that model on IActionable as a profile as the type, and with the identifiers, described in the class.
|
49
|
+
|
50
|
+
#### Profile Instance Methods ####
|
51
|
+
|
52
|
+
Classes that declare themselves as IActionable profiles are given instance methods that tie in to the IActionable API, as many uses of the API take a profile as an argument.
|
53
|
+
|
54
|
+
@api.get_profile_summary("player", "username", "zortnac", 10)
|
55
|
+
# is equivalent to the following...
|
56
|
+
@user_instance.riaction_profile_summary(10)
|
57
|
+
|
58
|
+
@api.get_profile_challenges("player", "username", "zortnac", :completed)
|
59
|
+
# is equivalent to the following...
|
60
|
+
@user_instance.riaction_profile_challenges(:completed)
|
61
|
+
|
62
|
+
@api.add_profile_identifier("player", "username", "zortnac", "custom", 42)
|
63
|
+
# is equivalent to the following...
|
64
|
+
@user_instance.riaction_update_profile(:custom)
|
65
|
+
|
66
|
+
### Declaring Events ###
|
67
|
+
|
68
|
+
Models in your application may declare any number of events that they log through IActionable. For each event that is declared the important elements are:
|
69
|
+
|
70
|
+
1. The event's name (or key)
|
71
|
+
2. The type of trigger that causes the event to be logged
|
72
|
+
3. The profile under which the event is logged
|
73
|
+
4. Any optional parameters (key-value pairs) that you want to pass
|
74
|
+
|
75
|
+
` `
|
76
|
+
|
77
|
+
class Comment
|
78
|
+
belongs_to :user
|
79
|
+
belongs_to :post
|
80
|
+
|
81
|
+
riaction :event, :name => :make_a_comment, :trigger => :create, :profile => :user, :params => {:post => :post_id}
|
82
|
+
end
|
83
|
+
|
84
|
+
# == Schema Information
|
85
|
+
#
|
86
|
+
# Table name: comments
|
87
|
+
#
|
88
|
+
# id :integer(4)
|
89
|
+
# user_id :integer(4)
|
90
|
+
# post_id :integer(4)
|
91
|
+
|
92
|
+
Here, the name of the event is `make_a_comment`. The trigger for the event, in this case, is `:create`, which will add an after_create callback to log the event to the API.
|
93
|
+
|
94
|
+
_Note: If the trigger is one of :create, :update, or :destroy, then the appropriate ActiveRecord callback will log the event. If the trigger is anything else, then an instance method is provided to log the event by hand. For example, an argument of `:trigger => :foo` will provide an instance method `trigger_foo!`_
|
95
|
+
|
96
|
+
The profile that this event will be logged under can be any object whose class declares itself as a profile. Here, the profile is the object returned by the ActiveRecord association `:user`, and we assume is an instance of the User class from above. Lastly, the optional params passed along with the event is the key-value pair `{:post => :post_id}`, where `:post_id` is an ActiveRecord table column.
|
97
|
+
|
98
|
+
Putting this all together, whenever an instance of the Comment class is created, an event is logged for which the equivalent call to the API might look like this:
|
99
|
+
|
100
|
+
@api.log_event("player", "username", "zortnac", "make_a_comment", {:post => 33})
|
101
|
+
|
102
|
+
_Note: If a class both declares itself as a profile and declares one or more events, and wants to refer to itself as the profile for any of those events, use `:trigger => :self`_
|
103
|
+
|
104
|
+
## IActionable ##
|
105
|
+
|
106
|
+
[Visit their website!](http://www.iactionable.com)
|
107
|
+
|
108
|
+
## Author ##
|
109
|
+
|
110
|
+
Christopher Eberz; chris@chriseberz.com; @zortnac
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/lib/riaction.rb
ADDED
@@ -0,0 +1,209 @@
|
|
1
|
+
require 'riaction/iactionable/connection.rb'
|
2
|
+
require 'riaction/iactionable/settings.rb'
|
3
|
+
require 'riaction/iactionable/objects.rb'
|
4
|
+
|
5
|
+
module IActionable
|
6
|
+
|
7
|
+
class Api
|
8
|
+
attr :connection
|
9
|
+
@@settings = nil
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
if @@settings
|
13
|
+
@connection = IActionable::Connection.new(@@settings)
|
14
|
+
else
|
15
|
+
raise IActionable::ConfigError.new("IActionable::Api cannot be initialized without credentials being set in IActionable::Api.init_settings()")
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.init_settings(values)
|
20
|
+
@@settings = IActionable::Settings.new(values)
|
21
|
+
rescue IActionable::ConfigError => e
|
22
|
+
raise e
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.settings
|
26
|
+
@@settings
|
27
|
+
end
|
28
|
+
|
29
|
+
# =================
|
30
|
+
# = Event Logging =
|
31
|
+
# =================
|
32
|
+
|
33
|
+
def log_event(profile_type, id_type, id, event_key, event_attrs = {})
|
34
|
+
response = @connection.request.with_app_key.with_api_key.to("/#{profile_type}/#{id_type}/#{id}/events/#{event_key}").with_params(event_attrs).post
|
35
|
+
end
|
36
|
+
|
37
|
+
# =====================
|
38
|
+
# = Profile API calls =
|
39
|
+
# =====================
|
40
|
+
|
41
|
+
def get_profile_summary(profile_type, id_type, id, achievement_count = nil)
|
42
|
+
request = @connection.request.with_app_key.to("/#{profile_type}/#{id_type}/#{id}")
|
43
|
+
request.with_params(:achievement_count => achievement_count) unless achievement_count.blank?
|
44
|
+
response = request.get
|
45
|
+
IActionable::Objects::ProfileSummary.new(response)
|
46
|
+
end
|
47
|
+
|
48
|
+
def create_profile(profile_type, id_type, id, display_name = nil)
|
49
|
+
request = @connection.request.with_app_key.with_api_key.to("/#{profile_type}/#{id_type}/#{id}")
|
50
|
+
request.with_params(:display_name => display_name) unless display_name.blank?
|
51
|
+
request.post
|
52
|
+
end
|
53
|
+
alias_method :update_profile, :create_profile
|
54
|
+
|
55
|
+
def add_profile_identifier(profile_type, id_type, id, alt_id_type, alt_id)
|
56
|
+
@connection.request.with_app_key.with_api_key.to("/#{profile_type}/#{id_type}/#{id}/identifiers/#{alt_id_type}/#{alt_id}").post
|
57
|
+
end
|
58
|
+
|
59
|
+
# ====================
|
60
|
+
# = Points API calls =
|
61
|
+
# ====================
|
62
|
+
|
63
|
+
def get_profile_points(profile_type, id_type, id, point_type)
|
64
|
+
response = @connection.request.with_app_key.to("/#{profile_type}/#{id_type}/#{id}/points/#{point_type}").get
|
65
|
+
IActionable::Objects::ProfilePoints.new(response)
|
66
|
+
end
|
67
|
+
|
68
|
+
def update_profile_points(profile_type, id_type, id, point_type, amount, reason = nil)
|
69
|
+
request = @connection.request.with_app_key.with_api_key.to("/#{profile_type}/#{id_type}/#{id}/points/#{point_type}").with_params(:value => amount)
|
70
|
+
request.with_params(:description => reason) unless reason.blank?
|
71
|
+
response = request.post
|
72
|
+
IActionable::Objects::ProfilePoints.new(response)
|
73
|
+
end
|
74
|
+
|
75
|
+
# =========================
|
76
|
+
# = Achievement API calls =
|
77
|
+
# =========================
|
78
|
+
|
79
|
+
def get_profile_achievements(profile_type, id_type, id, filter_type = nil)
|
80
|
+
request = @connection.request.with_app_key
|
81
|
+
case filter_type
|
82
|
+
when :completed
|
83
|
+
request.to("/#{profile_type}/#{id_type}/#{id}/achievements/Completed")
|
84
|
+
response = request.get
|
85
|
+
response.map{|achievement_json| IActionable::Objects::Achievement.new(achievement_json)}
|
86
|
+
when :available
|
87
|
+
request.to("/#{profile_type}/#{id_type}/#{id}/achievements/Available")
|
88
|
+
response = request.get
|
89
|
+
response.map{|achievement_json| IActionable::Objects::Achievement.new(achievement_json)}
|
90
|
+
else
|
91
|
+
request.to("/#{profile_type}/#{id_type}/#{id}/achievements")
|
92
|
+
response = request.get
|
93
|
+
{
|
94
|
+
:available => response["Available"].map{|achievement_json| IActionable::Objects::Achievement.new(achievement_json)},
|
95
|
+
:completed => response["Completed"].map{|achievement_json| IActionable::Objects::Achievement.new(achievement_json)}
|
96
|
+
}
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def get_achievements()
|
101
|
+
response = @connection.request.with_app_key.to("/achievements").get
|
102
|
+
response.map{|achievement_json| IActionable::Objects::Achievement.new(achievement_json)}
|
103
|
+
rescue NoMethodError => e
|
104
|
+
[]
|
105
|
+
end
|
106
|
+
|
107
|
+
# ========================
|
108
|
+
# = Challenges API calls =
|
109
|
+
# ========================
|
110
|
+
|
111
|
+
def get_profile_challenges(profile_type, id_type, id, filter_type = nil)
|
112
|
+
request = @connection.request.with_app_key
|
113
|
+
case filter_type
|
114
|
+
when :completed
|
115
|
+
request.to("/#{profile_type}/#{id_type}/#{id}/challenges/Completed")
|
116
|
+
response = request.get
|
117
|
+
response.map{|challenge_json| IActionable::Objects::Challenge.new(challenge_json)}
|
118
|
+
when :available
|
119
|
+
request.to("/#{profile_type}/#{id_type}/#{id}/challenges/Available")
|
120
|
+
response = request.get
|
121
|
+
response.map{|challenge_json| IActionable::Objects::Challenge.new(challenge_json)}
|
122
|
+
else
|
123
|
+
request.to("/#{profile_type}/#{id_type}/#{id}/challenges")
|
124
|
+
response = request.get
|
125
|
+
{
|
126
|
+
:available => response["Available"].map{|challenge_json| IActionable::Objects::Challenge.new(challenge_json)},
|
127
|
+
:completed => response["Completed"].map{|challenge_json| IActionable::Objects::Challenge.new(challenge_json)}
|
128
|
+
}
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def get_challenges()
|
133
|
+
response = @connection.request.with_app_key.to("/challenges").get
|
134
|
+
response.map{|challenge_json| IActionable::Objects::Challenge.new(challenge_json)}
|
135
|
+
rescue NoMethodError => e
|
136
|
+
[]
|
137
|
+
end
|
138
|
+
|
139
|
+
# ===================
|
140
|
+
# = Goals API calls =
|
141
|
+
# ===================
|
142
|
+
|
143
|
+
def get_profile_goals(profile_type, id_type, id, filter_type = nil)
|
144
|
+
request = @connection.request.with_app_key
|
145
|
+
case filter_type
|
146
|
+
when :completed
|
147
|
+
request.to("/#{profile_type}/#{id_type}/#{id}/goals/Completed")
|
148
|
+
response = request.get
|
149
|
+
response.map{|goal_json| IActionable::Objects::Goal.new(goal_json)}
|
150
|
+
when :available
|
151
|
+
request.to("/#{profile_type}/#{id_type}/#{id}/goals/Available")
|
152
|
+
response = request.get
|
153
|
+
response.map{|goal_json| IActionable::Objects::Goal.new(goal_json)}
|
154
|
+
else
|
155
|
+
request.to("/#{profile_type}/#{id_type}/#{id}/goals")
|
156
|
+
response = request.get
|
157
|
+
{
|
158
|
+
:available => response["Available"].map{|goal_json| IActionable::Objects::Goal.new(goal_json)},
|
159
|
+
:completed => response["Completed"].map{|goal_json| IActionable::Objects::Goal.new(goal_json)}
|
160
|
+
}
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
def get_goals()
|
165
|
+
response = @connection.request.with_app_key.to("/goals").get
|
166
|
+
response.map{|goal_json| IActionable::Objects::Goal.new(goal_json)}
|
167
|
+
rescue NoMethodError => e
|
168
|
+
[]
|
169
|
+
end
|
170
|
+
|
171
|
+
# =========================
|
172
|
+
# = Leaderboard API calls =
|
173
|
+
# =========================
|
174
|
+
|
175
|
+
def get_leaderboard(profile_type, point_type, leaderboard, page_number=nil, page_count=nil, id=nil, id_type=nil)
|
176
|
+
request = @connection.request.with_app_key.to("/#{profile_type}/leaderboards/points/#{point_type}/#{leaderboard}")
|
177
|
+
request.with_params(:pageNumber => page_number) unless page_number.blank?
|
178
|
+
request.with_params(:pageCount => page_count) unless page_count.blank?
|
179
|
+
request.with_params(:id => id) unless id.blank? || id_type.blank?
|
180
|
+
request.with_params(:idType => id_type) unless id.blank? || id_type.blank?
|
181
|
+
response = request.get
|
182
|
+
IActionable::Objects::LeaderboardReport.new(response)
|
183
|
+
end
|
184
|
+
|
185
|
+
# ===================================
|
186
|
+
# = Profile Notifications API calls =
|
187
|
+
# ===================================
|
188
|
+
|
189
|
+
def get_profile_notifications(profile_type, id_type, id)
|
190
|
+
response = @connection.request.with_app_key.to("/#{profile_type}/#{id_type}/#{id}/notifications").get
|
191
|
+
{
|
192
|
+
:achievements => {
|
193
|
+
:available => response["Achievements"]["Available"].map{|a| IActionable::Objects::Achievement.new(a)},
|
194
|
+
:completed => response["Achievements"]["Completed"].map{|a| IActionable::Objects::Achievement.new(a)}
|
195
|
+
},
|
196
|
+
:challenges => {
|
197
|
+
:available => response["Challenges"]["Available"].map{|c| IActionable::Objects::Challenge.new(c)},
|
198
|
+
:completed => response["Challenges"]["Completed"].map{|c| IActionable::Objects::Challenge.new(c)}
|
199
|
+
},
|
200
|
+
:goals => {
|
201
|
+
:available => response["Goals"]["Available"].map{|g| IActionable::Objects::Goal.new(g)},
|
202
|
+
:completed => response["Goals"]["Completed"].map{|g| IActionable::Objects::Goal.new(g)}
|
203
|
+
},
|
204
|
+
:levels => response["Levels"].map{|l| IActionable::Objects::Level.new(l)},
|
205
|
+
:points => response["Points"].map{|p| IActionable::Objects::ProfilePoints.new(p)}
|
206
|
+
}
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
require 'faraday_stack'
|
3
|
+
require 'riaction/iactionable/error'
|
4
|
+
|
5
|
+
module IActionable
|
6
|
+
class Request < Struct.new(:path, :params, :headers, :body)
|
7
|
+
attr :settings
|
8
|
+
|
9
|
+
def initialize(settings)
|
10
|
+
@settings = settings
|
11
|
+
self.path = nil
|
12
|
+
self.params = {}
|
13
|
+
self.headers = {}
|
14
|
+
self.body = {}
|
15
|
+
end
|
16
|
+
|
17
|
+
def to(path)
|
18
|
+
self.path = path unless path.nil? || path.empty?
|
19
|
+
self
|
20
|
+
end
|
21
|
+
|
22
|
+
def with_api_key
|
23
|
+
(self.headers[:Authorization] = @settings.api_key) and self
|
24
|
+
end
|
25
|
+
|
26
|
+
def with_app_key
|
27
|
+
(self.params[:appKey] = @settings.app_key) and self
|
28
|
+
end
|
29
|
+
|
30
|
+
def with_params(params={})
|
31
|
+
self.params.merge!(params) and self
|
32
|
+
end
|
33
|
+
|
34
|
+
def with_body(body={})
|
35
|
+
self.body.merge!(body) and self
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class Connection
|
40
|
+
attr :connection
|
41
|
+
attr :settings
|
42
|
+
attr :request
|
43
|
+
attr_accessor :response
|
44
|
+
|
45
|
+
def initialize(settings)
|
46
|
+
@settings = settings
|
47
|
+
|
48
|
+
@connection = Faraday.new api_url(@settings.version) do |builder|
|
49
|
+
builder.use FaradayStack::ResponseJSON, content_type: 'application/json'
|
50
|
+
builder.use Faraday::Response::RaiseError
|
51
|
+
builder.use Faraday::Adapter::NetHttp
|
52
|
+
end
|
53
|
+
|
54
|
+
@request = nil
|
55
|
+
end
|
56
|
+
|
57
|
+
def request
|
58
|
+
(@request = Request.new(@settings)) and self
|
59
|
+
end
|
60
|
+
|
61
|
+
def get(path=nil, query_params={})
|
62
|
+
@request ||= Request.new(@settings)
|
63
|
+
@request.to(path).with_params(query_params)
|
64
|
+
@response = @connection.get do |req|
|
65
|
+
req.headers.merge! @request.headers
|
66
|
+
req.url @request.path, @request.params
|
67
|
+
end
|
68
|
+
@response.body
|
69
|
+
rescue Faraday::Error::ClientError => e
|
70
|
+
handle_client_error e
|
71
|
+
ensure
|
72
|
+
@request = nil
|
73
|
+
end
|
74
|
+
|
75
|
+
def post(path=nil, query_params={}, body_params={})
|
76
|
+
@request ||= Request.new(@settings)
|
77
|
+
@request.to(path).with_params(query_params)
|
78
|
+
@response = @connection.post do |req|
|
79
|
+
req.headers.merge! @request.headers
|
80
|
+
req.url @request.path, @request.params
|
81
|
+
req.body = @request.body unless @request.body.empty?
|
82
|
+
end
|
83
|
+
@response.body
|
84
|
+
rescue Exception => e
|
85
|
+
handle_client_error e
|
86
|
+
ensure
|
87
|
+
@request = nil
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
def method_missing(symbol, *args)
|
93
|
+
@request.send(symbol, *args) and self
|
94
|
+
rescue NoMethodError => e
|
95
|
+
raise e
|
96
|
+
end
|
97
|
+
|
98
|
+
def api_url(version)
|
99
|
+
"http://api.iactionable.com/v#{version}/"
|
100
|
+
end
|
101
|
+
|
102
|
+
def handle_client_error(e)
|
103
|
+
# http://iactionable.com/api/response-codes/
|
104
|
+
case e.response[:status]
|
105
|
+
when 400
|
106
|
+
raise IActionable::Error::BadRequest.new(e.response)
|
107
|
+
when 401
|
108
|
+
raise IActionable::Error::Unauthorized.new(e.response)
|
109
|
+
when 500
|
110
|
+
raise IActionable::Error::Internal.new(e.response)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|