rubeetup 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ /.idea/
11
+ /.coveralls.yml
data/.travis.yml ADDED
@@ -0,0 +1,9 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.1
4
+ - 2.2
5
+ - 2.1
6
+ - 2.0.0
7
+ - 1.9.3
8
+ - 1.9.2
9
+ before_install: gem install bundler -v 1.10.4
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in rubeetup.gemspec
4
+ gemspec
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
@@ -0,0 +1,7 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new
5
+
6
+ task default: :spec
7
+ task test: :spec
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,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -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