BRIMIL01-meetup_api 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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
+