rubeetup 0.0.1
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.
- 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
|