rgdata 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gemtest ADDED
File without changes
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in rgdata.gemspec
4
+ gemspec
data/README.rdoc ADDED
@@ -0,0 +1,98 @@
1
+ = RGData
2
+
3
+ RGData makes it easy to get data from Google's Data APIs.
4
+ It streamlines requests and gives you back good ole Ruby objects.
5
+
6
+ An Example:
7
+
8
+ domain = 'redningja.com'
9
+ profiles = RGData::API::Profiles.retrieve domain, oauth_token: 'access_token_thatlookslikegibberish'
10
+ => [#<RGData::Model::Profile full_name: 'Jared Ning'>, ...]
11
+ # Now you can just do this:
12
+ profiles.first.full_name
13
+ # Want each user's email addresses?
14
+ profiles.map(&:emails)
15
+
16
+ == Installation
17
+
18
+ gem install rgdata
19
+
20
+ RGData.config do
21
+ client_id <your client_id>
22
+ client_secret <your client_secret>
23
+ end
24
+
25
+ == Authentication
26
+
27
+ To retrieve data, you need an oauth token (a.k.a. access token).
28
+ There's a lot of setup, but after that it should be easy.
29
+
30
+ * Setup
31
+ * Get your client_id and client_secret
32
+ * https://code.google.com/apis/console
33
+ * Click on Identity at the left.
34
+ * Look for OAuth2 Credentials and you'll find your client_id and client_secret
35
+ * While you're you may want to list your redirect URIs if you know what they will be. These must match your authentication requests (more on this later).
36
+ * Request authentication. (Depending on which API you are using, there may be specific instructions you may want to tell your user about. See the individual APIs classes for specifics.)
37
+ * Your user will go to the request URL generated for you by calling, for example, RGData::API::Profiles.request_url(scope, redirect_uri).
38
+ * They will be promted by Google to authenticate and grant permission.
39
+ * Successful authentication will return hash that you are responsible for collecting. It includes 3 parts:
40
+ * access_token: This is what we're after. You can use this immediately to retrieve data. You can also get one by using refresh_token which we'll get to in a sec.
41
+ * expiration: You have this many seconds to use access_token before it expires.
42
+ * refresh_token: See below.
43
+ * Once you have your access token, you can start accessing the data.
44
+
45
+ === Refresh tokens
46
+
47
+ A refresh token won't get you into the data, but it will allow you to get more access tokens in the future. Keep this if you don't want your users to re-authenticate over and over again.
48
+ To get another access token:
49
+
50
+ RGData::API::Profiles.get_access_token(refresh_token)
51
+
52
+ For convenience, APIs are smart enough to do this work for you:
53
+
54
+ RGData::API::Profiles.retrieve domain, refresh_token: refresh_token
55
+
56
+ === client-side and native application authentication
57
+
58
+ TODO
59
+
60
+ == Retrieving data
61
+
62
+ Easy:
63
+
64
+ # If you already have an access_token or oauth_token (same diff)
65
+ RGData::API::Profiles.retrieve domain, oauth_token: oauth_token
66
+ # If you only have a refresh token
67
+ RGData::API::Profiles.retrieve domain, refresh_token: refresh_token
68
+ # Both will return familiar, easy-to-use Ruby objects
69
+ _.first.full_name
70
+ #=> 'Jared Ning'
71
+
72
+ === Query parameters, Retrieving a single profile, etc.
73
+
74
+ TODO
75
+
76
+ There are more ways to retrieve information (e.g. http://code.google.com/googleapps/domain/profiles/developers_guide_protocol.html#Retrieving).
77
+ Currently the gem only supports retrieving all (or at least all that Google allows in one request).
78
+
79
+ == Supported APIs
80
+
81
+ * Profiles
82
+
83
+ === Add more
84
+
85
+ Please feel free to contribute more APIs.
86
+ Take a look at lib/rgdata/apis/profiles.rb and lib/rgdata/models/profile.rb to see how easy it is.
87
+ For the API, just find the scope (link below)
88
+
89
+ == Compatibility
90
+
91
+ Ruby 1.9. Want 1.8 support? Go fork yourself.
92
+ gem-testers: http://test.rubygems.org/gems/rgdata
93
+
94
+ == Useful links
95
+
96
+ * OAuth2 for server side: http://code.google.com/apis/accounts/docs/OAuth2.html#SS
97
+ * Scopes: http://code.google.com/apis/gdata/faq.html#AuthScopes
98
+ * Each API class should have a link to its corresponding Google API page.
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ require 'rake/testtask'
5
+
6
+ task :default => :test
7
+
8
+ Rake::TestTask.new do |t|
9
+ t.pattern = "mspec/**/*_spec.rb"
10
+ end
data/lib/rgdata/api.rb ADDED
@@ -0,0 +1,55 @@
1
+ module RGData::API
2
+
3
+ def self.included(target)
4
+ target.extend ClassMethods
5
+ target.instance_eval do
6
+ include HTTParty
7
+ headers 'GData-Version' => '3.0'
8
+ format :xml
9
+ end
10
+ end
11
+
12
+ module ClassMethods
13
+
14
+ attr_accessor :scope
15
+ attr_accessor :model
16
+
17
+ def request_url(redirect_uri)
18
+ RGData::Authentication.request_url(scope, redirect_uri)
19
+ end
20
+
21
+ # Send request and return response.
22
+ # options can be :oauth_token, :access_token, or :refresh_token.
23
+ def do_request(domain, options)
24
+ oauth_token = extract_oauth_token_or_access_token_or_get_one_using_refresh_token(options)
25
+ query = {oauth_token: oauth_token}
26
+ query.verify_keys_present_and_values_not_nil(:oauth_token)
27
+ get "/#{domain}/full", query: query
28
+ end
29
+
30
+ # Perform request and instantiate new model objects.
31
+ def retrieve(domain, options)
32
+ full_xml_hash = do_request(domain, options)
33
+ full_xml_hash['feed']['entry'].map do |xml_hash|
34
+ model.new_from_xml_hash xml_hash
35
+ end
36
+ end
37
+
38
+ private
39
+
40
+ # Get an oauth_token from a hash.
41
+ # It can be in the form of oauth_token or access_token.
42
+ # If neither of those is found, refresh_token should be present and
43
+ # will be used to get an access_token.
44
+ def extract_oauth_token_or_access_token_or_get_one_using_refresh_token(hash)
45
+ oauth_token = hash.extract_values_at(:oauth_token, :access_token).compact.first
46
+ unless oauth_token
47
+ refresh_token = hash.extract(:refresh_token)
48
+ oauth_token = RGData::Authentication::get_access_token(refresh_token)
49
+ end
50
+ oauth_token
51
+ end
52
+
53
+ end
54
+
55
+ end
@@ -0,0 +1,18 @@
1
+ # http://code.google.com/googleapps/domain/profiles/developers_guide_protocol.html
2
+ # Authentication notes:
3
+ # Google requires that the user granting permission be an administrator for their domain.
4
+ # When prompted for which account they are going to use, they should choose the one under the Google Apps section (not the default one).
5
+
6
+ module RGData::API
7
+
8
+ class Profiles
9
+
10
+ include RGData::API
11
+
12
+ base_uri 'http://www.google.com/m8/feeds/profiles/domain'
13
+ self.scope = 'http://www.google.com/m8/feeds/'
14
+ self.model = RGData::Model::Profile
15
+
16
+ end
17
+
18
+ end
@@ -0,0 +1,37 @@
1
+ class RGData::Authentication
2
+
3
+ include HTTParty
4
+ base_uri 'https://accounts.google.com/o/oauth2'
5
+
6
+ class << self
7
+
8
+ # Construct a url for requesting authentication.
9
+ def request_url(scope, redirect_uri)
10
+ method = Net::HTTP::Get
11
+ path = default_options[:base_uri] + '/auth'
12
+ query = {
13
+ redirect_uri: redirect_uri,
14
+ scope: scope,
15
+ response_type: 'code',
16
+ client_id: RGData.config.client_id,
17
+ }
18
+ query.verify_keys_present_and_values_not_nil(:client_id, :redirect_uri, :scope, :response_type)
19
+ request = HTTParty::Request.new(method, path, query: query)
20
+ URI.unescape(request.uri.to_s)
21
+ end
22
+
23
+ def get_access_token(refresh_token)
24
+ body = {
25
+ refresh_token: refresh_token,
26
+ grant_type: 'refresh_token',
27
+ client_id: RGData.config.client_id,
28
+ client_secret: RGData.config.client_secret,
29
+ }
30
+ body.verify_keys_present_and_values_not_nil(:refresh_token, :grant_type, :client_secret, :client_id)
31
+ response = post('/token', body: body)
32
+ response['access_token']
33
+ end
34
+
35
+ end
36
+
37
+ end
@@ -0,0 +1,34 @@
1
+ class RGData::Config
2
+
3
+ configurations = [
4
+ :client_id, # https://code.google.com/apis/console, (click 'Identity' on left)
5
+ :client_secret, # https://code.google.com/apis/console, (click 'Identity' on left)
6
+ ]
7
+
8
+ configurations.each do |configuration|
9
+ define_method configuration, do |*new_value|
10
+ instance_variable = :"@#{configuration}"
11
+ if new_value.any?
12
+ instance_variable_set instance_variable, new_value.first
13
+ else
14
+ instance_variable_get instance_variable
15
+ end
16
+ end
17
+ end
18
+
19
+ end
20
+
21
+ module RGData
22
+
23
+ class << self
24
+
25
+ def config(&block)
26
+ @config ||= RGData::Config.new
27
+ @config.instance_eval &block if block_given?
28
+ raise 'You need to set client_id and client_secret in configs' unless @config.client_id && @config.client_secret
29
+ @config
30
+ end
31
+
32
+ end
33
+
34
+ end
@@ -0,0 +1,55 @@
1
+ # A class that includes RGData::Model represent an XML response from GOogle as a Ruby object.
2
+ # The class method new_from_xml_hash accepts an XML hash.
3
+ # It should be the portion of the HTTParty/Crack parsed hash that represents a single record.
4
+ # Once the module is included, the class just needs to declare fields and
5
+ # define instructions on how to drill down into the hash to get to the useful information.
6
+
7
+ module RGData::Model
8
+
9
+ def self.included(target)
10
+ target.extend ClassMethods
11
+ end
12
+
13
+ module ClassMethods
14
+
15
+ # Declare a field.
16
+ # It will be given attr_accessor.
17
+ # parse_block will be instructions on how to drill down into the xml_hash and get the value.
18
+ def field(name, &parse_block)
19
+ parse_blocks[name] = parse_block
20
+ attr_accessor name
21
+ end
22
+
23
+ def new_from_xml_hash(xml_hash)
24
+ attributes = parse_blocks.inject({}) do |atts, (field, parse_block)|
25
+ begin
26
+ atts[field] = parse_block.call(xml_hash)
27
+ atts
28
+ rescue Exception => ex
29
+ raise FieldError.new(field, ex.message)
30
+ end
31
+ end
32
+ new(attributes)
33
+ end
34
+
35
+ private
36
+
37
+ def parse_blocks
38
+ @parse_blocks ||= {}
39
+ end
40
+
41
+ end
42
+
43
+ def initialize(atts = {})
44
+ atts.each do |field, value|
45
+ send "#{field}=", value
46
+ end
47
+ end
48
+
49
+ class FieldError < StandardError
50
+ def initialize(field, msg)
51
+ super "Error extracting '#{field}' field value from xml_hash: #{msg}"
52
+ end
53
+ end
54
+
55
+ end
@@ -0,0 +1,58 @@
1
+ # This is a a single Google profile obtained from from XML, parsed into to a hash (by HTTParty/Crack), then output by awesome_print.
2
+ # {
3
+ # "id" => "http://www.google.com/m8/feeds/profiles/domain/redningja.com/full/jared",
4
+ # "updated" => "2011-03-31T15:24:04.179Z",
5
+ # "app:edited" => "2011-03-31T15:24:04.179Z",
6
+ # "category" => {
7
+ # "scheme" => "http://schemas.google.com/g/2005#kind",
8
+ # "term" => "http://schemas.google.com/contact/2008#profile"
9
+ # },
10
+ # "title" => "Jared Ning",
11
+ # "link" => [
12
+ # [0] {
13
+ # "rel" => "http://schemas.google.com/contacts/2008/rel#photo",
14
+ # "type" => "image/*",
15
+ # "href" => "https://www.google.com/m8/feeds/photos/profile/redningja.com/jared"
16
+ # },
17
+ # [1] {
18
+ # "rel" => "self",
19
+ # "type" => "application/atom+xml",
20
+ # "href" => "https://www.google.com/m8/feeds/profiles/domain/redningja.com/full/jared"
21
+ # },
22
+ # [2] {
23
+ # "rel" => "edit",
24
+ # "type" => "application/atom+xml",
25
+ # "href" => "https://www.google.com/m8/feeds/profiles/domain/redningja.com/full/jared"
26
+ # }
27
+ # ],
28
+ # "gd:name" => {
29
+ # "gd:fullName" => "Jared Ning",
30
+ # "gd:givenName" => "Jared",
31
+ # "gd:familyName" => "Ning"
32
+ # },
33
+ # "gd:email" => [
34
+ # [0] {
35
+ # "rel" => "http://schemas.google.com/g/2005#other",
36
+ # "address" => "jared@redningja.com",
37
+ # "primary" => "true"
38
+ # },
39
+ # [1] {
40
+ # "rel" => "http://schemas.google.com/g/2005#other",
41
+ # "address" => "*@redningja.com"
42
+ # }
43
+ # ],
44
+ # "gd:etag" => "\"D0NbQR5cQit7I2A4\""
45
+ # }
46
+ #
47
+ # It is possible for a profile to have multiple email addresses.
48
+
49
+ class RGData::Model::Profile
50
+
51
+ include RGData::Model
52
+
53
+ field(:full_name) { |xml_hash| xml_hash['gd:name']['gd:fullName'] }
54
+ field(:given_name) { |xml_hash| xml_hash['gd:name']['gd:givenName'] }
55
+ field(:family_name) { |xml_hash| xml_hash['gd:name']['gd:familyName'] }
56
+ field(:emails) { |xml_hash| [xml_hash['gd:email']].flatten.map { |email| email['address'] } }
57
+
58
+ end
@@ -0,0 +1,3 @@
1
+ module Rgdata
2
+ VERSION = '0.1.0'
3
+ end
data/lib/rgdata.rb ADDED
@@ -0,0 +1,18 @@
1
+ require 'httparty'
2
+
3
+ module RGData
4
+ end
5
+
6
+ require 'rgdata/config'
7
+ require 'rgdata/authentication'
8
+ require 'rgdata/api'
9
+ require 'rgdata/model'
10
+
11
+ lib_requires = [
12
+ '/support/**/*.rb',
13
+ '/rgdata/models/**/*.rb',
14
+ '/rgdata/apis/**/*.rb',
15
+ ]
16
+ lib_requires.each do |lib|
17
+ Dir[File.dirname(File.expand_path(__FILE__)) + lib].each { |file| require file }
18
+ end
@@ -0,0 +1,27 @@
1
+ class Hash
2
+
3
+ # This gem was meant to help deal with the strictness of APIs
4
+ # and their inability to give good feedback for mundane detail errors.
5
+ # This method helps eliminate guessing when it comes to Request query/body/etc. hashes.
6
+ def verify_keys_present_and_values_not_nil(*keys)
7
+ keys.each do |key|
8
+ unless has_key?(key.to_sym) || has_key?(key.to_s)
9
+ raise "Options missing '#{key}'"
10
+ end
11
+ if extract(key).nil?
12
+ raise "Option '#{key}' cannot be nil"
13
+ end
14
+ end
15
+ end
16
+
17
+ # get a value from the given key whether it be stored as a symbol or string.
18
+ def extract(key)
19
+ self[key.to_s] || self[key.to_sym]
20
+ end
21
+
22
+ # Same as values_at(), but uses extract().
23
+ def extract_values_at(*keys)
24
+ keys.map { |key| extract(key) }
25
+ end
26
+
27
+ end
data/mspec/api_spec.rb ADDED
@@ -0,0 +1,13 @@
1
+ require File.dirname(File.expand_path(__FILE__)) + '/spec_helper'
2
+
3
+ describe RGData::API do
4
+
5
+ it 'extracts oauth_token or access_token or gets one using refresh_token' do
6
+ klass = Class.new
7
+ klass.extend RGData::API::ClassMethods
8
+ klass.send(:extract_oauth_token_or_access_token_or_get_one_using_refresh_token, oauth_token: 'asdf').must_equal 'asdf'
9
+ klass.send(:extract_oauth_token_or_access_token_or_get_one_using_refresh_token, access_token: 'asdf').must_equal 'asdf'
10
+ klass.send(:extract_oauth_token_or_access_token_or_get_one_using_refresh_token, refresh_token: TEST_REFRESH_TOKEN).wont_be_nil
11
+ end
12
+
13
+ end
@@ -0,0 +1,15 @@
1
+ require File.dirname(File.expand_path(__FILE__)) + '/../spec_helper'
2
+
3
+ describe RGData::API do
4
+
5
+ before :each do
6
+ @klass = Class.new
7
+ @klass.send :include, RGData::API
8
+ end
9
+
10
+ it 'stores and returns scope' do
11
+ @klass.scope = 'asdf'
12
+ @klass.scope.must_equal 'asdf'
13
+ end
14
+
15
+ end
@@ -0,0 +1,27 @@
1
+ require File.dirname(File.expand_path(__FILE__)) + '/../spec_helper'
2
+
3
+ describe RGData::API::Profiles do
4
+
5
+ it 'has a scope' do
6
+ RGData::API::Profiles.scope.wont_be_nil
7
+ end
8
+
9
+ it 'requests authentication url' do
10
+ redirect_uri = 'http://come.back.here.after.successful.authentication.com/'
11
+ request_url = RGData::API::Profiles.request_url(redirect_uri)
12
+ uri = URI.escape("https://accounts.google.com/o/oauth2/auth?redirect_uri=http://come.back.here.after.successful.authentication.com/&scope=http://www.google.com/m8/feeds/&response_type=code&client_id=#{RGData.config.client_id}")
13
+ request_url.must_equal uri
14
+ end
15
+
16
+ it 'retrieves profiles from a domain' do
17
+ domain = 'redningja.com'
18
+ access_token = RGData::Authentication.get_access_token(TEST_REFRESH_TOKEN)
19
+ profiles = RGData::API::Profiles.retrieve(domain, oauth_token: access_token)
20
+ profiles.wont_be_empty
21
+ end
22
+
23
+ it 'has a corresponding model' do
24
+ RGData::API::Profiles.model.must_equal RGData::Model::Profile
25
+ end
26
+
27
+ end
@@ -0,0 +1,17 @@
1
+ require File.dirname(File.expand_path(__FILE__)) + '/spec_helper'
2
+
3
+ describe RGData::Authentication do
4
+
5
+ it 'provides a url for a user to request authentication' do
6
+ scope = RGData::API::Profiles.scope
7
+ redirect_uri = 'http://come.back.here.after.successful.authentication.com/'
8
+ request_url = RGData::Authentication.request_url(scope, redirect_uri)
9
+ uri = URI.escape("https://accounts.google.com/o/oauth2/auth?redirect_uri=http://come.back.here.after.successful.authentication.com/&scope=http://www.google.com/m8/feeds/&response_type=code&client_id=#{RGData.config.client_id}")
10
+ request_url.must_equal uri
11
+ end
12
+
13
+ it 'retrieves another access_token given a refresh_token' do
14
+ RGData::Authentication.get_access_token(TEST_REFRESH_TOKEN).wont_be_nil
15
+ end
16
+
17
+ end
@@ -0,0 +1,22 @@
1
+ require File.dirname(File.expand_path(__FILE__)) + '/spec_helper'
2
+
3
+ describe RGData::Config do
4
+
5
+ after :each do
6
+ # Restore configs.
7
+ RGData.config do
8
+ client_id ENV['GOOGLE_APPS_CLIENT_ID']
9
+ client_secret ENV['GOOGLE_APPS_CLIENT_SECRET']
10
+ end
11
+ end
12
+
13
+ it 'stores and returns client_id and client_secret' do
14
+ RGData.config do
15
+ client_id 1
16
+ client_secret 2
17
+ end
18
+ RGData.config.client_id.must_equal 1
19
+ RGData.config.client_secret.must_equal 2
20
+ end
21
+
22
+ end
@@ -0,0 +1 @@
1
+ <?xml version='1.0' encoding='UTF-8'?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearch/1.1/' xmlns:gContact='http://schemas.google.com/contact/2008' xmlns:batch='http://schemas.google.com/gdata/batch' xmlns:gd='http://schemas.google.com/g/2005' gd:etag='W/&quot;D0cARX86cSt7I2A9WhZSFUU.&quot;'><id>redningja.com</id><updated>2011-03-31T15:24:04.119Z</updated><category scheme='http://schemas.google.com/g/2005#kind' term='http://schemas.google.com/contact/2008#profile'/><title>redningja.com's Profiles</title><link rel='alternate' type='text/html' href='http://www.google.com/'/><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='https://www.google.com/m8/feeds/profiles/domain/redningja.com/full'/><link rel='http://schemas.google.com/g/2005#batch' type='application/atom+xml' href='https://www.google.com/m8/feeds/profiles/domain/redningja.com/full/batch'/><link rel='self' type='application/atom+xml' href='https://www.google.com/m8/feeds/profiles/domain/redningja.com/full?max-results=60'/><author><name>redningja.com</name></author><generator version='1.0' uri='http://www.google.com/m8/feeds'>Contacts</generator><openSearch:totalResults>4</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>60</openSearch:itemsPerPage><entry gd:etag='&quot;D0NbQR5cQit7I2A4&quot;'><id>http://www.google.com/m8/feeds/profiles/domain/redningja.com/full/jared</id><updated>2011-03-31T15:24:04.179Z</updated><app:edited xmlns:app='http://www.w3.org/2007/app'>2011-03-31T15:24:04.179Z</app:edited><category scheme='http://schemas.google.com/g/2005#kind' term='http://schemas.google.com/contact/2008#profile'/><title>Jared Ning</title><link rel='http://schemas.google.com/contacts/2008/rel#photo' type='image/*' href='https://www.google.com/m8/feeds/photos/profile/redningja.com/jared'/><link rel='self' type='application/atom+xml' href='https://www.google.com/m8/feeds/profiles/domain/redningja.com/full/jared'/><link rel='edit' type='application/atom+xml' href='https://www.google.com/m8/feeds/profiles/domain/redningja.com/full/jared'/><gd:name><gd:fullName>Jared Ning</gd:fullName><gd:givenName>Jared</gd:givenName><gd:familyName>Ning</gd:familyName></gd:name><gd:email rel='http://schemas.google.com/g/2005#other' address='jared@redningja.com' primary='true'/><gd:email rel='http://schemas.google.com/g/2005#other' address='*@redningja.com'/></entry><entry gd:etag='&quot;XBEHEU8MRit7I2A0&quot;'><id>http://www.google.com/m8/feeds/profiles/domain/redningja.com/full/jwn</id><updated>2011-03-31T15:24:04.179Z</updated><app:edited xmlns:app='http://www.w3.org/2007/app'>2011-03-31T15:24:04.179Z</app:edited><category scheme='http://schemas.google.com/g/2005#kind' term='http://schemas.google.com/contact/2008#profile'/><title>jared weining</title><link rel='http://schemas.google.com/contacts/2008/rel#photo' type='image/*' href='https://www.google.com/m8/feeds/photos/profile/redningja.com/jwn'/><link rel='self' type='application/atom+xml' href='https://www.google.com/m8/feeds/profiles/domain/redningja.com/full/jwn'/><link rel='edit' type='application/atom+xml' href='https://www.google.com/m8/feeds/profiles/domain/redningja.com/full/jwn'/><gd:name><gd:fullName>jared weining</gd:fullName><gd:givenName>jared</gd:givenName><gd:familyName>weining</gd:familyName></gd:name><gd:email rel='http://schemas.google.com/g/2005#other' address='jwn@redningja.com' primary='true'/></entry><entry gd:etag='&quot;ChRQGUxZQSt7I2A9&quot;'><id>http://www.google.com/m8/feeds/profiles/domain/redningja.com/full/darcy</id><updated>2011-03-31T15:24:04.179Z</updated><app:edited xmlns:app='http://www.w3.org/2007/app'>2011-03-31T15:24:04.179Z</app:edited><category scheme='http://schemas.google.com/g/2005#kind' term='http://schemas.google.com/contact/2008#profile'/><title>darcy clark</title><link rel='http://schemas.google.com/contacts/2008/rel#photo' type='image/*' href='https://www.google.com/m8/feeds/photos/profile/redningja.com/darcy'/><link rel='self' type='application/atom+xml' href='https://www.google.com/m8/feeds/profiles/domain/redningja.com/full/darcy'/><link rel='edit' type='application/atom+xml' href='https://www.google.com/m8/feeds/profiles/domain/redningja.com/full/darcy'/><gd:name><gd:fullName>darcy clark</gd:fullName><gd:givenName>darcy</gd:givenName><gd:familyName>clark</gd:familyName></gd:name><gd:email rel='http://schemas.google.com/g/2005#other' address='darcy@redningja.com' primary='true'/></entry><entry gd:etag='&quot;URFUQx5eFCt7I2A-&quot;'><id>http://www.google.com/m8/feeds/profiles/domain/redningja.com/full/stephanie</id><updated>2011-03-31T15:24:04.180Z</updated><app:edited xmlns:app='http://www.w3.org/2007/app'>2011-03-31T15:24:04.180Z</app:edited><category scheme='http://schemas.google.com/g/2005#kind' term='http://schemas.google.com/contact/2008#profile'/><title>stephanie adler</title><link rel='http://schemas.google.com/contacts/2008/rel#photo' type='image/*' href='https://www.google.com/m8/feeds/photos/profile/redningja.com/stephanie'/><link rel='self' type='application/atom+xml' href='https://www.google.com/m8/feeds/profiles/domain/redningja.com/full/stephanie'/><link rel='edit' type='application/atom+xml' href='https://www.google.com/m8/feeds/profiles/domain/redningja.com/full/stephanie'/><gd:name><gd:fullName>stephanie adler</gd:fullName><gd:givenName>stephanie</gd:givenName><gd:familyName>adler</gd:familyName></gd:name><gd:email rel='http://schemas.google.com/g/2005#other' address='stephanie@redningja.com' primary='true'/></entry></feed>
@@ -0,0 +1,20 @@
1
+ require File.dirname(File.expand_path(__FILE__)) + '/../spec_helper'
2
+
3
+ describe RGData::Model::Profile do
4
+
5
+ it 'can be created from an xml hash' do
6
+ data = [
7
+ ['Jared Ning', 'Jared', 'Ning', ['jared@redningja.com', '*@redningja.com']],
8
+ ['jared weining', 'jared', 'weining', ['jwn@redningja.com']],
9
+ ['darcy clark', 'darcy', 'clark', ['darcy@redningja.com']],
10
+ ['stephanie adler', 'stephanie', 'adler', ['stephanie@redningja.com']],
11
+ ]
12
+ xml_hashes = Crack::XML.parse(fixture_file('profiles.xml'))['feed']['entry']
13
+ xml_hashes.map do |xml_hash|
14
+ profile = RGData::Model::Profile.new_from_xml_hash(xml_hash)
15
+ profile_data = [:full_name, :given_name, :family_name, :emails].map { |field| profile.send field }
16
+ profile_data
17
+ end.must_equal data
18
+ end
19
+
20
+ end
@@ -0,0 +1,23 @@
1
+ require 'minitest/pride'
2
+ require 'minitest/autorun'
3
+ require 'pathname'
4
+ ROOT_DIR = Pathname(File.dirname(File.expand_path(__FILE__))) + '..'
5
+ $LOAD_PATH << (ROOT_DIR + 'lib').to_s
6
+ require 'rgdata'
7
+ require 'crack'
8
+
9
+ RGData.config do
10
+ client_id ENV['GOOGLE_APPS_CLIENT_ID']
11
+ client_secret ENV['GOOGLE_APPS_CLIENT_SECRET']
12
+ end
13
+ TEST_REFRESH_TOKEN = ENV['GOOGLE_APPS_REFRESH_TOKEN']
14
+
15
+ module MiniTest::Spec::Helpers
16
+
17
+ def fixture_file(file_name)
18
+ File.open(ROOT_DIR + 'mspec/fixtures/' + file_name)
19
+ end
20
+
21
+ end
22
+
23
+ MiniTest::Unit::TestCase.send :include, MiniTest::Spec::Helpers
data/rgdata.gemspec ADDED
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path('../lib', __FILE__)
3
+ require 'rgdata/version'
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = 'rgdata'
7
+ s.version = Rgdata::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ['Jared Ning']
10
+ s.email = ['jared@redningja.com']
11
+ s.homepage = 'https://github.com/ordinaryzelig/rgdata'
12
+ s.summary = %q{Google Data API for Ruby}
13
+ s.description = %q{Google Data API for Ruby}
14
+
15
+ s.rubyforge_project = 'rgdata'
16
+
17
+ s.files = `git ls-files`.split()
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split('\n')
19
+ s.executables = `git ls-files -- bin/*`.split('\n').map{ |f| File.basename(f) }
20
+ s.require_paths = ['lib']
21
+
22
+ s.add_dependency 'httparty', '~> 0.7.4'
23
+
24
+ s.add_development_dependency 'minitest', '2.0.2'
25
+ end
metadata ADDED
@@ -0,0 +1,100 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rgdata
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.1.0
6
+ platform: ruby
7
+ authors:
8
+ - Jared Ning
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-04-01 00:00:00 -05:00
14
+ default_executable:
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: httparty
18
+ prerelease: false
19
+ requirement: &id001 !ruby/object:Gem::Requirement
20
+ none: false
21
+ requirements:
22
+ - - ~>
23
+ - !ruby/object:Gem::Version
24
+ version: 0.7.4
25
+ type: :runtime
26
+ version_requirements: *id001
27
+ - !ruby/object:Gem::Dependency
28
+ name: minitest
29
+ prerelease: false
30
+ requirement: &id002 !ruby/object:Gem::Requirement
31
+ none: false
32
+ requirements:
33
+ - - "="
34
+ - !ruby/object:Gem::Version
35
+ version: 2.0.2
36
+ type: :development
37
+ version_requirements: *id002
38
+ description: Google Data API for Ruby
39
+ email:
40
+ - jared@redningja.com
41
+ executables: []
42
+
43
+ extensions: []
44
+
45
+ extra_rdoc_files: []
46
+
47
+ files:
48
+ - .gemtest
49
+ - .gitignore
50
+ - Gemfile
51
+ - README.rdoc
52
+ - Rakefile
53
+ - lib/rgdata.rb
54
+ - lib/rgdata/api.rb
55
+ - lib/rgdata/apis/profiles.rb
56
+ - lib/rgdata/authentication.rb
57
+ - lib/rgdata/config.rb
58
+ - lib/rgdata/model.rb
59
+ - lib/rgdata/models/profile.rb
60
+ - lib/rgdata/version.rb
61
+ - lib/support/hash.rb
62
+ - mspec/api_spec.rb
63
+ - mspec/apis/api_spec.rb
64
+ - mspec/apis/profiles_spec.rb
65
+ - mspec/authentication_spec.rb
66
+ - mspec/config_spec.rb
67
+ - mspec/fixtures/profiles.xml
68
+ - mspec/models/profile_spec.rb
69
+ - mspec/spec_helper.rb
70
+ - rgdata.gemspec
71
+ has_rdoc: true
72
+ homepage: https://github.com/ordinaryzelig/rgdata
73
+ licenses: []
74
+
75
+ post_install_message:
76
+ rdoc_options: []
77
+
78
+ require_paths:
79
+ - lib
80
+ required_ruby_version: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ version: "0"
86
+ required_rubygems_version: !ruby/object:Gem::Requirement
87
+ none: false
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ version: "0"
92
+ requirements: []
93
+
94
+ rubyforge_project: rgdata
95
+ rubygems_version: 1.6.1
96
+ signing_key:
97
+ specification_version: 3
98
+ summary: Google Data API for Ruby
99
+ test_files: []
100
+