hayesdavis-grackle 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt ADDED
@@ -0,0 +1,4 @@
1
+ == 1.0.0 / 2009-03-22
2
+
3
+ * 1 major enhancement
4
+ * Birthday!
data/README.txt ADDED
@@ -0,0 +1,118 @@
1
+ grackle
2
+ by Hayes Davis
3
+ http://www.appozite.com
4
+ http://hayesdavis.net
5
+
6
+ == DESCRIPTION
7
+ Grackle is a lightweight Ruby wrapper around the Twitter REST and Search APIs. It's based on my experience using the
8
+ Twitter API to build http://cheaptweet.com. The main goal of Grackle is to never require a release when the Twitter
9
+ API changes (which it often does) or in the face of a particular Twitter API bug. As such it is somewhat different
10
+ from other Twitter API libraries. It does not try to hide the Twitter "methods" under an access layer nor does it
11
+ introduce concrete classes for the various objects returned by Twitter. Instead, calls to the Grackle client map
12
+ directly to Twitter API URLs. The objects returned by API calls are generated as OpenStructs on the fly and make no
13
+ assumptions about the presence or absence of any particular attributes. Taking this approach means that changes to
14
+ URLs used by Twitter, parameters required by those URLs or return values will not require a new release. It
15
+ will potentially require, however, some modifications to your code that uses Grackle.
16
+
17
+ ==USING GRACKLE
18
+
19
+ ===Creating a Grackle::Client
20
+ require 'grackle'
21
+ client = Grackle::Client(:username=>'your_user',:password=>'yourpass')
22
+
23
+ See Grackle::Client for more information about valid arguments to the constructor including for custom headers and SSL.
24
+
25
+ ===Grackle Method Syntax
26
+ Grackle uses a method syntax that corresponds to the Twitter API URLs with a few twists. Where you would have a slash in
27
+ a Twitter URL, that becomes a "." in a chained set of Grackle method calls. Each call in the method chain is used to build
28
+ Twitter URL path until a particular call is encountered which causes the request to be sent. Methods which will cause a
29
+ request to be execute include:
30
+ *A method call ending in "?" will cause an HTTP GET to be executed
31
+ *A method call ending in "!" will cause an HTTP POST to be executed
32
+ *If a valid format such as .json, .xml, .rss or .atom is encounted, a get will be executed with that format
33
+ *A format method can also include a ? or ! to determine GET or POST in that format respectively
34
+
35
+ ===GETting Data
36
+ Invoking the API method "/users/show" in XML format for the Twitter user "some_user" looks like
37
+ client.users.show.xml :id=>'some_user' #http://twitter.com/users/show.xml?id=some_user
38
+
39
+ Or using the JSON format:
40
+ client.users.show.json :id=>'some_user' #http://twitter.com/users/show.json?id=some_user
41
+
42
+ Or, since Twitter also allows certain ids to be part of their URLs, this works:
43
+ client.users.show.some_user.json #http://twitter.com/users/show/some_user.json
44
+
45
+ The client has a default format of :json so if you want to use the above call with the default format you can do:
46
+ client.users.show.some_user? #http://twitter.com/users/show/some_user.json
47
+
48
+ If you include a "?" at the end of any method name, that signals to the Grackle client that you want to execute an HTTP GET request.
49
+
50
+ ===POSTing data
51
+ To use Twitter API methods that require an HTTP POST, you need to end your method chain with a bang (!)
52
+
53
+ For example, to update the authenticated user's status using the XML format:
54
+ client.statuses.update.xml! :status=>'this status is from grackle' #POST to http://twitter.com/statuses/update.xml
55
+
56
+ Or, with JSON
57
+ client.statuses.update.json! :status=>'this status is from grackle' #POST to http://twitter.com/statuses/update.json
58
+
59
+ Or, using the default format
60
+ client.statuses.update! :status=>'this status is from grackle' #POST to http://twitter.com/statuses/update.json
61
+
62
+ ===Search
63
+ Search works the same as everything else but your queries get submitted to search.twitter.com
64
+ client.search? :q=>'grackle' #http://search.twitter.com/search.json?q=grackle
65
+
66
+ ===Parameter handling
67
+ *All parameters are URL encoded as necessary.
68
+ *If you use a File object as a parameter it will be POSTed to Twitter in a multipart request.
69
+ *If you use a Time object as a parameter, .httpdate will be called on it and that value will be used
70
+
71
+ ===Return Values
72
+ Regardless of the format used, Grackle returns a Grackle::TwitterStruct (which is mostly just an OpenStruct) of data. The attributes
73
+ available on these structs correspond to the data returned by Twitter.
74
+
75
+ ===Dealing with Errors
76
+ If the request to Twitter does not return a status code of 200, then a TwitterError is thrown. This contains the HTTP method used,
77
+ the full request URI, the response status, the response body in text and a response object build by parsing the formatted error
78
+ returned by Twitter. It's a good idea to wrap your API calls with rescue clauses for Grackle::TwitterError.
79
+
80
+ ===Formats
81
+ Twitter allows you to request data in particular formats. Grackle currently supports JSON and XML. The Grackle::Client has a
82
+ default_format you can specify. By default, the default_format is :json. If you don't include a named format in your method
83
+ chain as described above, but use a "?" or "!" then the Grackle::Client.default_format is used.
84
+
85
+ == REQUIREMENTS:
86
+
87
+ You'll need the following gems to use all features of Grackle:
88
+ *json_pure
89
+ *httpclient
90
+
91
+ == INSTALL:
92
+
93
+ sudo gem install grackle
94
+
95
+ == LICENSE:
96
+
97
+ (The MIT License)
98
+
99
+ Copyright (c) 2009
100
+
101
+ Permission is hereby granted, free of charge, to any person obtaining
102
+ a copy of this software and associated documentation files (the
103
+ 'Software'), to deal in the Software without restriction, including
104
+ without limitation the rights to use, copy, modify, merge, publish,
105
+ distribute, sublicense, and/or sell copies of the Software, and to
106
+ permit persons to whom the Software is furnished to do so, subject to
107
+ the following conditions:
108
+
109
+ The above copyright notice and this permission notice shall be
110
+ included in all copies or substantial portions of the Software.
111
+
112
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
113
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
114
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
115
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
116
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
117
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
118
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,35 @@
1
+ # Look in the tasks/setup.rb file for the various options that can be
2
+ # configured in this Rakefile. The .rake files in the tasks directory
3
+ # are where the options are used.
4
+
5
+ begin
6
+ require 'bones'
7
+ Bones.setup
8
+ rescue LoadError
9
+ begin
10
+ load 'tasks/setup.rb'
11
+ rescue LoadError
12
+ raise RuntimeError, '### please install the "bones" gem ###'
13
+ end
14
+ end
15
+
16
+ ensure_in_path 'lib'
17
+ require 'grackle'
18
+
19
+ task :default => 'spec:run'
20
+
21
+ PROJ.name = 'grackle'
22
+ PROJ.authors = 'Hayes Davis'
23
+ PROJ.email = 'hayes@appozite.com'
24
+ PROJ.url = 'http://github.com/hayesdavis/grackle'
25
+ PROJ.version = Grackle::VERSION
26
+ PROJ.rubyforge.name = 'grackle'
27
+ PROJ.summary = 'Grackle is a library for the Twitter REST and Search API'
28
+ PROJ.description = 'Grackle is a library for the Twitter REST and Search API that aims to go with the flow.'
29
+ PROJ.spec.opts << '--color'
30
+ PROJ.exclude = %w(.git pkg)
31
+
32
+ depend_on 'json'
33
+ depend_on 'httpclient'
34
+
35
+ # EOF
data/bin/grackle ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.expand_path(
4
+ File.join(File.dirname(__FILE__), %w[.. lib grackle]))
5
+
6
+ # Put your code here
7
+
8
+ # EOF
data/grackle.gemspec ADDED
@@ -0,0 +1,39 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{grackle}
5
+ s.version = "0.0.1"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Hayes Davis"]
9
+ s.date = %q{2009-03-22}
10
+ s.description = %q{Grackle is a library for the Twitter REST and Search API that aims to go with the flow.}
11
+ s.email = %q{hayes@appozite.com}
12
+ s.files = ["History.txt", "README.txt", "Rakefile", "bin/grackle", "grackle.gemspec", "lib/grackle.rb", "lib/grackle/client.rb", "lib/grackle/handlers.rb", "lib/grackle/transport.rb", "lib/grackle/utils.rb", "spec/grackle_spec.rb", "spec/spec_helper.rb", "test/test_grackle.rb"]
13
+ s.has_rdoc = true
14
+ s.homepage = %q{http://github.com/hayesdavis/grackle}
15
+ s.rdoc_options = ["--inline-source", "--charset=UTF-8"]
16
+ s.require_paths = ["lib"]
17
+ s.rubyforge_project = %q{grackle}
18
+ s.rubygems_version = %q{1.3.1}
19
+ s.summary = %q{Grackle is a library for the Twitter REST and Search API}
20
+
21
+ if s.respond_to? :specification_version then
22
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
23
+ s.specification_version = 2
24
+
25
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
26
+ s.add_runtime_dependency(%q<json>, [">= 0"])
27
+ s.add_runtime_dependency(%q<httpclient>, [">= 2.1.4"])
28
+ s.add_development_dependency(%q<bones>, [">= 2.4.2"])
29
+ else
30
+ s.add_dependency(%q<json>, [">= 0"])
31
+ s.add_dependency(%q<httpclient>, [">= 2.1.4"])
32
+ s.add_dependency(%q<bones>, [">= 2.4.2"])
33
+ end
34
+ else
35
+ s.add_dependency(%q<json>, [">= 0"])
36
+ s.add_dependency(%q<httpclient>, [">= 2.1.4"])
37
+ s.add_dependency(%q<bones>, [">= 2.4.2"])
38
+ end
39
+ end
data/lib/grackle.rb ADDED
@@ -0,0 +1,27 @@
1
+ module Grackle
2
+
3
+ # :stopdoc:
4
+ VERSION = '0.0.1'
5
+ LIBPATH = ::File.expand_path(::File.dirname(__FILE__)) + ::File::SEPARATOR
6
+ PATH = ::File.dirname(LIBPATH) + ::File::SEPARATOR
7
+ # :startdoc:
8
+
9
+ # Returns the version string for the library.
10
+ def self.version
11
+ VERSION
12
+ end
13
+
14
+ end # module Grackle
15
+
16
+ $:.unshift File.dirname(__FILE__)
17
+
18
+ require 'ostruct'
19
+ require 'open-uri'
20
+ require 'net/http'
21
+ require 'rexml/document'
22
+ require 'json'
23
+
24
+ require 'grackle/utils'
25
+ require 'grackle/transport'
26
+ require 'grackle/handlers'
27
+ require 'grackle/client'
@@ -0,0 +1,153 @@
1
+ module Grackle
2
+
3
+ class TwitterStruct < OpenStruct
4
+ attr_accessor :id
5
+ end
6
+
7
+ class TwitterError < StandardError
8
+ attr_accessor :method, :request_uri, :status, :response_body, :response_object
9
+
10
+ def initialize(method, request_uri, status, response_body)
11
+ self.method = method
12
+ self.request_uri = request_uri
13
+ self.status = status
14
+ self.response_body = response_body
15
+ super("#{self.method} #{self.request_uri} => #{self.status}: #{self.response_body}")
16
+ end
17
+ end
18
+
19
+ class Client
20
+
21
+ class Request
22
+ attr_accessor :path, :method
23
+
24
+ def method
25
+ @method ||= :get
26
+ end
27
+
28
+ def <<(path)
29
+ self.path << path
30
+ end
31
+
32
+ def path
33
+ @path ||= ''
34
+ end
35
+
36
+ def path?
37
+ path.length > 0
38
+ end
39
+ end
40
+
41
+ VALID_METHODS = [:get,:post,:put,:delete]
42
+ VALID_FORMATS = [:json,:xml,:atom,:rss]
43
+
44
+ REST_API_DOMAIN = 'twitter.com'
45
+ SEARCH_API_DOMAIN = 'search.twitter.com'
46
+
47
+ attr_accessor :username, :password, :handlers, :default_format, :headers, :ssl, :transport, :request
48
+
49
+ # Arguments (all are optional):
50
+ # :username - twitter username to authenticate with
51
+ # :password - twitter password to authenticate with
52
+ # :handlers - Hash of formats to Handler instances (e.g. {:json=>CustomJSONHandler.new})
53
+ # :default_format - Symbol of format to use when no format is specified in an API call (e.g. :json)
54
+ # :headers - Hash of string keys and values for headers to pass in the HTTP request to twitter
55
+ # :ssl - true or false to turn SSL on or off. Default is off (i.e. http://)
56
+ def initialize(options={})
57
+ self.transport = Transport.new
58
+ self.username = options.delete(:username)
59
+ self.password = options.delete(:password)
60
+ self.handlers = {:json=>Handlers::JSONHandler.new,:xml=>Handlers::XMLHandler.new,:unknown=>Handlers::StringHandler.new}
61
+ self.handlers.merge!(options[:handlers]||{})
62
+ self.default_format = options[:default_format] || :json
63
+ self.headers = {'User-Agent'=>'Grackle/1.0'}.merge!(options[:headers]||{})
64
+ self.ssl = options[:ssl] == true
65
+ end
66
+
67
+ def method_missing(name,*args)
68
+ #Check for HTTP method and apply it to the request.
69
+ #Can use this for an explict HTTP method
70
+ if http_method_invocation?(name)
71
+ self.request.method = name
72
+ return self
73
+ end
74
+ #If method is a format name, execute using that format
75
+ if format_invocation?(name)
76
+ return call_with_format(name,*args)
77
+ end
78
+ #If method ends in ! or ? use that to determine post or get
79
+ if name.to_s =~ /^(.*)(!|\?)$/
80
+ name = $1.to_sym
81
+ #! is a post, ? is a get
82
+ self.request.method = ($2 == '!' ? :post : :get)
83
+ if format_invocation?(name)
84
+ return call_with_format(name,*args)
85
+ else
86
+ self.request << "/#{$1}"
87
+ return call_with_format(self.default_format,*args)
88
+ end
89
+ end
90
+ #Else add to the request path
91
+ self.request << "/#{name}"
92
+ self
93
+ end
94
+
95
+ protected
96
+ def rest_api_domain
97
+ REST_API_DOMAIN
98
+ end
99
+
100
+ def search_api_domain
101
+ SEARCH_API_DOMAIN
102
+ end
103
+
104
+ def call_with_format(format,params={})
105
+ id = params.delete(:id)
106
+ self.request << "/#{id}" if id
107
+ self.request << ".#{format}"
108
+ url = "#{scheme}://#{request_host}#{self.request.path}"
109
+ req_info = self.request
110
+ self.request = nil
111
+ res = transport.request(
112
+ req_info.method,url,:username=>self.username,:password=>self.password,:headers=>headers,:params=>params
113
+ )
114
+ fmt_handler = handler(format)
115
+ unless res.status == 200
116
+ handle_error_response(res,fmt_handler)
117
+ else
118
+ fmt_handler.decode_response(res.body)
119
+ end
120
+ end
121
+
122
+ def request
123
+ @request ||= Request.new
124
+ end
125
+
126
+ def handler(format)
127
+ handlers[format] || handlers[:unknown]
128
+ end
129
+
130
+ def handle_error_response(res,handler)
131
+ err = TwitterError.new(res.method,res.request_uri,res.status,res.body)
132
+ err.response_object = handler.decode_response(err.response_body)
133
+ raise err
134
+ end
135
+
136
+ def http_method_invocation?(name)
137
+ !self.request.path? && VALID_METHODS.include?(name)
138
+ end
139
+
140
+ def format_invocation?(name)
141
+ self.request.path? && VALID_FORMATS.include?(name)
142
+ end
143
+
144
+ def request_host
145
+ self.request.path =~ /^\/search/ ? search_api_domain : rest_api_domain
146
+ end
147
+
148
+ def scheme
149
+ self.ssl ? 'https' :'http'
150
+ end
151
+
152
+ end
153
+ end
@@ -0,0 +1,90 @@
1
+ module Grackle
2
+
3
+ # This module contain handlers that know how to take a response body
4
+ # from Twitter and turn it into a TwitterStruct return value. Handlers are
5
+ # used by the Client to give back return values from API calls. A handler
6
+ # is intended to provide a +decode+ method which accepts the response body
7
+ # as a string.
8
+ module Handlers
9
+
10
+ # Decodes JSON Twitter API responses
11
+ class JSONHandler
12
+
13
+ def decode_response(res)
14
+ json_result = JSON.parse(res)
15
+ load_recursive(json_result)
16
+ end
17
+
18
+ private
19
+ def load_recursive(value)
20
+ if value.kind_of? Hash
21
+ build_struct(value)
22
+ elsif value.kind_of? Array
23
+ value.map{|v| load_recursive(v)}
24
+ else
25
+ value
26
+ end
27
+ end
28
+
29
+ def build_struct(hash)
30
+ struct = TwitterStruct.new
31
+ hash.each do |key,v|
32
+ struct.send("#{key}=",load_recursive(v))
33
+ end
34
+ struct
35
+ end
36
+
37
+ end
38
+
39
+ # Decodes XML Twitter API responses
40
+ class XMLHandler
41
+
42
+ #Known nodes returned by twitter that contain arrays
43
+ ARRAY_NODES = ['ids','statuses','users']
44
+
45
+ def decode_response(res)
46
+ xml = REXML::Document.new(res)
47
+ load_recursive(xml.root)
48
+ end
49
+
50
+ private
51
+ def load_recursive(node)
52
+ if array_node?(node)
53
+ node.elements.map {|e| load_recursive(e)}
54
+ elsif node.elements.size > 0
55
+ build_struct(node)
56
+ elsif node.elements.size == 0
57
+ value = node.text
58
+ fixnum?(value) ? value.to_i : value
59
+ end
60
+ end
61
+
62
+ def build_struct(node)
63
+ ts = TwitterStruct.new
64
+ node.elements.each do |e|
65
+ ts.send("#{e.name}=",load_recursive(e))
66
+ end
67
+ ts
68
+ end
69
+
70
+ # Most of the time Twitter specifies nodes that contain an array of
71
+ # sub-nodes with a type="array" attribute. There are some nodes that
72
+ # they dont' do that for, though, including the <ids> node returned
73
+ # by the social graph methods. This method tries to work in both situations.
74
+ def array_node?(node)
75
+ node.attributes['type'] == 'array' || ARRAY_NODES.include?(node.name)
76
+ end
77
+
78
+ def fixnum?(value)
79
+ value =~ /^\d+$/
80
+ end
81
+ end
82
+
83
+ # Just echoes back the response body. This is primarily used for unknown formats
84
+ class StringHandler
85
+ def decode_response(res)
86
+ res
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,137 @@
1
+ module Grackle
2
+
3
+ class Response
4
+ attr_accessor :method, :request_uri, :status, :body
5
+
6
+ def initialize(method,request_uri,status,body)
7
+ self.method = method
8
+ self.request_uri = request_uri
9
+ self.status = status
10
+ self.body = body
11
+ end
12
+ end
13
+
14
+ class Transport
15
+
16
+ def get(string_url,options={})
17
+ request(:get,url,options)
18
+ end
19
+
20
+ def post(string_url,options={})
21
+ request(:post,url,options)
22
+ end
23
+
24
+ def put(url,options={})
25
+ request(:put,url,options)
26
+ end
27
+
28
+ def delete(url,options={})
29
+ request(:delete,url,options)
30
+ end
31
+
32
+ def req_class(method)
33
+ case method
34
+ when :get then Net::HTTP::Get
35
+ when :post then Net::HTTP::Post
36
+ when :put then Net::HTTP::Put
37
+ when :delete then Net::HTTP::Delete
38
+ end
39
+ end
40
+
41
+ def request(method, string_url, options={})
42
+ params = stringify_params(options[:params])
43
+ if method == :get && params
44
+ string_url << query_string(params)
45
+ end
46
+ url = URI.parse(string_url)
47
+ begin
48
+ if file_param?(options[:params])
49
+ request_multipart(method,url,options)
50
+ else
51
+ request_standard(method,url,options)
52
+ end
53
+ rescue Timeout::Error
54
+ raise "Timeout while #{req.method}ing #{url.to_s}"
55
+ end
56
+ end
57
+
58
+ def request_multipart(method, url, options={})
59
+ require 'httpclient' unless defined? HTTPClient
60
+ client = HTTPClient.new
61
+ if options[:username] && options[:password]
62
+ client.set_auth(url.to_s,options.delete(:username),options.delete(:password))
63
+ end
64
+ res = client.request(method,url.to_s,nil,options[:params],options[:headers])
65
+ Response.new(method,url.to_s,res.status,res.content)
66
+ end
67
+
68
+ def request_standard(method,url,options={})
69
+ Net::HTTP.new(url.host, url.port).start do |http|
70
+ req = req_class(method).new(url.request_uri)
71
+ add_headers(req,options[:headers])
72
+ add_form_data(req,options[:params])
73
+ add_basic_auth(req,options[:username],options[:password])
74
+ res = http.request(req)
75
+ Response.new(method,url.to_s,res.code.to_i,res.body)
76
+ end
77
+ end
78
+
79
+ def query_string(params)
80
+ query = case params
81
+ when Hash then params.map{|key,value| url_encode_param(key,value) }.join("&")
82
+ else url_encode(params.to_s)
83
+ end
84
+ if !(query == nil || query.length == 0) && query[0,1] != '?'
85
+ query = "?#{query}"
86
+ end
87
+ query
88
+ end
89
+
90
+ private
91
+ def stringify_params(params)
92
+ return nil unless params
93
+ params.inject({}) do |h, pair|
94
+ key, value = pair
95
+ if value.respond_to? :httpdate
96
+ value = value.httpdate
97
+ end
98
+ h[key] = value
99
+ h
100
+ end
101
+ end
102
+
103
+ def file_param?(params)
104
+ return false unless params
105
+ params.any? {|key,value| value.respond_to? :read }
106
+ end
107
+
108
+ def url_encode(value)
109
+ require 'cgi' unless defined?(CGI) && defined?(CGI::escape)
110
+ CGI.escape(value.to_s)
111
+ end
112
+
113
+ def url_encode_param(key,value)
114
+ "#{url_encode(key)}=#{url_encode(value)}"
115
+ end
116
+
117
+ def add_headers(req,headers)
118
+ if headers
119
+ headers.each do |header, value|
120
+ req[header] = value
121
+ end
122
+ end
123
+ end
124
+
125
+ def add_form_data(req,params)
126
+ if req.request_body_permitted? && params
127
+ req.set_form_data(params)
128
+ end
129
+ end
130
+
131
+ def add_basic_auth(req,username,password)
132
+ if username && password
133
+ req.basic_auth(username,password)
134
+ end
135
+ end
136
+ end
137
+ end
@@ -0,0 +1,16 @@
1
+ module Grackle
2
+ module Utils
3
+
4
+ VALID_PROFILE_IMAGE_SIZES = [:bigger,:normal,:mini]
5
+
6
+ #Easy method for getting different sized profile images using Twitter's naming scheme
7
+ def profile_image_url(url,size=:normal)
8
+ size = VALID_PROFILE_IMAGE_SIZES.find(:normal){|s| s == size.to_sym}
9
+ return url if url.nil? || size == :normal
10
+ url.sub(/_normal\./,"_#{size.to_s}.")
11
+ end
12
+
13
+ module_function :profile_image_url
14
+
15
+ end
16
+ end
@@ -0,0 +1,7 @@
1
+
2
+ require File.join(File.dirname(__FILE__), %w[spec_helper])
3
+
4
+ describe Grackle do
5
+ end
6
+
7
+ # EOF
@@ -0,0 +1,16 @@
1
+
2
+ require File.expand_path(
3
+ File.join(File.dirname(__FILE__), %w[.. lib grackle]))
4
+
5
+ Spec::Runner.configure do |config|
6
+ # == Mock Framework
7
+ #
8
+ # RSpec uses it's own mocking framework by default. If you prefer to
9
+ # use mocha, flexmock or RR, uncomment the appropriate line:
10
+ #
11
+ # config.mock_with :mocha
12
+ # config.mock_with :flexmock
13
+ # config.mock_with :rr
14
+ end
15
+
16
+ # EOF
File without changes
metadata ADDED
@@ -0,0 +1,95 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hayesdavis-grackle
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Hayes Davis
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-03-22 00:00:00 -07: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: "0"
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: httpclient
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 2.1.4
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: bones
37
+ type: :development
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 2.4.2
44
+ version:
45
+ description: Grackle is a library for the Twitter REST and Search API that aims to go with the flow.
46
+ email: hayes@appozite.com
47
+ executables: []
48
+
49
+ extensions: []
50
+
51
+ extra_rdoc_files: []
52
+
53
+ files:
54
+ - History.txt
55
+ - README.txt
56
+ - Rakefile
57
+ - bin/grackle
58
+ - grackle.gemspec
59
+ - lib/grackle.rb
60
+ - lib/grackle/client.rb
61
+ - lib/grackle/handlers.rb
62
+ - lib/grackle/transport.rb
63
+ - lib/grackle/utils.rb
64
+ - spec/grackle_spec.rb
65
+ - spec/spec_helper.rb
66
+ - test/test_grackle.rb
67
+ has_rdoc: true
68
+ homepage: http://github.com/hayesdavis/grackle
69
+ post_install_message:
70
+ rdoc_options:
71
+ - --inline-source
72
+ - --charset=UTF-8
73
+ require_paths:
74
+ - lib
75
+ required_ruby_version: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ version: "0"
80
+ version:
81
+ required_rubygems_version: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ version: "0"
86
+ version:
87
+ requirements: []
88
+
89
+ rubyforge_project: grackle
90
+ rubygems_version: 1.2.0
91
+ signing_key:
92
+ specification_version: 2
93
+ summary: Grackle is a library for the Twitter REST and Search API
94
+ test_files: []
95
+