rubeetup 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +11 -0
- data/.travis.yml +9 -0
- data/Gemfile +4 -0
- data/README.md +173 -0
- data/Rakefile +7 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/lib/rubeetup/errors.rb +32 -0
- data/lib/rubeetup/request.rb +133 -0
- data/lib/rubeetup/request_builder.rb +68 -0
- data/lib/rubeetup/request_response.rb +88 -0
- data/lib/rubeetup/request_sender.rb +93 -0
- data/lib/rubeetup/requester.rb +74 -0
- data/lib/rubeetup/requests_catalog.rb +79 -0
- data/lib/rubeetup/requests_lib/meetup_catalog.json +634 -0
- data/lib/rubeetup/requests_lib/meetup_catalog.rb +33 -0
- data/lib/rubeetup/response_wrapper.rb +29 -0
- data/lib/rubeetup/utilities.rb +51 -0
- data/lib/rubeetup/version.rb +6 -0
- data/lib/rubeetup.rb +140 -0
- data/rubeetup.gemspec +33 -0
- metadata +179 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 6e43ac5b43c6a21aa36bd9be908cafe94977ad7b
|
4
|
+
data.tar.gz: 63a251ccdb7a80f09835a247797d9f4f33af57b9
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f8528053c9beea9268370c27e5b4add52fff3f3094a0bdc9a06f3d3784532a638559f2341dcc4df2cf05788abfe117e7e9fbb274694244afd2eca12069aaaf16
|
7
|
+
data.tar.gz: 5bb0bb297c6a36bf1bd80727f373272b31cf78904c2e84ab841e6f1fab10a5fe18849ade6cef722035ce9d8ee159345ee66b2bea4e9c9717204ce67ce958413b
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,173 @@
|
|
1
|
+
<a href="https://travis-ci.org/mike-vascelli/rubeetup"><img src="https://travis-ci.org/mike-vascelli/rubeetup.svg?branch=master"/></a>
|
2
|
+
<a href="https://codeclimate.com/github/mike-vascelli/rubeetup"><img src="https://codeclimate.com/github/mike-vascelli/rubeetup/badges/gpa.svg" /></a>
|
3
|
+
<a href='https://gemnasium.com/mike-vascelli/rubeetup'><img src="https://gemnasium.com/mike-vascelli/rubeetup.svg" alt="Dependency Status" /></a>
|
4
|
+
<a href='https://coveralls.io/r/mike-vascelli/rubeetup'><img src='https://coveralls.io/repos/mike-vascelli/rubeetup/badge.svg' alt='Coverage Status' /></a>
|
5
|
+
<a href="https://inch-ci.org/github/mike-vascelli/rubeetup"><img src="http://inch-ci.org/github/mike-vascelli/rubeetup.svg?branch=master"/></a>
|
6
|
+
|
7
|
+
|
8
|
+
# Rubeetup
|
9
|
+
|
10
|
+
## Overview
|
11
|
+
|
12
|
+
This gem allows you to interact with the `Meetup.com` API in a simple and direct way. It gives you access to all of the available API methods (full `CRUD`), and it also makes sure that all the required parameters are provided with each request.
|
13
|
+
|
14
|
+
For more information on the Meetup API itself, visit: http://www.meetup.com/meetup_api
|
15
|
+
|
16
|
+
|
17
|
+
### Requests' Naming Convention
|
18
|
+
|
19
|
+
They begin with the action to perform on a resource: (i.e. `create`, `get`, `edit`, `delete`) and are followed by underscore, and then the name of the resource requested (as in the Meetup API).
|
20
|
+
#### Eg.
|
21
|
+
http://www.meetup.com/meetup_api/docs/2/open_events/
|
22
|
+
|
23
|
+
resource: open_events
|
24
|
+
|
25
|
+
action: get
|
26
|
+
|
27
|
+
request name: get_open_events
|
28
|
+
|
29
|
+
All the requests on Rubeetup follow this scheme.
|
30
|
+
|
31
|
+
|
32
|
+
## Supported Requests
|
33
|
+
|
34
|
+
get_open_events, get_concierge, get_events, create_event, get_event, edit_event, delete_event, get_event_comments, create_event_comment, get_event_comment, delete_event_comment, create_event_comment_flag, create_event_comment_subscribe, delete_event_comment_subscribe, create_event_comment_like, delete_event_comment_like, get_event_comment_likes, get_event_ratings, create_event_rating, create_attendance, get_attendance, create_event_payments, create_watchlist, delete_watchlist, get_boards, get_discussions, get_discussion_posts, get_categories, get_cities, get_dashboard, get_activity, get_groups, get_comments, create_group_photo, get_find_groups, get_group, edit_group, create_group_topics, delete_group_topics, get_recommended_groups, create_recommended_groups_ignores, get_similar_groups, get_members, get_member, edit_member, delete_member_photo, create_member_photo, get_status, get_notifications, create_notifications_read, get_oembed, delete_photo, get_photo_comments, create_photo_comment, get_photo_albums, get_photos, create_photo_album, create_photo, get_profiles, create_profile, edit_profile, get_profile, delete_profile, create_member_approvals, delete_member_approvals, get_rsvps, create_rsvp, edit_rsvp, get_rsvp, get_topic_categories, get_topics, get_recommended_group_topics, get_open_venues, get_venues, get_group_venues, get_recommended_venues, create_venue
|
35
|
+
|
36
|
+
### Authorization
|
37
|
+
Rubeetup will need a Meetup api key to successfully perform the requests. This can be obtained by signing-up at http://www.meetup.com, and by requesting a key for your account.
|
38
|
+
Note that in order to `edit`, `create`, or `delete` any resource in the API, you must first obtain the privileges to do those actions. This usually means becoming an organizer for a group. This can be achieved by either starting a meetup group, or by being granted the authorization by a current organizer of a group. If you want to experiment with the API or simply do some live testing, then you can request to become an organizer for the <tt>Meetup API Testing Sandbox Group</tt>.
|
39
|
+
|
40
|
+
Visit this link http://www.meetup.com/Meetup-API-Testing for more information.
|
41
|
+
|
42
|
+
## Usage
|
43
|
+
|
44
|
+
```ruby
|
45
|
+
auth = key: 'val'
|
46
|
+
|
47
|
+
# If you wish to provide auth data for each instance,
|
48
|
+
# then pass it to the Rubeetup.setup call as an argument:
|
49
|
+
#
|
50
|
+
requester = Rubeetup.setup(auth)
|
51
|
+
|
52
|
+
# Otherwise you can pass default auth data once and be done with it:
|
53
|
+
#
|
54
|
+
Rubeetup.default_auth(auth)
|
55
|
+
requester = Rubeetup.setup
|
56
|
+
|
57
|
+
|
58
|
+
# Each request will take a Hash of options.
|
59
|
+
# Check in rubeetup/requests_lib/meetup_catalog.rb, or in the
|
60
|
+
# MeetupCatalog documentation, or at: http://www.meetup.com/meetup_api
|
61
|
+
# for a list of required and available parameters for your specific request.
|
62
|
+
#
|
63
|
+
# Rubeetup will raise an Error if any request is attempted without
|
64
|
+
# providing all the required parameters.
|
65
|
+
#
|
66
|
+
events = requester.get_events(group_urlname: 'Meetup-API-Testing')
|
67
|
+
|
68
|
+
|
69
|
+
# Every request returns an array of results, and you can safely iterate over them.
|
70
|
+
# Furthermore, each result stored in the array is a symbol-keyed Hash.
|
71
|
+
#
|
72
|
+
events.each do |event|
|
73
|
+
# Each element is a Hash, so you can simply do:
|
74
|
+
puts event[:name]
|
75
|
+
|
76
|
+
# But Rubeetup also allows you to access elements' keys in OO-style:
|
77
|
+
puts event.name, event.time, event.duration, ...
|
78
|
+
|
79
|
+
venue = event.venue
|
80
|
+
puts venue[:address_1]
|
81
|
+
puts venue[:city]
|
82
|
+
end
|
83
|
+
|
84
|
+
pic = requester.create_member_photo(photo: 'spec/test_files/cat.png').first
|
85
|
+
puts pic.photo_url, pic.member_photo_id
|
86
|
+
|
87
|
+
event = requester.create_event(group_id: testing_group_id,
|
88
|
+
group_urlname: testing_group_urlname,
|
89
|
+
name: 'test').first
|
90
|
+
puts event.id
|
91
|
+
```
|
92
|
+
Take a look at the file `spec/lib/integration_spec.rb` to see examples for all the requests
|
93
|
+
|
94
|
+
To check each request's own required parameters:
|
95
|
+
Look in `lib/rubeetup/requests_lib/meetup_catalog.json`, or at: http://www.meetup.com/meetup_api
|
96
|
+
|
97
|
+
To find out about all the available response attributes visit the Meetup API documentation:
|
98
|
+
http://www.meetup.com/meetup_api
|
99
|
+
|
100
|
+
For example, for the `events` API request in the `Usage` example above, check this link:
|
101
|
+
http://www.meetup.com/meetup_api/docs/2/events
|
102
|
+
|
103
|
+
|
104
|
+
|
105
|
+
|
106
|
+
|
107
|
+
|
108
|
+
## Installation
|
109
|
+
|
110
|
+
Add this line to your application's Gemfile:
|
111
|
+
|
112
|
+
```ruby
|
113
|
+
gem 'rubeetup'
|
114
|
+
```
|
115
|
+
|
116
|
+
And then execute:
|
117
|
+
|
118
|
+
$ bundle
|
119
|
+
|
120
|
+
Or install it yourself as:
|
121
|
+
|
122
|
+
$ gem install rubeetup
|
123
|
+
|
124
|
+
## Ruby Interpreter Compatibility
|
125
|
+
|
126
|
+
Rubeetup has been tested on the following ruby interpreters:
|
127
|
+
|
128
|
+
- 2.2.1
|
129
|
+
- 2.2
|
130
|
+
- 2.1
|
131
|
+
- 2.0.0
|
132
|
+
- 1.9.3
|
133
|
+
- 1.9.2
|
134
|
+
|
135
|
+
## Runtime Dependencies
|
136
|
+
|
137
|
+
- multipart-post
|
138
|
+
|
139
|
+
## Development
|
140
|
+
|
141
|
+
After checking out the repo, run `bin/setup`, or `bundle install` to install dependencies. Then, run `rake` or `rspec` to run the tests. You can also run `bin/console` or `bundle console` for an interactive prompt that will allow you to experiment.
|
142
|
+
|
143
|
+
To install this gem onto your local machine, run `bundle exec rake install`.
|
144
|
+
|
145
|
+
## Contributing
|
146
|
+
|
147
|
+
Bug reports are more than welcome! Here's the link: https://github.com/mike-vascelli/rubeetup/issues
|
148
|
+
|
149
|
+
|
150
|
+
## License
|
151
|
+
|
152
|
+
The MIT License (MIT)
|
153
|
+
|
154
|
+
Copyright (c) 2015 Mike Vascelli
|
155
|
+
|
156
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
157
|
+
of this software and associated documentation files (the "Software"), to deal
|
158
|
+
in the Software without restriction, including without limitation the rights
|
159
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
160
|
+
copies of the Software, and to permit persons to whom the Software is
|
161
|
+
furnished to do so, subject to the following conditions:
|
162
|
+
|
163
|
+
The above copyright notice and this permission notice shall be included in
|
164
|
+
all copies or substantial portions of the Software.
|
165
|
+
|
166
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
167
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
168
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
169
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
170
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
171
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
172
|
+
THE SOFTWARE.
|
173
|
+
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "rubeetup"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
module Rubeetup
|
2
|
+
##
|
3
|
+
# Raised when the user attempts to create an instance of Requester with
|
4
|
+
# invalid auth data
|
5
|
+
#
|
6
|
+
InvalidAuthenticationError = Class.new(StandardError)
|
7
|
+
|
8
|
+
##
|
9
|
+
# Raised when the user attempts to create an instance of Request, but the
|
10
|
+
# request either does not exist, or it has one or more missing arguments
|
11
|
+
#
|
12
|
+
RequestError = Class.new(StandardError)
|
13
|
+
|
14
|
+
##
|
15
|
+
# Custom Error to wrap Rubeetup::RequestResponse objects in case of error
|
16
|
+
# while performing a request
|
17
|
+
#
|
18
|
+
class MeetupResponseError < StandardError
|
19
|
+
|
20
|
+
##
|
21
|
+
# @return [Rubeetup::RequestResponse] the failed request's response
|
22
|
+
#
|
23
|
+
attr_reader :response
|
24
|
+
|
25
|
+
##
|
26
|
+
# @param [Rubeetup::RequestResponse] response the failed request's response
|
27
|
+
#
|
28
|
+
def initialize(response)
|
29
|
+
@response = response
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
module Rubeetup
|
4
|
+
##
|
5
|
+
# Represents API requests. Provides for their own validation and execution
|
6
|
+
#
|
7
|
+
class Request
|
8
|
+
include Rubeetup::RequestsCatalog
|
9
|
+
include Rubeetup::Utilities
|
10
|
+
|
11
|
+
##
|
12
|
+
# @return [Symbol] name the name of the request as input by the user
|
13
|
+
#
|
14
|
+
attr_reader :name
|
15
|
+
|
16
|
+
##
|
17
|
+
# @return [Symbol] http_verb the http_verb for the request
|
18
|
+
#
|
19
|
+
attr_reader :http_verb
|
20
|
+
|
21
|
+
##
|
22
|
+
# @return [String] method_path the path of the Meetup API resource
|
23
|
+
#
|
24
|
+
attr_reader :method_path
|
25
|
+
|
26
|
+
##
|
27
|
+
# @return [Hash{Symbol=>String}] options holds the request's options
|
28
|
+
#
|
29
|
+
attr_reader :options
|
30
|
+
|
31
|
+
##
|
32
|
+
# @return [Rubeetup::Sender] sender the request's chosen sender
|
33
|
+
#
|
34
|
+
attr_reader :sender
|
35
|
+
|
36
|
+
##
|
37
|
+
# @return [Lambda] multipart if present it contains the multipart POST logic
|
38
|
+
#
|
39
|
+
attr_reader :multipart
|
40
|
+
|
41
|
+
##
|
42
|
+
# @param [Hash{Symbol=>String}] args holds the request's data
|
43
|
+
# @option args [Symbol] :name the full request's name
|
44
|
+
# @option args [Hash{Symbol=>String}] :options holds the request's options
|
45
|
+
# @option args [Symbol] :http_verb the request's http_verb
|
46
|
+
#
|
47
|
+
def initialize(args = {})
|
48
|
+
@name = args[:name]
|
49
|
+
@options = args[:options]
|
50
|
+
validate_request
|
51
|
+
@http_verb = args[:http_verb]
|
52
|
+
@method_path = request_path.call(@options)
|
53
|
+
@multipart = request_multipart
|
54
|
+
@sender = Rubeetup::RequestSender.new
|
55
|
+
end
|
56
|
+
|
57
|
+
##
|
58
|
+
# Completes this request
|
59
|
+
# @return [Array<Rubeetup::ResponseWrapper>] the request's response
|
60
|
+
#
|
61
|
+
def execute
|
62
|
+
sender.get_response(self)
|
63
|
+
end
|
64
|
+
|
65
|
+
##
|
66
|
+
# For debugging purposes
|
67
|
+
#
|
68
|
+
def to_s
|
69
|
+
<<-DOC.gsub(/^ {8}/, '')
|
70
|
+
\nREQUEST
|
71
|
+
name => #{name}
|
72
|
+
verb => #{http_verb}
|
73
|
+
path => #{method_path}
|
74
|
+
options => #{options.inspect}\n
|
75
|
+
DOC
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
def validate_request
|
81
|
+
verify_existence
|
82
|
+
validate_options
|
83
|
+
end
|
84
|
+
|
85
|
+
def verify_existence
|
86
|
+
fail error_class, existence_message unless is_in_catalog?
|
87
|
+
end
|
88
|
+
|
89
|
+
def validate_options
|
90
|
+
fail error_class, options_message unless has_all_required_options?
|
91
|
+
end
|
92
|
+
|
93
|
+
##
|
94
|
+
# Uses sets to make sure that there is at least a set of required parameters
|
95
|
+
# which is a subset of the arguments passed to the request
|
96
|
+
# @return [Boolean] whether all the required arguments have been passed
|
97
|
+
#
|
98
|
+
def has_all_required_options?
|
99
|
+
required_keys_sets = required_options.map do |elem|
|
100
|
+
elem.respond_to?(:to_set) ? elem.to_set : Set[elem]
|
101
|
+
end
|
102
|
+
return true if required_keys_sets.empty?
|
103
|
+
option_keys_set = options.keys.to_set
|
104
|
+
required_keys_sets.any? { |set| set.subset? option_keys_set }
|
105
|
+
end
|
106
|
+
|
107
|
+
def existence_message
|
108
|
+
<<-DOC.gsub(/^ {8}/, '')
|
109
|
+
The provided request => '#{name}' is an invalid request.
|
110
|
+
This request does not exist in the catalog of supported requests.
|
111
|
+
Please consult rubeetup/requests_lib/meetup_catalog.rb, or the provided
|
112
|
+
documentation for the complete list of requests.
|
113
|
+
DOC
|
114
|
+
end
|
115
|
+
|
116
|
+
def options_message
|
117
|
+
<<-DOC.gsub(/^ {8}/, '')
|
118
|
+
This request cannot be completed as is.
|
119
|
+
The provided arguments => '#{options.inspect}' miss one or more
|
120
|
+
required parameters.
|
121
|
+
The request '#{name}' requires the following parameters:
|
122
|
+
'#{required_options.inspect}'
|
123
|
+
Please consult rubeetup/requests_lib/meetup_catalog.rb, or the provided
|
124
|
+
documentation for the complete list of requests, and their respective
|
125
|
+
required parameters.
|
126
|
+
DOC
|
127
|
+
end
|
128
|
+
|
129
|
+
def error_class
|
130
|
+
Rubeetup::RequestError
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module Rubeetup
|
2
|
+
##
|
3
|
+
# Allows a Requester object to compose a Request instance with the passed data
|
4
|
+
#
|
5
|
+
module RequestBuilder
|
6
|
+
class << self
|
7
|
+
##
|
8
|
+
# Creates an instance of the chosen request type
|
9
|
+
# @note only preliminary error checking on the request is performed here.
|
10
|
+
# @param [Symbol] name the request's name
|
11
|
+
# @param [Hash{Symbol=>String}] args holds the request's options
|
12
|
+
# @return [Rubeetup::Request] the newly created Request instance
|
13
|
+
#
|
14
|
+
def compose_request(name, args)
|
15
|
+
verb = get_verb(name)
|
16
|
+
validate_verb(verb)
|
17
|
+
request_type.new(
|
18
|
+
name: name,
|
19
|
+
http_verb: infer_http_verb(verb),
|
20
|
+
options: args.first
|
21
|
+
)
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def request_type
|
27
|
+
Rubeetup::Request
|
28
|
+
end
|
29
|
+
|
30
|
+
def verbs
|
31
|
+
{ create: :post, get: :get, edit: :post, delete: :delete }
|
32
|
+
end
|
33
|
+
|
34
|
+
##
|
35
|
+
# Parses the request name, and attempts to split it around the leftmost
|
36
|
+
# underscore char. If the name is properly formed, then the left half
|
37
|
+
# is the verb of the request
|
38
|
+
# @param [Symbol] name the request name
|
39
|
+
# @return [Symbol] if name contains the required underscore char
|
40
|
+
# @return [nil] if name is invalid
|
41
|
+
#
|
42
|
+
def get_verb(name)
|
43
|
+
pos = (name =~ /_/)
|
44
|
+
pos ? name[0...pos].to_sym : nil
|
45
|
+
end
|
46
|
+
|
47
|
+
def validate_verb(verb)
|
48
|
+
valid_verbs = verbs.keys
|
49
|
+
fail Rubeetup::RequestError, error_message(verb, valid_verbs) unless
|
50
|
+
valid_verbs.include? verb
|
51
|
+
end
|
52
|
+
|
53
|
+
def error_message(verb, valid_verbs)
|
54
|
+
<<-DOC.gsub(/^ {10}/, '')
|
55
|
+
'#{verb}' is an invalid method.
|
56
|
+
The only available requests must begin with any of:
|
57
|
+
#{valid_verbs.join(', ')}
|
58
|
+
Followed by underscore, and a Meetup request name.
|
59
|
+
Consult the documentation for a complete list of available requests.
|
60
|
+
DOC
|
61
|
+
end
|
62
|
+
|
63
|
+
def infer_http_verb(verb)
|
64
|
+
verbs[verb]
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module Rubeetup
|
4
|
+
##
|
5
|
+
# Packages a response, and provides for its validation
|
6
|
+
#
|
7
|
+
class RequestResponse
|
8
|
+
include Rubeetup::Utilities
|
9
|
+
|
10
|
+
##
|
11
|
+
# @return [Net::HTTPResponse] response the raw response from the sender
|
12
|
+
#
|
13
|
+
attr_reader :response
|
14
|
+
|
15
|
+
##
|
16
|
+
# @return [Hash{Symbol=>...}] parsed_body the JSON-parsed body of the response.
|
17
|
+
# It is a Hash with all the keys as Symbols
|
18
|
+
#
|
19
|
+
attr_reader :parsed_body
|
20
|
+
|
21
|
+
##
|
22
|
+
# @return [Rubeetup::Request] the request which caused this response
|
23
|
+
#
|
24
|
+
attr_reader :request
|
25
|
+
|
26
|
+
##
|
27
|
+
# @param [Net::HTTPResponse] raw_data the raw response from the sender
|
28
|
+
#
|
29
|
+
def initialize(sender)
|
30
|
+
@response = sender.response_data
|
31
|
+
@request = sender.request
|
32
|
+
body = @response.body
|
33
|
+
@parsed_body = blank?(body) ? [] : parse(body)
|
34
|
+
end
|
35
|
+
|
36
|
+
##
|
37
|
+
# If the request was successful, then creates a collection of
|
38
|
+
# Rubeetup::ResponseWrapper instances.
|
39
|
+
# @return [Array<Rubeetup::ResponseWrapper>] a collection containing the response's data
|
40
|
+
#
|
41
|
+
def data
|
42
|
+
fail error_class.new(self), error_message unless success?
|
43
|
+
collection = collectionize(parsed_body)
|
44
|
+
collection.map {|elem| wrapper_class.new(elem)}
|
45
|
+
end
|
46
|
+
|
47
|
+
# Consider implementing pagination
|
48
|
+
#def prev; end
|
49
|
+
#def next; end
|
50
|
+
#def all; end # Calls #next until no more and returns all the results at once
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def success?
|
55
|
+
response.is_a? Net::HTTPSuccess
|
56
|
+
end
|
57
|
+
|
58
|
+
def error_class
|
59
|
+
Rubeetup::MeetupResponseError
|
60
|
+
end
|
61
|
+
|
62
|
+
def wrapper_class
|
63
|
+
Rubeetup::ResponseWrapper
|
64
|
+
end
|
65
|
+
|
66
|
+
def collectionize(data)
|
67
|
+
return data if data.is_a? Array
|
68
|
+
data[:results] || [data]
|
69
|
+
end
|
70
|
+
|
71
|
+
def parse(body)
|
72
|
+
begin
|
73
|
+
JSON.parse(body, symbolize_names: true)
|
74
|
+
rescue
|
75
|
+
body
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def error_message
|
80
|
+
<<-DOC.gsub(/^ {8}/, '')
|
81
|
+
An error was encountered while processing the following request:
|
82
|
+
#{request}
|
83
|
+
Here's some information to describe the error, and/or its causes:
|
84
|
+
#{parsed_body}
|
85
|
+
DOC
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'net/http/post/multipart'
|
2
|
+
|
3
|
+
module Rubeetup
|
4
|
+
##
|
5
|
+
# Responsible for sending responses over an http connection
|
6
|
+
#
|
7
|
+
class RequestSender
|
8
|
+
include Rubeetup::Utilities
|
9
|
+
|
10
|
+
##
|
11
|
+
# Destination host
|
12
|
+
#
|
13
|
+
HOST = 'api.meetup.com'
|
14
|
+
|
15
|
+
##
|
16
|
+
# @return [Net::HTTP] this Sender's http connection
|
17
|
+
#
|
18
|
+
attr_reader :http
|
19
|
+
|
20
|
+
##
|
21
|
+
# @return [Rubeetup::Request] this Sender's request job
|
22
|
+
#
|
23
|
+
attr_reader :request
|
24
|
+
|
25
|
+
##
|
26
|
+
# @return [Net::HTTPResponse] the response data obtained from the request
|
27
|
+
#
|
28
|
+
attr_reader :response_data
|
29
|
+
|
30
|
+
def initialize
|
31
|
+
@http = Net::HTTP.new(HOST)
|
32
|
+
end
|
33
|
+
|
34
|
+
##
|
35
|
+
# Performs a request and returns back the response
|
36
|
+
# @param [Rubeetup::Request] request the request instance to be sent
|
37
|
+
# @return [Array<Rubeetup::ResponseWrapper>] the request response
|
38
|
+
#
|
39
|
+
def get_response(request)
|
40
|
+
@request = request
|
41
|
+
@response_data = fetch
|
42
|
+
response_class.new(self).data
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def fetch
|
48
|
+
request.http_verb == :post ? (req = post) : (req = get_or_delete)
|
49
|
+
http.request(req)
|
50
|
+
end
|
51
|
+
|
52
|
+
def get_or_delete
|
53
|
+
path = "#{request.method_path}?#{stringify(request.options)}"
|
54
|
+
http_method_class.new(path)
|
55
|
+
end
|
56
|
+
|
57
|
+
def post
|
58
|
+
request.multipart ? multipart_post : url_encoded_post
|
59
|
+
end
|
60
|
+
|
61
|
+
def url_encoded_post
|
62
|
+
req = http_method_class.new(request.method_path)
|
63
|
+
req.set_form_data(request.options)
|
64
|
+
req
|
65
|
+
end
|
66
|
+
|
67
|
+
def multipart_post
|
68
|
+
encode_resources
|
69
|
+
http_method_class.new(request.method_path, request.options)
|
70
|
+
end
|
71
|
+
|
72
|
+
def encode_resources
|
73
|
+
request.multipart.call(request.options)
|
74
|
+
end
|
75
|
+
|
76
|
+
def http_method_class
|
77
|
+
class_name = request.http_verb.capitalize.to_s
|
78
|
+
if request.multipart
|
79
|
+
Net::HTTP::Post::Multipart
|
80
|
+
else
|
81
|
+
Net::HTTP.const_get(class_name.to_sym)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def response_class
|
86
|
+
Rubeetup::RequestResponse
|
87
|
+
end
|
88
|
+
|
89
|
+
def handler_class
|
90
|
+
Typhoeus::Request
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|