BRIMIL01-meetup_api 0.1.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.
data/README ADDED
@@ -0,0 +1,67 @@
1
+ = Meetup API
2
+
3
+ == Developers
4
+ * {Bosco So}[http://boscomonkey.com] <rubymeetup _AT_ boscoso _DOT_ com>
5
+
6
+ == Description
7
+ Meetup API is a port of the official Python client released by Meetup
8
+ for their API. This port provides an object based API to query or
9
+ update Meetup data via pure Ruby. It hides the ugly HTTP/REST calls
10
+ from your code.
11
+
12
+ The Meetup Python API client can be found at
13
+ http://www.meetup.com/meetup_api/clients/
14
+
15
+ == External Dependencies
16
+ * Ruby (tested with 1.8.6)
17
+ * net/http
18
+ * json (tested with versions: 1.1.3)
19
+
20
+ == Usage Examples
21
+ Start by creating an instance of the Meetup object with your API key
22
+ as the constructor argument. You can get your API key at
23
+ http://www.meetup.com/meetup_api/key/
24
+
25
+ mu = MeetupApi::Client.new 'your_api_key'
26
+
27
+ Then, you can query the API directly by using one of the following
28
+ methods:
29
+
30
+ mu.get_events HASH_PARAMS
31
+ mu.get_rsvps HASH_PARAMS
32
+
33
+ Call the method using keyword args containing the parameters of the
34
+ query (identical to parameters described on the main documentation
35
+ page - http://www.meetup.com/meetup_api/docs/). For example, find all
36
+ the events around zip 94108:
37
+
38
+ local_events = mu.get_events :zip => 94108
39
+
40
+ Another example, find the event with id 9348580 (the results method
41
+ still returns an array, but it only has 1 element):
42
+
43
+ ruby_events = mu.get_events :id => 9348580
44
+
45
+ The returned value will be an object of type
46
+ MeetupApi::ApiResponse. This object's property meta is a Hash
47
+ containing all the metadata from the query results. The property
48
+ results is an Array containing objects of type MeetupApi::Event or
49
+ MeetupApi::Rsvp (depending which MeetupApi::Client method was
50
+ used). These objects support additional methods that will make API
51
+ calls to get more information (taking the MeetupApi::Client object as
52
+ an argument to make the request). For example, take the ruby event
53
+ from above and retrieve its RSVP list:
54
+
55
+ first_event = ruby_events.results.first
56
+ rsvps = first_event.get_rsvps mu
57
+
58
+ Of course, if you just wanted to only get the RSVP's, you can simply
59
+ call MeetupApi::Client#get_rsvps directly:
60
+
61
+ rsvps = mu.get_rsvps :event_id => 9348580
62
+
63
+ == To Do
64
+ * Currently, only MeetupApi::Client#get_events and get_rsvps are
65
+ implemented; so, finish the other client methods.
66
+ * Implement OAuth
67
+
@@ -0,0 +1,37 @@
1
+ require 'rubygems'
2
+ require 'rake/gempackagetask'
3
+ require 'rake/testtask'
4
+
5
+ task :default => :test
6
+
7
+ spec = Gem::Specification.new do |s|
8
+ s.name = 'meetup_api'
9
+ s.version = '0.1.0'
10
+ s.has_rdoc = true
11
+ s.extra_rdoc_files = %w(README)
12
+ s.rdoc_options = %w(--main README)
13
+ s.summary = "Ruby port of Meetup's official Python API client"
14
+ s.author = 'Bosco So'
15
+ s.email = 'git@boscoso.com'
16
+ s.homepage = 'http://boscoso.com'
17
+ s.files = %w(README Rakefile) + Dir.glob("{lib,test}/**/*")
18
+
19
+ s.add_dependency('json', '= 1.1.3')
20
+ end
21
+
22
+ Rake::GemPackageTask.new(spec) do |pkg|
23
+ pkg.gem_spec = spec
24
+ end
25
+
26
+ Rake::TestTask.new do |t|
27
+ t.libs << 'test'
28
+ t.test_files = FileList["test/**/*_test.rb"]
29
+ t.verbose = true
30
+ end
31
+
32
+ desc 'Generate the gemspec to serve this Gem from Github'
33
+ task :github do
34
+ file = File.dirname(__FILE__) + "/#{spec.name}.gemspec"
35
+ File.open(file, 'w') {|f| f << spec.to_ruby }
36
+ puts "Created gemspec: #{file}"
37
+ end
@@ -0,0 +1,189 @@
1
+ require 'net/http'
2
+ require 'rubygems'
3
+ require 'json'
4
+
5
+ module MeetupApi
6
+ DEV = ''
7
+ API_BASE_URL = "http://api#{DEV}.meetup.com/"
8
+ EVENTS_URI = 'events'
9
+ RSVPS_URI = 'rsvps'
10
+ MEMBERS_URI = 'members'
11
+ GROUPS_URI = 'groups'
12
+ PHOTOS_URI = 'photos'
13
+ TOPICS_URI = 'topics'
14
+ COMMENTS_URI = 'comments'
15
+
16
+ class Client
17
+ def initialize(apiKey)
18
+ @key = apiKey
19
+ end
20
+
21
+ def get_events(args)
22
+ ApiResponse.new(fetch(EVENTS_URI, args), Event)
23
+ end
24
+
25
+ def get_rsvps(args)
26
+ ApiResponse.new(fetch(RSVPS_URI, args), Rsvp)
27
+ end
28
+
29
+ def get_members(args)
30
+ ApiResponse.new(fetch(MEMBERS_URI, args), Member)
31
+ end
32
+
33
+ def get_groups(args)
34
+ ApiResponse.new(fetch(GROUPS_URI, args), Group)
35
+ end
36
+
37
+ def get_photos(args)
38
+ ApiResponse.new(fetch(PHOTOS_URI, args), Photo)
39
+ end
40
+
41
+ def get_topics(args)
42
+ ApiResponse.new(fetch(TOPICS_URI, args), Photo)
43
+ end
44
+
45
+ def get_comments(args)
46
+ ApiResponse.new(fetch(COMMENTS_URI, args), Comment)
47
+ end
48
+
49
+ def fetch(uri, url_args={})
50
+ url_args['format'] = 'json'
51
+ url_args['key'] = @key if @key
52
+ args = URI.escape(url_args.collect{|k,v| "#{k}=#{v}"}.join('&'))
53
+ url = "#{API_BASE_URL}#{uri}/?#{args}"
54
+ data = Net::HTTP.get_response(URI.parse(url)).body
55
+
56
+ # ugh - rate limit error throws badly formed JSON
57
+ begin
58
+ JSON.parse(data)
59
+ rescue Exception => e
60
+ raise BaseException(e)
61
+ end
62
+ end
63
+ end
64
+
65
+ class ApiResponse
66
+ attr_reader :meta, :results
67
+
68
+ def initialize(json, klass)
69
+ if (meta_data = json['meta'])
70
+ @meta = meta_data
71
+ @results = json['results'].collect {|hash| klass.new hash}
72
+ else
73
+ raise ClientException.new(json)
74
+ end
75
+ end
76
+ end
77
+
78
+ # Turns a hash into an object - see http://tinyurl.com/97dtjj
79
+ class Hashit
80
+ def initialize(hash)
81
+ hash.each do |k,v|
82
+ # create and initialize an instance variable for this key/value pair
83
+ self.instance_variable_set("@#{k}", v)
84
+ # create the getter that returns the instance variable
85
+ self.class.send(:define_method, k, proc{self.instance_variable_get("@#{k}")})
86
+ # create the setter that sets the instance variable (disabled for readonly)
87
+ ##self.class.send(:define_method, "#{k}=", proc{|v| self.instance_variable_set("@#{k}", v)})
88
+ end
89
+ end
90
+ end
91
+
92
+ # Base class for an item in a result set returned by the API.
93
+ class ApiItem < Hashit
94
+ end
95
+
96
+ class Event < ApiItem
97
+ def get_rsvps(apiclient, extraparams={})
98
+ extraparams['event_id'] = self.id
99
+ apiclient.get_rsvps extraparams
100
+ end
101
+
102
+ def to_s
103
+ "Event #{self.id} named #{self.name} at #{self.time} (url: #{self.event_url})"
104
+ end
105
+ end
106
+
107
+ class Rsvp < ApiItem
108
+ def to_s
109
+ "Rsvp by #{self.name} (#{self.link}) with comment: #{self.comment}"
110
+ end
111
+ end
112
+
113
+ class Group < ApiItem
114
+ def get_members(apiclient, extraparams={})
115
+ extraparams['group_id'] = self.id
116
+ apiclient.get_members extraparams
117
+ end
118
+
119
+ def get_photos(apiclient, extraparams={})
120
+ extraparams['group_id'] = self.id
121
+ apiclient.get_photos extraparams
122
+ end
123
+
124
+ def get_comments(apiclient, extraparams={})
125
+ extraparams['group_id'] = self.id
126
+ apiclient.get_comments extraparams
127
+ end
128
+
129
+ def to_s
130
+ "Group #{self.id} named #{self.name}"
131
+ end
132
+ end
133
+
134
+ class Member < ApiItem
135
+ def to_s
136
+ "Member #{self.id} named #{self.name}"
137
+ end
138
+ end
139
+
140
+ class Photo < ApiItem
141
+ def to_s
142
+ "Photo #{self.id} named #{self.name}"
143
+ end
144
+ end
145
+
146
+ class Topic < ApiItem
147
+ def get_photos(apiclient, extraparams={})
148
+ extraparams['topic_id'] = self.id
149
+ apiclient.get_photos extraparams
150
+ end
151
+
152
+ def to_s
153
+ "Topic #{self.id} named #{self.name}"
154
+ end
155
+ end
156
+
157
+ class Comment < ApiItem
158
+ def to_s
159
+ "Comment #{self.id} named #{self.name}"
160
+ end
161
+ end
162
+
163
+ # Base class for unexpected errors returned by the Client
164
+ class BaseException < Exception
165
+ attr_reader :problem
166
+
167
+ def initialize(e)
168
+ @problem = e
169
+ end
170
+
171
+ def to_s
172
+ "#{self.problem}"
173
+ end
174
+ end
175
+
176
+ class ClientException < BaseException
177
+ attr_reader :description
178
+
179
+ def initialize(error_json)
180
+ @description = error_json['details']
181
+ @problem = error_json['problem']
182
+ end
183
+
184
+ def to_s
185
+ "#{self.problem}: #{self.description}"
186
+ end
187
+ end
188
+
189
+ end
@@ -0,0 +1,4 @@
1
+ # Copy this file to api_key.rb but make sure you get a real key first
2
+ # from http://www.meetup.com/meetup_api/key/
3
+ #
4
+ API_KEY = "dummy key"
@@ -0,0 +1,106 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'test/unit'
4
+ require File.join(File.expand_path(File.dirname(__FILE__)),
5
+ "..", "lib", "meetup_api")
6
+
7
+ class MeetupApiTester < Test::Unit::TestCase
8
+
9
+ def setup
10
+ require File.join(File.expand_path(File.dirname(__FILE__)), 'api_key')
11
+
12
+ @key = API_KEY
13
+ @api = MeetupApi::Client.new(@key)
14
+ end
15
+
16
+ def test_instantiate_new_client
17
+ end
18
+
19
+ def test_fetch
20
+ # 8337541 is ID for 'My Rake is Bigger than your Rake (Ooga Labs)'
21
+ json = @api.fetch(MeetupApi::RSVPS_URI, :event_id => 8337541)
22
+ assert_not_nil(json, 'json cannot be nil')
23
+ assert_instance_of Hash, json
24
+ assert_equal(2, json.keys.size, 'valid JSON should have only 2 keys')
25
+ json
26
+ end
27
+
28
+ def test_fetch_meta
29
+ json = test_fetch
30
+ meta = json['meta']
31
+ assert_not_nil(meta, "result metadata can't be nil")
32
+ end
33
+
34
+ def test_fetch_results
35
+ json = test_fetch
36
+ results = json['results']
37
+ assert_not_nil(results, "result data can't be nil")
38
+ assert_instance_of Array, results
39
+ assert_equal(62, results.size, "should have 62 RSVP's")
40
+ end
41
+
42
+ def test_fetch_missing_2nd_argument
43
+ json = @api.fetch(MeetupApi::RSVPS_URI)
44
+ assert_not_nil(json)
45
+ end
46
+
47
+ def test_fetch_missing_event_id
48
+ json = @api.fetch(MeetupApi::RSVPS_URI)
49
+ assert_instance_of Hash, json
50
+ assert_equal(2, json.keys.size, 'valid JSON should have only 2 keys')
51
+ assert_not_nil(json['details'])
52
+ assert_not_nil(json['problem'])
53
+ end
54
+
55
+ def test_fetch_unauthorized
56
+ client = MeetupApi::Client.new('invalid_key')
57
+ json = client.fetch(MeetupApi::RSVPS_URI, :event_id => 8337541)
58
+ assert_instance_of Hash, json
59
+ assert_equal(2, json.keys.size, 'valid JSON should have only 2 keys')
60
+ assert_not_nil(json['details'])
61
+ assert_not_nil(json['problem'])
62
+ end
63
+
64
+
65
+ def test_rsvp_valid
66
+ ret = @api.get_rsvps :event_id => 8337541
67
+ verify_my_rake_is_bigger_than_your_rake ret
68
+ end
69
+
70
+ def test_rsvp_missing_id
71
+ begin
72
+ ret = @api.get_rsvps({})
73
+ flunk 'previous call should raise'
74
+ rescue MeetupApi::ClientException => e
75
+ assert_not_nil e.description
76
+ assert_not_nil e.problem
77
+ end
78
+ end
79
+
80
+ def test_events
81
+ ret = @api.get_events :id => 8337541, :after => '01011970'
82
+ assert_not_nil ret.meta
83
+ assert_not_nil ret.results
84
+ assert_instance_of Array, ret.results
85
+ assert_equal 1, ret.results.size
86
+ ret
87
+ end
88
+
89
+ def test_events_rsvps
90
+ events = test_events
91
+ rsvps = events.results.first.get_rsvps @api
92
+ verify_my_rake_is_bigger_than_your_rake rsvps
93
+ end
94
+
95
+ private
96
+
97
+ def verify_my_rake_is_bigger_than_your_rake(ret)
98
+ assert_not_nil ret, "RSVP's for 'My Rake...' event should not be nil"
99
+ assert_not_nil ret.meta
100
+ assert_not_nil ret.results
101
+ assert_instance_of Array, ret.results
102
+ assert_equal 62, ret.results.size
103
+ ret.results.each {|r| assert_instance_of MeetupApi::Rsvp, r}
104
+ end
105
+
106
+ end
metadata ADDED
@@ -0,0 +1,67 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: BRIMIL01-meetup_api
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Bosco So
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-02-04 00:00:00 -08:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: json
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - "="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.1.3
24
+ version:
25
+ description:
26
+ email: user@example.com
27
+ executables: []
28
+
29
+ extensions: []
30
+
31
+ extra_rdoc_files:
32
+ - README
33
+ files:
34
+ - README
35
+ - Rakefile
36
+ - lib/meetup_api.rb
37
+ - test/api_key.README
38
+ - test/meetup_api_tester.rb
39
+ has_rdoc: true
40
+ homepage: http://example.com
41
+ post_install_message:
42
+ rdoc_options:
43
+ - --main
44
+ - README
45
+ require_paths:
46
+ - lib
47
+ required_ruby_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: "0"
52
+ version:
53
+ required_rubygems_version: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: "0"
58
+ version:
59
+ requirements: []
60
+
61
+ rubyforge_project:
62
+ rubygems_version: 1.2.0
63
+ signing_key:
64
+ specification_version: 2
65
+ summary: Ruby port of Meetup's official Python API client
66
+ test_files: []
67
+