rMeetup 1.0 → 2.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.md +21 -0
  3. data/README.md +65 -0
  4. data/Rakefile +6 -12
  5. data/lib/core_ext/string.rb +11 -0
  6. data/lib/rmeetup.rb +2 -63
  7. data/lib/rmeetup/client.rb +77 -0
  8. data/lib/rmeetup/errors.rb +15 -0
  9. data/lib/rmeetup/fetcher.rb +20 -33
  10. data/lib/rmeetup/fetcher/base.rb +66 -55
  11. data/lib/rmeetup/fetcher/open_events.rb +14 -0
  12. data/lib/rmeetup/fetcher/venues.rb +14 -0
  13. data/lib/rmeetup/poster.rb +17 -0
  14. data/lib/rmeetup/poster/base.rb +81 -0
  15. data/lib/rmeetup/{fetcher/comments.rb → poster/event_comment.rb} +5 -5
  16. data/lib/rmeetup/type.rb +2 -1
  17. data/lib/rmeetup/type/event.rb +21 -18
  18. data/lib/rmeetup/type/event_comment.rb +42 -0
  19. data/lib/rmeetup/type/rsvp.rb +9 -5
  20. data/lib/rmeetup/type/venue.rb +63 -0
  21. data/lib/rmeetup/version.rb +14 -0
  22. data/rmeetup.gemspec +34 -0
  23. data/spec/client_spec.rb +77 -29
  24. data/spec/fetcher_spec.rb +4 -4
  25. data/spec/fixtures/vcr_cassettes/fetching_cities.yml +358 -0
  26. data/spec/fixtures/vcr_cassettes/fetching_events.yml +54 -0
  27. data/spec/fixtures/vcr_cassettes/fetching_groups.yml +8627 -0
  28. data/spec/fixtures/vcr_cassettes/fetching_members.yml +3984 -0
  29. data/spec/fixtures/vcr_cassettes/fetching_photos.yml +54 -0
  30. data/spec/fixtures/vcr_cassettes/fetching_rsvps.yml +54 -0
  31. data/spec/spec_helper.rb +11 -69
  32. metadata +140 -101
  33. data/Manifest +0 -45
  34. data/README.rdoc +0 -34
  35. data/lib/rmeetup/fetcher/topics.rb +0 -14
  36. data/rMeetup.gemspec +0 -29
  37. data/spec/fetchers/base_spec.rb +0 -58
  38. data/spec/fetchers/cities_spec.rb +0 -18
  39. data/spec/fetchers/comments_spec.rb +0 -14
  40. data/spec/fetchers/events_spec.rb +0 -14
  41. data/spec/fetchers/groups_spec.rb +0 -14
  42. data/spec/fetchers/members_spec.rb +0 -14
  43. data/spec/fetchers/photos_spec.rb +0 -14
  44. data/spec/fetchers/rsvps_spec.rb +0 -14
  45. data/spec/fetchers/topics_spec.rb +0 -14
  46. data/spec/responses/cities.json +0 -1
  47. data/spec/responses/comments.json +0 -1
  48. data/spec/responses/error.json +0 -1
  49. data/spec/responses/events.json +0 -1
  50. data/spec/responses/groups.json +0 -1
  51. data/spec/responses/members.json +0 -1
  52. data/spec/responses/photos.json +0 -1
  53. data/spec/responses/rsvps.json +0 -1
  54. data/spec/responses/topics.json +0 -1
@@ -0,0 +1,14 @@
1
+ module RMeetup
2
+ module Fetcher
3
+ class OpenEvents < Base
4
+ def initialize
5
+ @type = :open_events
6
+ end
7
+
8
+ # Turn the result hash into a Event Class
9
+ def format_result(result)
10
+ RMeetup::Type::Event.new(result)
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ module RMeetup
2
+ module Fetcher
3
+ class Venues < Base
4
+ def initialize
5
+ @type = :venues
6
+ end
7
+
8
+ # Turn the result hash into a Venue Class
9
+ def format_result(result)
10
+ RMeetup::Type::Venue.new(result)
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,17 @@
1
+ require "rmeetup/poster/base"
2
+ require "rmeetup/poster/event_comment"
3
+
4
+ module RMeetup
5
+ module Poster
6
+
7
+ def self.for(type)
8
+ name = type.to_s.camel_case.to_sym
9
+ if (name && constants.include?(name))
10
+ const_get(name).new
11
+ else
12
+ raise InvalidRequestTypeError.new(type)
13
+ end
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,81 @@
1
+ require 'net/http'
2
+ require 'net/https'
3
+
4
+ module RMeetup
5
+ module Poster
6
+ class ApiError < StandardError
7
+ def initialize(error_message, request_url)
8
+ super "Meetup API Error: #{error_message} - API URL: #{request_url}"
9
+ end
10
+ end
11
+
12
+ class NoResponseError < StandardError
13
+ def initialize
14
+ super "No Response was returned from the Meetup API."
15
+ end
16
+ end
17
+
18
+ # == RMeetup::Fetcher::Base
19
+ #
20
+ # Base fetcher class that other fetchers
21
+ # will inherit from.
22
+ class Base
23
+ def initialize
24
+ @type = nil
25
+ end
26
+
27
+ # Fetch and parse a response
28
+ # based on a set of options.
29
+ # Override this method to ensure
30
+ # neccessary options are passed
31
+ # for the request.
32
+ def post(options = {})
33
+ url = build_url(options)
34
+ ret = post_response(url, options)
35
+ ret
36
+ end
37
+
38
+ protected
39
+ # OVERRIDE this method to format a result section
40
+ # as per Result type.
41
+ # Takes a result in a collection and
42
+ # formats it to be put back into the collection.
43
+ def format_result(result)
44
+ result
45
+ end
46
+
47
+ def build_url(options)
48
+ base_url
49
+ end
50
+
51
+ def base_url
52
+ "https://api.meetup.com/2/#{@type}/"
53
+ end
54
+
55
+ # Create a query string from an options hash
56
+ def params_for(options)
57
+ params = []
58
+ options.each do |key, value|
59
+ params << "#{key}=#{value}"
60
+ end
61
+ "?#{params.join("&")}"
62
+ end
63
+
64
+ # Encode a hash of options to be used as request parameters
65
+ def encode_options(options)
66
+ options.each do |key,value|
67
+ options[key] = URI.encode(value.to_s)
68
+ end
69
+ end
70
+
71
+ def post_response(url, options)
72
+ sslurl = URI.parse(url)
73
+ https = Net::HTTP.new(sslurl.host, sslurl.port)
74
+ https.use_ssl = true
75
+ req = Net::HTTP::Post.new(sslurl.path)
76
+ req.set_form_data(options)
77
+ resp = https.request(req)
78
+ end
79
+ end
80
+ end
81
+ end
@@ -1,14 +1,14 @@
1
1
  module RMeetup
2
- module Fetcher
3
- class Comments < Base
2
+ module Poster
3
+ class EventComment < Base
4
4
  def initialize
5
- @type = :comments
5
+ @type = :event_comment
6
6
  end
7
7
 
8
8
  # Turn the result hash into a Comment Class
9
9
  def format_result(result)
10
- RMeetup::Type::Comment.new(result)
10
+ RMeetup::Type::EventComment.new(result)
11
11
  end
12
12
  end
13
13
  end
14
- end
14
+ end
@@ -5,4 +5,5 @@ require "rmeetup/type/member"
5
5
  require "rmeetup/type/rsvp"
6
6
  require "rmeetup/type/event"
7
7
  require "rmeetup/type/comment"
8
- require "rmeetup/type/photo"
8
+ require "rmeetup/type/photo"
9
+ require "rmeetup/type/venue"
@@ -1,47 +1,50 @@
1
1
  module RMeetup
2
2
  module Type
3
-
3
+
4
4
  # == RMeetup::Type::Event
5
5
  #
6
6
  # Data wraper for a Event fethcing response
7
7
  # Used to access result attributes as well
8
8
  # as progammatically fetch relative data types
9
9
  # based on this event.
10
-
11
- # Edited by Jason Berlinsky on 1/20/11 to allow for arbitrary data access
12
- # See http://www.meetup.com/meetup_api/docs/events/ for available fields
13
-
10
+
14
11
  class Event
15
-
16
- attr_accessor :event
17
-
18
12
  def initialize(event = {})
19
- self.event = event
13
+ @event = event
20
14
  end
21
-
15
+ attr_reader :event
16
+ alias_method :to_h, :event
17
+
22
18
  def method_missing(id, *args)
23
- return self.event[id.id2name]
19
+ if event.has_key?(id.id2name)
20
+ event[id.id2name]
21
+ else
22
+ fail NoMethodError.new('no method')
23
+ end
24
24
  end
25
-
25
+
26
26
  # Special accessors that need typecasting or other parsing
27
27
  def id
28
- self.event['id'].to_i
28
+ event['id'].to_i
29
29
  end
30
30
  def lat
31
- self.event['lat'].to_f
31
+ event['lat'].to_f
32
32
  end
33
33
  def lon
34
- self.event['lon'].to_f
34
+ event['lon'].to_f
35
35
  end
36
36
  def rsvpcount
37
- self.event['rsvpcount'].to_i
37
+ event['rsvpcount'].to_i
38
38
  end
39
39
  def updated
40
40
  DateTime.parse(self.event['updated'])
41
41
  end
42
42
  def time
43
- DateTime.parse(self.event['time'])
43
+ Time.at(self.event['time']/1000).to_datetime
44
+ end
45
+ def venue
46
+ OpenStruct.new( self.event['venue'] )
44
47
  end
45
48
  end
46
49
  end
47
- end
50
+ end
@@ -0,0 +1,42 @@
1
+ module RMeetup
2
+ module Type
3
+
4
+ # == RMeetup::Type::Comment
5
+ #
6
+ # Data wraper for a Comment fethcing response
7
+ # Used to access result attributes as well
8
+ # as progammatically fetch relative data types
9
+ # based on this comment.
10
+
11
+ # Edited by Jason Berlinsky on 1/20/11 to allow for arbitrary data access
12
+ # See http://www.meetup.com/meetup_api/docs/ew/comment/ for available fields
13
+
14
+ class EventComment
15
+
16
+ attr_accessor :comment
17
+
18
+ def initialize(comment = {})
19
+ self.comment = comment
20
+ end
21
+
22
+ def method_missing(id, *args)
23
+ return self.comment[id.id2name]
24
+ end
25
+
26
+ # Special accessors that need typecasting or other parsing
27
+
28
+ def rating
29
+ return self.comment['rating'].to_i
30
+ end
31
+ def created
32
+ return DateTime.parse(self.comment['created'])
33
+ end
34
+ def lat
35
+ return self.comment['lat'].to_f
36
+ end
37
+ def lon
38
+ return self.comment['lon'].to_f
39
+ end
40
+ end
41
+ end
42
+ end
@@ -12,14 +12,18 @@ module RMeetup
12
12
  # See http://www.meetup.com/meetup_api/docs/ew/rsvps/ for available fields
13
13
 
14
14
  class Rsvp
15
- attr_accessor :rsvp
16
-
17
15
  def initialize(rsvp = {})
18
- self.rsvp = rsvp
16
+ @rsvp = rsvp
19
17
  end
18
+ attr_accessor :rsvp
19
+ alias_method :to_h, :rsvp
20
20
 
21
21
  def method_missing(id, *args)
22
- return self.rsvp[id.id2name]
22
+ if rsvp.has_key?(id.id2name)
23
+ rsvp[id.id2name]
24
+ else
25
+ fail NoMethodError.new('no method')
26
+ end
23
27
  end
24
28
 
25
29
  # Special accessors that need typecasting or other parsing
@@ -32,4 +36,4 @@ module RMeetup
32
36
  end
33
37
  end
34
38
  end
35
- end
39
+ end
@@ -0,0 +1,63 @@
1
+ module RMeetup
2
+ module Type
3
+
4
+ # == RMeetup::Type::City
5
+ #
6
+ # Data wraper for a City fethcing response
7
+ # Used to access result attributes as well
8
+ # as progammatically fetch relative data types
9
+ # based on this city.
10
+
11
+ # Edited by Jason Berlinsky on 1/20/11 to allow for arbitrary data access
12
+ # See http://www.meetup.com/meetup_api/docs/cities/ for available fields
13
+
14
+ class Venue
15
+
16
+ attr_accessor :venue
17
+
18
+ def initialize(venue = {})
19
+ self.venue = venue
20
+ end
21
+
22
+ def method_missing(id, *args)
23
+ return self.venue[id.id2name]
24
+ end
25
+
26
+ # Special accessors that need typecasting or other parsing
27
+
28
+ def id
29
+ return self.venue['id'].to_i
30
+ end
31
+ def zip
32
+ return self.venue['zip'].to_s
33
+ end
34
+ def state
35
+ return self.venue['state'].to_s
36
+ end
37
+ def address_1
38
+ return self.venue['address_1'].to_s
39
+ end
40
+ def city
41
+ return self.venue['city'].to_s
42
+ end
43
+ def country
44
+ return self.venue['country'].to_s
45
+ end
46
+ def distance
47
+ return self.venue['distance'].to_i
48
+ end
49
+ def rating_count
50
+ return self.venue['rating_count'].to_i
51
+ end
52
+ def rating
53
+ return self.venue['rating'].to_s
54
+ end
55
+ def lat
56
+ return self.venue['lat'].to_f
57
+ end
58
+ def lon
59
+ return self.venue['lon'].to_f
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,14 @@
1
+ module RMeetup
2
+ class Version
3
+ MAJOR = 2
4
+ MINOR = 0
5
+ PATCH = 1
6
+ PRE = nil
7
+
8
+ class << self
9
+ def to_s
10
+ [MAJOR, MINOR, PATCH, PRE].compact.join('.')
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,34 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib/', __FILE__)
3
+ $:.unshift lib unless $:.include?(lib)
4
+ require 'rmeetup/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.platform = Gem::Platform::RUBY
8
+
9
+ spec.name = 'rMeetup'
10
+ spec.description = 'meetup.com ruby client lib'
11
+ spec.authors = ['Jared Pace', 'Jason Berlinsky', 'Tommy Chan', 'Tanner Mares', 'Zishan Ahmad', 'Nikica Jokić']
12
+ spec.email = ['jdpace@github.com', 'jason@jasonberlinsky.com', 'tommytcchan@gmail.com', 'tannermares@gmail.com', 'me@zishanahmad.com', 'neektza@gmail.com']
13
+ spec.homepage = 'https://github.com/neektza/rmeetup'
14
+ spec.summary = 'A Ruby wrapper for the Meetup REST API v2'
15
+
16
+ spec.required_rubygems_version = '>= 1.3.6'
17
+
18
+ spec.add_dependency 'json', '~> 1.8'
19
+ spec.add_dependency 'buftok', '~> 0.2'
20
+ spec.add_dependency 'http', '~> 0.6'
21
+ spec.add_dependency 'http_parser.rb', '~> 0.6'
22
+
23
+ spec.add_development_dependency 'bundler', '~> 1.6'
24
+
25
+ spec.files = %w[LICENSE.md README.md Rakefile rmeetup.gemspec]
26
+ spec.licenses = %w[MIT]
27
+ spec.files += Dir.glob('lib/**/*.rb')
28
+ spec.files += Dir.glob('spec/**/*')
29
+
30
+ spec.test_files = Dir.glob('spec/**/*')
31
+ spec.require_paths = %w[lib]
32
+
33
+ spec.version = RMeetup::Version
34
+ end
@@ -1,35 +1,83 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe RMeetup::Client, 'trying to access the API before being configured' do
4
- it 'should throw an error trying to search' do
5
- lambda {
6
- RMeetup::Client.fetch(:topics)
7
- }.should raise_error(RMeetup::NotConfiguredError)
8
- end
9
- end
3
+ describe RMeetup::Client do
10
4
 
11
- describe RMeetup::Client, 'trying to fetch an unknown type' do
12
- before do
13
- RMeetup::Client.api_key = API_KEY
5
+ context 'provided no API key' do
6
+ it 'fails with an NotConfiguredError' do
7
+ expect { RMeetup::Client.new }.to raise_error(RMeetup::Error::NotConfiguredError)
8
+ end
14
9
  end
15
-
16
- it 'should throw an error' do
17
- lambda {
18
- RMeetup::Client.fetch(:clowns)
19
- }.should raise_error(RMeetup::InvalidRequestTypeError)
20
- end
21
- end
22
10
 
23
- describe RMeetup::Client, 'fetching some topics' do
24
- before do
25
- RMeetup::Client.api_key = API_KEY
26
- @topics_fetcher = mock(RMeetup::Fetcher::Topics)
27
- @topics_fetcher.stub!(:fetch).and_return([])
28
- @type = :topics
29
- end
30
-
31
- it 'should try to get a Topic Fetcher' do
32
- RMeetup::Fetcher.should_receive(:for).with(@type).and_return(@topics_fetcher)
33
- RMeetup::Client.fetch(@type,{})
11
+ context 'provided with proper options' do
12
+
13
+ describe "#fetch" do
14
+ let(:client) { RMeetup::Client.new({api_key: API_KEY}) }
15
+
16
+ it 'should throw an error if invalid data type is requested' do
17
+ expect do
18
+ client.fetch(:clowns)
19
+ end.to raise_error(RMeetup::Error::InvalidRequestTypeError)
20
+ end
21
+
22
+ context 'when fetching :groups' do
23
+ it 'returns a collection of Groups' do
24
+ VCR.use_cassette('fetching_groups') do
25
+ client.fetch(:groups, { topic: 'Ruby' }).each do |result|
26
+ result.should be_kind_of(RMeetup::Type::Group)
27
+ end
28
+ end
29
+ end
30
+ end
31
+
32
+ context 'when fetching :events' do
33
+ it 'returns a collection of Events' do
34
+ VCR.use_cassette('fetching_events') do
35
+ client.fetch(:events, { group_id: '95875' }).each do |result|
36
+ result.should be_kind_of(RMeetup::Type::Event)
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ context 'when fetching :rsvps' do
43
+ it 'returns a collection of RSVPs' do
44
+ VCR.use_cassette('fetching_rsvps') do
45
+ client.fetch(:rsvps, { event_id: '1342342' }).each do |result|
46
+ result.should be_kind_of(RMeetup::Type::Rsvp)
47
+ end
48
+ end
49
+ end
50
+ end
51
+
52
+ context 'when fetching :members' do
53
+ it 'returns a collection of Members' do
54
+ VCR.use_cassette('fetching_members') do
55
+ client.fetch(:members, { group_id: '95875' }).each do |result|
56
+ result.should be_kind_of(RMeetup::Type::Member)
57
+ end
58
+ end
59
+ end
60
+ end
61
+
62
+ context 'when fetching :photos' do
63
+ it 'returns a collection of Photos' do
64
+ VCR.use_cassette('fetching_photos') do
65
+ client.fetch(:photos, { event_id: '13452536' }).each do |result|
66
+ result.should be_kind_of(RMeetup::Type::Photo)
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+ context 'when fetching :cities' do
73
+ it 'returns a collection of Cities' do
74
+ VCR.use_cassette('fetching_cities') do
75
+ client.fetch(:cities).each do |result|
76
+ result.should be_kind_of(RMeetup::Type::City)
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
34
82
  end
35
- end
83
+ end