rest-core 0.0.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -29,8 +29,8 @@ for any other REST APIs.
29
29
  ## REQUIREMENTS:
30
30
 
31
31
  * Tested with MRI (official ruby) 1.9.2, 1.8.7, and trunk
32
- * Tested with Rubinius (rbx) 1.2.3
33
- * Tested with JRuby 1.6.2
32
+ * Tested with Rubinius (rbx) 1.2
33
+ * Tested with JRuby 1.6
34
34
 
35
35
  ## INSTALLATION:
36
36
 
@@ -38,31 +38,53 @@ for any other REST APIs.
38
38
 
39
39
  Or if you want development version, put this in Gemfile:
40
40
 
41
- gem 'rest-core', :git => 'git://github.com/cardinalblue/rest-core.git'
41
+ gem 'rest-core', :git => 'git://github.com/cardinalblue/rest-core.git',
42
+ :submodules => true
42
43
 
43
- ## EXAMPLE:
44
+ ## Built-in Clients Example:
44
45
 
45
- RestCore::Builder.client('YourClient') do
46
+ require 'rest-core'
47
+
48
+ RestCore::Twitter.new.statuses('godfat') # get user tweets
49
+ RestCore::Github.new.get('users/godfat') # get user info
50
+
51
+ linkedin = RestCore::Linkedin.new(:consumer_key => '...',
52
+ :consumer_secret => '...')
53
+ linkedin.authorize_url! # copy and paste the URL in browser to authorize
54
+ linkedin.authorize!('..') # paste your code from browser
55
+ linkedin.me # get current user info
56
+
57
+ # below is exactly the same as [rest-graph][]
58
+ require 'rest-core/client/rest-graph'
59
+ RestGraph.new.get('4') # get user info
60
+
61
+ See [example][] for more complex examples.
62
+
63
+ [example]: https://github.com/cardinalblue/rest-core/tree/master/example
64
+
65
+ ## Build Your Own Clients Example:
66
+
67
+ require 'rest-core'
68
+
69
+ YourClient = RestCore::Builder.client do
46
70
  use DefaultSite , 'https://api.github.com/users/'
47
71
  use JsonDecode , true
48
72
  use CommonLogger, method(:puts)
49
- use Cache , {}, nil
73
+ use Cache , {}, 3600
50
74
  run RestClient
51
75
  end
52
76
 
53
77
  client = YourClient.new
54
- client.get('godfat')
55
- client.get('godfat')
78
+ client.get('godfat') # cache miss
79
+ client.get('godfat') # cache hit
56
80
 
57
81
  client.site = 'http://github.com/api/v2/json/user/show/'
58
- client.get('godfat')
59
- client.get('godfat')
82
+ client.get('godfat') # cache miss
83
+ client.get('godfat') # cache hit
60
84
 
61
- See [example][] for more complex examples, and [build-in clients][] for even
62
- more complex examples.
85
+ See [built-in clients][] for more complex examples.
63
86
 
64
- [example]: https://github.com/cardinalblue/rest-core/tree/master/example
65
- [build-in clients]: https://github.com/cardinalblue/rest-core/tree/master/lib/rest-core/client
87
+ [built-in clients]: https://github.com/cardinalblue/rest-core/tree/master/lib/rest-core/client
66
88
 
67
89
  ## LICENSE:
68
90
 
data/TODO.md CHANGED
@@ -7,11 +7,9 @@
7
7
 
8
8
  ## medium
9
9
 
10
- * namespace issue
11
10
  * what about async request? yield callback? coroutine?
12
11
  * dependency?
13
12
 
14
13
  ## low
15
14
 
16
- * config loader
17
15
  * test utility
@@ -1,145 +1,9 @@
1
1
 
2
- # simple client
2
+ require 'rest-core/client/rest-graph'
3
3
 
4
- require 'rest-core'
4
+ p RestGraph.new.get('4') # get user info
5
5
 
6
- RestCore::Builder.client('Facebook', :data, :app_id, :secret, :old_site) do
7
- s = self.class # this is only for ruby 1.8!
8
- use s::Timeout , 10
9
-
10
- use s::DefaultSite , 'https://graph.facebook.com/'
11
- use s::DefaultHeaders, {'Accept' => 'application/json',
12
- 'Accept-Language' => 'en-us'}
13
- use s::Oauth2Query , 'access_token', nil
14
-
15
- use s::CommonLogger , method(:puts)
16
- use s::Cache , {}, nil
17
- use s::ErrorHandler , lambda{ |env| raise ::Facebook::Error.call(env) }
18
- use s::ErrorDetector , lambda{ |env| env[s::RESPONSE_BODY]['error'] ||
19
- env[s::RESPONSE_BODY]['error_code'] }
20
- use s::JsonDecode , true
21
-
22
- use s::Defaults , :data => lambda{{}},
23
- :old_site => 'https://api.facebook.com/'
24
-
25
- run s::RestClient
26
- end
27
-
28
- class Facebook::Error < RuntimeError
29
- include RestCore
30
-
31
- attr_reader :error, :url
32
- def initialize error, url=''
33
- @error, @url = error, url
34
- super("#{error.inspect} from #{url}")
35
- end
36
-
37
- def self.call env
38
- error, url = env[RESPONSE_BODY], Middleware.request_uri(env)
39
- new(error, url)
40
- end
41
- end
42
-
43
- module Facebook::Client
44
- include RestCore
45
-
46
- def oauth_token
47
- data['access_token'] || data['oauth_token'] if data.kind_of?(Hash)
48
- end
49
- def oauth_token= token
50
- data['access_token'] = token if data.kind_of?(Hash)
51
- end
52
- alias_method :access_token , :oauth_token
53
- alias_method :access_token=, :oauth_token=
54
-
55
- def secret_oauth_token ; "#{app_id}|#{secret}" ; end
56
- alias_method :secret_access_token, :secret_oauth_token
57
-
58
- def accept ; headers['Accept'] ; end
59
- def accept= val; headers['Accept'] = val; end
60
- def lang ; headers['Accept-Language'] ; end
61
- def lang= val; headers['Accept-Language'] = val; end
62
-
63
- def authorized? ; !!oauth_token ; end
64
-
65
- # cookies, app_id, secrect related below
66
-
67
- def parse_fbs! fbs
68
- self.data = check_sig_and_return_data(
69
- # take out facebook sometimes there but sometimes not quotes in cookies
70
- Rack::Utils.parse_query(fbs.to_s.sub(/^"/, '').sub(/"$/, '')))
71
- end
72
-
73
- def fbs
74
- "#{fbs_without_sig(data).join('&')}&sig=#{calculate_sig(data)}"
75
- end
76
-
77
- # facebook's new signed_request...
78
-
79
- def parse_signed_request! request
80
- sig_encoded, json_encoded = request.split('.')
81
- sig, json = [sig_encoded, json_encoded].map{ |str|
82
- "#{str.tr('-_', '+/')}==".unpack('m').first
83
- }
84
- self.data = check_sig_and_return_data(
85
- JsonDecode.json_decode(json).merge('sig' => sig)){
86
- Hmac.sha256(secret, json_encoded)
87
- }
88
- rescue JsonDecode::ParseError
89
- self.data = nil
90
- end
91
-
92
- # oauth related
93
-
94
- def authorize_url opts={}
95
- url('oauth/authorize',
96
- {:client_id => app_id, :access_token => nil}.merge(opts))
97
- end
98
-
99
- def authorize! opts={}
100
- query = {:client_id => app_id, :client_secret => secret}.merge(opts)
101
- self.data = Rack::Utils.parse_query(
102
- request({:auto_decode => false}.merge(opts),
103
- [:get, url('oauth/access_token', query)]))
104
- end
105
-
106
- # old rest facebook api, i will definitely love to remove them someday
107
-
108
- def old_rest path, query={}, opts={}, &cb
109
- uri = url("method/#{path}", {:format => 'json'}.merge(query),
110
- {:site => old_site}.merge(opts))
111
- request(opts, [:get, uri], &cb)
112
- end
113
-
114
- def secret_old_rest path, query={}, opts={}, &cb
115
- old_rest(path, query, {:secret => true}.merge(opts), &cb)
116
- end
117
-
118
- def fql code, query={}, opts={}, &cb
119
- old_rest('fql.query', {:query => code}.merge(query), opts, &cb)
120
- end
121
-
122
- def fql_multi codes, query={}, opts={}, &cb
123
- old_rest('fql.multiquery',
124
- {:queries => JsonDecode.json_encode(codes)}.merge(query), opts, &cb)
125
- end
126
-
127
- protected
128
- def check_sig_and_return_data cookies
129
- cookies if secret && if block_given?
130
- yield
131
- else
132
- calculate_sig(cookies)
133
- end == cookies['sig']
134
- end
135
-
136
- def calculate_sig cookies
137
- Digest::MD5.hexdigest(fbs_without_sig(cookies).join + secret)
138
- end
139
-
140
- def fbs_without_sig cookies
141
- cookies.reject{ |(k, v)| k == 'sig' }.sort.map{ |a| a.join('=') }
142
- end
143
- end
144
-
145
- Facebook.send(:include, Facebook::Client)
6
+ facebook = RestGraph.new(:app_id => '...', :secret => '...')
7
+ facebook.authorize_url # copy and paste the URL in browser to authorize
8
+ facebook.authorize!(:redirect_uri => '...', :code => '...')
9
+ p facebook.get('me')
@@ -1,21 +1,4 @@
1
1
 
2
- # simple client
3
-
4
2
  require 'rest-core'
5
3
 
6
- RestCore::Builder.client('Github') do
7
- s = self.class # this is only for ruby 1.8!
8
- use s::Timeout , 10
9
-
10
- use s::DefaultSite , 'https://api.github.com/'
11
- use s::Oauth2Query , 'access_token', nil
12
-
13
- use s::CommonLogger , method(:puts)
14
- use s::Cache , {}, nil
15
- use s::ErrorHandler , lambda{|env| raise env[s::RESPONSE_BODY]['message']}
16
- use s::ErrorDetector, lambda{|env| env[s::RESPONSE_HEADERS]['status'].
17
- first !~ /^2/}
18
- use s::JsonDecode , true
19
-
20
- run s::RestClient
21
- end
4
+ p RestCore::Github.new.get('users/godfat') # get user info
@@ -0,0 +1,8 @@
1
+
2
+ require 'rest-core'
3
+
4
+ linkedin = RestCore::Linkedin.new(:consumer_key => '...',
5
+ :consumer_secret => '...')
6
+ linkedin.authorize_url! # copy and paste the URL in browser to authorize
7
+ linkedin.authorize!('..') # paste your code from browser
8
+ p linkedin.me # get current user info
@@ -0,0 +1,12 @@
1
+
2
+ require 'rest-core'
3
+ RestCore::Twitter.new.statuses('godfat') # get user tweets
4
+
5
+ twitter = RestCore::Twitter.new(:consumer_key => '...',
6
+ :consumer_secret => '...')
7
+ twitter.authorize_url! # copy and paste the URL in browser to authorize
8
+ twitter.authorize!('..') # paste your code from browser
9
+ p twitter.tweet('hi!') # tweet for the current user
10
+
11
+ p twitter.post('statuses/update_with_media.json',
12
+ :status => 'hi!', 'media[]' => File.open('...'))
@@ -45,4 +45,9 @@ module RestCore
45
45
  # apps
46
46
  autoload :Ask , 'rest-core/app/ask'
47
47
  autoload :RestClient , 'rest-core/app/rest-client'
48
+
49
+ # clients
50
+ autoload :Github , 'rest-core/client/github'
51
+ autoload :Twitter , 'rest-core/client/twitter'
52
+ autoload :Linkedin , 'rest-core/client/linkedin'
48
53
  end
@@ -6,16 +6,15 @@ class RestCore::Builder
6
6
  include RestCore
7
7
  include Wrapper
8
8
 
9
- def self.client prefix, *attrs, &block
10
- new(&block).to_client(prefix, *attrs)
9
+ def self.client *attrs, &block
10
+ new(&block).to_client(*attrs)
11
11
  end
12
12
 
13
- def to_client prefix, *attrs
13
+ def to_client *attrs
14
14
  # struct = Struct.new(*members, *attrs) if RUBY_VERSION >= 1.9.2
15
15
  struct = Struct.new(*(members + attrs))
16
16
  client = Class.new(struct)
17
17
  client.send(:include, Client)
18
- Object.const_set( prefix , client)
19
18
  client.const_set('Struct', struct)
20
19
  class << client; attr_reader :builder; end
21
20
  client.instance_variable_set(:@builder, self)
@@ -19,13 +19,15 @@ module RestCore::Client
19
19
  end
20
20
 
21
21
  def default_#{name} app=app
22
- if app.respond_to?(:#{name})
22
+ if app.respond_to?(:#{name}) # instance value
23
23
  app.#{name}({})
24
- elsif app.respond_to?(:wrapped)
25
- default_#{name}(app.wrapped) ||
24
+ elsif app.respond_to?(:wrapped) # wrapper value
25
+ default_#{name}(app.wrapped) || # walk into it
26
26
  default_#{name}(app.app)
27
- elsif app.respond_to?(:app)
27
+ elsif app.respond_to?(:app) # walk into next app
28
28
  default_#{name}(app.app)
29
+ elsif self.class.respond_to?("default_#{name}")
30
+ self.class.default_#{name} # old class default style
29
31
  else
30
32
  nil
31
33
  end
@@ -1,5 +1,7 @@
1
1
 
2
- RestCore::Builder.client('Github') do
2
+ require 'rest-core'
3
+
4
+ RestCore::Github = RestCore::Builder.client do
3
5
  s = self.class # this is only for ruby 1.8!
4
6
  use s::Timeout , 10
5
7
 
@@ -8,7 +10,7 @@ RestCore::Builder.client('Github') do
8
10
  use s::Oauth2Query , 'access_token', nil
9
11
 
10
12
  use s::CommonLogger , lambda{|obj|obj}
11
- use s::Cache , {}, nil do
13
+ use s::Cache , {}, 3600 do
12
14
  use s::ErrorHandler , lambda{|env| raise env[s::RESPONSE_BODY]['message']}
13
15
  use s::ErrorDetectorHttp
14
16
  use s::JsonDecode , true
@@ -1,5 +1,5 @@
1
1
 
2
- RestCore::Builder.client('Linkedin', :data) do
2
+ RestCore::Linkedin = RestCore::Builder.client(:data) do
3
3
  s = self.class # this is only for ruby 1.8!
4
4
  use s::Timeout , 10
5
5
 
@@ -13,7 +13,7 @@ RestCore::Builder.client('Linkedin', :data) do
13
13
 
14
14
  use s::CommonLogger , method(:puts)
15
15
 
16
- use s::Cache , {}, nil do
16
+ use s::Cache , {}, 3600 do
17
17
  use s::ErrorHandler , lambda{|env|
18
18
  if (body = env[s::RESPONSE_BODY]).kind_of?(Hash)
19
19
  raise body['message']
@@ -31,27 +31,31 @@ RestCore::Builder.client('Linkedin', :data) do
31
31
  run s::RestClient
32
32
  end
33
33
 
34
- module Linkedin::Client
34
+ module RestCore::Linkedin::Client
35
35
  include RestCore
36
36
 
37
37
  def oauth_token
38
38
  data['oauth_token'] if data.kind_of?(Hash)
39
39
  end
40
- def oauth_token= token
40
+ def oauth_token= token
41
41
  data['oauth_token'] = token if data.kind_of?(Hash)
42
42
  end
43
43
  def oauth_token_secret
44
44
  data['oauth_token_secret'] if data.kind_of?(Hash)
45
45
  end
46
- def oauth_token_secret= secret
46
+ def oauth_token_secret= secret
47
47
  data['oauth_token_secret'] = secret if data.kind_of?(Hash)
48
48
  end
49
49
 
50
+ def me queries={}, opts={}
51
+ get('v1/people/~', queries, opts)
52
+ end
53
+
50
54
  private
51
55
  def set_token query
52
56
  self.data = query
53
57
  end
54
58
  end
55
59
 
56
- Linkedin.send(:include, RestCore::ClientOauth1)
57
- Linkedin.send(:include, Linkedin::Client)
60
+ RestCore::Linkedin.send(:include, RestCore::ClientOauth1)
61
+ RestCore::Linkedin.send(:include, RestCore::Linkedin::Client)
@@ -5,7 +5,7 @@ require 'rest-core/util/hmac'
5
5
  # optional gem
6
6
  begin; require 'rack'; rescue LoadError; end
7
7
 
8
- RestCore::Builder.client('RestGraph', :data, :app_id, :secret, :old_site) do
8
+ RestGraph = RestCore::Builder.client(:data, :app_id, :secret, :old_site) do
9
9
  s = self.class # this is only for ruby 1.8!
10
10
  use s::Timeout , 10
11
11
 
@@ -16,7 +16,7 @@ RestCore::Builder.client('RestGraph', :data, :app_id, :secret, :old_site) do
16
16
 
17
17
  use s::CommonLogger , lambda{|obj|obj}
18
18
 
19
- use s::Cache , {}, nil do
19
+ use s::Cache , {}, 3600 do
20
20
  use s::ErrorHandler , lambda{ |env| raise ::RestGraph::Error.call(env) }
21
21
  use s::ErrorDetector , lambda{ |env|
22
22
  if env[s::RESPONSE_BODY].kind_of?(Hash)
@@ -1,9 +1,11 @@
1
1
 
2
- RestCore::Builder.client('Twitter', :data) do
2
+ require 'rest-core'
3
+
4
+ RestCore::Twitter = RestCore::Builder.client(:data) do
3
5
  s = self.class # this is only for ruby 1.8!
4
6
  use s::Timeout , 10
5
7
 
6
- use s::DefaultSite , 'https://api.twitter.com/'
8
+ use s::DefaultSite , 'https://api.twitter.com/1/'
7
9
  use s::DefaultHeaders, {'Accept' => 'application/json'}
8
10
 
9
11
  use s::Oauth1Header ,
@@ -11,7 +13,7 @@ RestCore::Builder.client('Twitter', :data) do
11
13
 
12
14
  use s::CommonLogger , method(:puts)
13
15
 
14
- use s::Cache , {}, nil do
16
+ use s::Cache , {}, 3600 do
15
17
  use s::ErrorHandler , lambda{ |env|
16
18
  if (body = env[s::RESPONSE_BODY]).kind_of?(Hash)
17
19
  raise body['error']
@@ -29,24 +31,29 @@ RestCore::Builder.client('Twitter', :data) do
29
31
  run s::RestClient
30
32
  end
31
33
 
32
- module Twitter::Client
34
+ module RestCore::Twitter::Client
33
35
  include RestCore
34
36
 
35
37
  def oauth_token
36
38
  data['oauth_token'] if data.kind_of?(Hash)
37
39
  end
38
- def oauth_token= token
40
+ def oauth_token= token
39
41
  data['oauth_token'] = token if data.kind_of?(Hash)
40
42
  end
41
43
  def oauth_token_secret
42
44
  data['oauth_token_secret'] if data.kind_of?(Hash)
43
45
  end
44
- def oauth_token_secret= secret
46
+ def oauth_token_secret= secret
45
47
  data['oauth_token_secret'] = secret if data.kind_of?(Hash)
46
48
  end
47
49
 
48
- def tweet status, opt={}
49
- post('1/statuses/update.json', {:status => status}.merge(opt))
50
+ def tweet status, queries={}, opts={}
51
+ post('statuses/update.json',
52
+ {:status => status}.merge(queries), opts)
53
+ end
54
+
55
+ def statuses user, queries={}, opts={}
56
+ get('statuses/user_timeline.json', {:id => user}.merge(queries), opts)
50
57
  end
51
58
 
52
59
  private
@@ -55,5 +62,5 @@ module Twitter::Client
55
62
  end
56
63
  end
57
64
 
58
- Twitter.send(:include, RestCore::ClientOauth1)
59
- Twitter.send(:include, Twitter::Client)
65
+ RestCore::Twitter.send(:include, RestCore::ClientOauth1)
66
+ RestCore::Twitter.send(:include, RestCore::Twitter::Client)
@@ -55,8 +55,16 @@ class RestCore::Oauth1Header
55
55
  def base_string env, oauth_params
56
56
  method = env[REQUEST_METHOD].to_s.upcase
57
57
  base_uri = env[REQUEST_PATH]
58
+ # TODO: the detection should be checking if it's
59
+ # application/x-www-form-urlencoded or not, instead of multipart or not.
60
+ # but since the Content-Type is generated in app (http client),
61
+ # we have no idea what it would be here. so simply guessing it...
62
+ payload = if multipart?(env)
63
+ {}
64
+ else
65
+ reject_blank(env[REQUEST_PAYLOAD] || {})
66
+ end
58
67
  query = reject_blank(env[REQUEST_QUERY] || {})
59
- payload = reject_blank(env[REQUEST_PAYLOAD] || {})
60
68
  params = reject_blank(oauth_params.merge(query.merge(payload))).
61
69
  to_a.sort.map{ |(k, v)|
62
70
  "#{encode(k.to_s)}=#{encode(v.to_s)}"}.join('&')
@@ -68,6 +76,11 @@ class RestCore::Oauth1Header
68
76
  [OpenSSL::Random.random_bytes(32)].pack('m').tr("+/=\n", '')
69
77
  end
70
78
 
79
+ def multipart? env
80
+ !!(env[REQUEST_PAYLOAD] &&
81
+ env[REQUEST_PAYLOAD].find{ |k, v| v.kind_of?(IO) })
82
+ end
83
+
71
84
  def reject_blank params
72
85
  params.reject{ |k, v| v.nil? || v == false ||
73
86
  (v.respond_to?(:strip) &&
@@ -0,0 +1,49 @@
1
+
2
+ require 'erb'
3
+ require 'yaml'
4
+
5
+ require 'rest-core'
6
+
7
+ module RestCore::Config
8
+ extend self
9
+
10
+ DefaultModuleName = 'DefaultAttributes'
11
+
12
+ def load_for_rails klass, app=Rails
13
+ root = File.expand_path(app.root)
14
+ path = ["#{root}/config/rest-core.yaml", # YAML should use .yaml
15
+ "#{root}/config/rest-core.yml" ].find{|p| File.exist?(p)}
16
+ RestCore::Config.load(klass, path, app.env)
17
+ end
18
+
19
+ def load klass, path, env
20
+ return false if klass.const_defined?(DefaultModuleName)
21
+ RestCore::Config.load!(klass, path, env)
22
+ end
23
+
24
+ def load! klass, path, env
25
+ config = YAML.load(ERB.new(File.read(path)).result(binding))
26
+ defaults = config[env]
27
+ return false unless defaults
28
+ raise ArgumentError.new("#{defaults} is not a hash") unless
29
+ defaults.kind_of?(Hash)
30
+
31
+ mod = if klass.const_defined?(DefaultModuleName)
32
+ klass.const_get(DefaultModuleName)
33
+ else
34
+ m = Module.new
35
+ klass.send(:extend, m)
36
+ klass.send(:const_set, DefaultModuleName, m)
37
+ m
38
+ end
39
+
40
+ mod.module_eval(defaults.inject([]){ |r, (k, v)|
41
+ # quote strings, leave others free (e.g. false, numbers, etc)
42
+ r << <<-RUBY
43
+ def default_#{k}
44
+ #{v.kind_of?(String) ? "'#{v}'" : v}
45
+ end
46
+ RUBY
47
+ }.join, __FILE__, __LINE__)
48
+ end
49
+ end
@@ -1,4 +1,4 @@
1
1
 
2
2
  module RestCore
3
- VERSION = '0.0.1'
3
+ VERSION = '0.2.0'
4
4
  end
File without changes
@@ -2,13 +2,13 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{rest-core}
5
- s.version = "0.0.1"
5
+ s.version = "0.2.0"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = [
9
9
  %q{Cardinal Blue},
10
10
  %q{Lin Jen-Shin (godfat)}]
11
- s.date = %q{2011-08-16}
11
+ s.date = %q{2011-08-24}
12
12
  s.description = %q{A modular Ruby REST client collection/infrastructure.
13
13
 
14
14
  In this era of web services and mashups, we have seen a blooming of REST
@@ -25,11 +25,6 @@ for any other REST APIs.
25
25
  [rest-core]: http://github.com/cardinalblue/rest-core
26
26
  [rest-graph]: http://github.com/cardinalblue/rest-graph}
27
27
  s.email = [%q{dev (XD) cardinalblue.com}]
28
- s.extra_rdoc_files = [
29
- %q{CONTRIBUTORS},
30
- %q{LICENSE},
31
- %q{TODO.md},
32
- %q{CONTRIBUTORS}]
33
28
  s.files = [
34
29
  %q{.gitignore},
35
30
  %q{.gitmodules},
@@ -38,12 +33,13 @@ for any other REST APIs.
38
33
  %q{Gemfile},
39
34
  %q{LICENSE},
40
35
  %q{NOTE.md},
41
- %q{README},
42
36
  %q{README.md},
43
37
  %q{Rakefile},
44
38
  %q{TODO.md},
45
39
  %q{example/facebook.rb},
46
40
  %q{example/github.rb},
41
+ %q{example/linkedin.rb},
42
+ %q{example/twitter.rb},
47
43
  %q{lib/rest-core.rb},
48
44
  %q{lib/rest-core/app/ask.rb},
49
45
  %q{lib/rest-core/app/rest-client.rb},
@@ -69,55 +65,48 @@ for any other REST APIs.
69
65
  %q{lib/rest-core/middleware/oauth1_header.rb},
70
66
  %q{lib/rest-core/middleware/oauth2_query.rb},
71
67
  %q{lib/rest-core/middleware/timeout.rb},
68
+ %q{lib/rest-core/util/config.rb},
72
69
  %q{lib/rest-core/util/hmac.rb},
73
70
  %q{lib/rest-core/version.rb},
74
71
  %q{lib/rest-core/wrapper.rb},
75
- %q{lib/rest-graph/config_util.rb},
72
+ %q{pending/test_multi.rb},
73
+ %q{pending/test_test_util.rb},
76
74
  %q{rest-core.gemspec},
77
75
  %q{task/.gitignore},
78
76
  %q{task/gemgem.rb},
79
77
  %q{test/common.rb},
80
- %q{test/config/rest-graph.yaml},
81
- %q{test/pending/test_load_config.rb},
82
- %q{test/pending/test_multi.rb},
83
- %q{test/pending/test_test_util.rb},
78
+ %q{test/config/rest-core.yaml},
84
79
  %q{test/test_api.rb},
85
80
  %q{test/test_cache.rb},
86
81
  %q{test/test_default.rb},
87
82
  %q{test/test_error.rb},
88
83
  %q{test/test_handler.rb},
84
+ %q{test/test_load_config.rb},
89
85
  %q{test/test_misc.rb},
90
86
  %q{test/test_oauth.rb},
91
87
  %q{test/test_oauth1_header.rb},
92
88
  %q{test/test_old.rb},
93
89
  %q{test/test_page.rb},
94
90
  %q{test/test_parse.rb},
95
- %q{test/test_rest-graph.rb},
96
91
  %q{test/test_serialize.rb},
97
92
  %q{test/test_timeout.rb}]
98
93
  s.homepage = %q{https://github.com/cardinalblue/rest-core}
99
- s.rdoc_options = [
100
- %q{--main},
101
- %q{README}]
102
94
  s.require_paths = [%q{lib}]
103
95
  s.rubygems_version = %q{1.8.7}
104
96
  s.summary = %q{A modular Ruby REST client collection/infrastructure.}
105
97
  s.test_files = [
106
- %q{test/pending/test_load_config.rb},
107
- %q{test/pending/test_multi.rb},
108
- %q{test/pending/test_test_util.rb},
109
98
  %q{test/test_api.rb},
110
99
  %q{test/test_cache.rb},
111
100
  %q{test/test_default.rb},
112
101
  %q{test/test_error.rb},
113
102
  %q{test/test_handler.rb},
103
+ %q{test/test_load_config.rb},
114
104
  %q{test/test_misc.rb},
115
105
  %q{test/test_oauth.rb},
116
106
  %q{test/test_oauth1_header.rb},
117
107
  %q{test/test_old.rb},
118
108
  %q{test/test_page.rb},
119
109
  %q{test/test_parse.rb},
120
- %q{test/test_rest-graph.rb},
121
110
  %q{test/test_serialize.rb},
122
111
  %q{test/test_timeout.rb}]
123
112
 
@@ -12,17 +12,13 @@ module Gemgem
12
12
  s.authors = ['Lin Jen-Shin (godfat)']
13
13
  s.email = ['godfat (XD) godfat.org']
14
14
 
15
- description = File.read("#{Gemgem.dir}/README").
15
+ description = readme.
16
16
  match(/DESCRIPTION:\n\n(.+?)(?=\n\n[^\n]+:\n)/m)[1].
17
17
  lines.to_a
18
18
 
19
19
  s.description = description.join
20
20
  s.summary = description.first
21
21
 
22
- s.extra_rdoc_files = %w[CHANGES TODO CONTRIBUTORS LICENSE
23
- CHANGES.md TODO.md CONTRIBUTORS].select{ |f|
24
- File.exist?(f) }
25
- s.rdoc_options = %w[--main README]
26
22
  s.rubygems_version = Gem::VERSION
27
23
  s.date = Time.now.strftime('%Y-%m-%d')
28
24
  s.files = gem_files
@@ -34,6 +30,17 @@ module Gemgem
34
30
  spec
35
31
  end
36
32
 
33
+ def readme
34
+ path = %w[README.md README].find{ |name|
35
+ File.exist?("#{Gemgem.dir}/#{name}")
36
+ }
37
+ if path
38
+ File.read(path)
39
+ else
40
+ "DESCRIPTION:\n\n \n\nEND:\n"
41
+ end
42
+ end
43
+
37
44
  def gem_tag
38
45
  "#{spec.name}-#{spec.version}"
39
46
  end
@@ -153,7 +160,7 @@ task :test do
153
160
  Bacon.extend(Bacon::TestUnitOutput)
154
161
  Bacon.summary_on_exit
155
162
  $LOAD_PATH.unshift('lib')
156
- Dir['test/**/test_*.rb'].each{ |file| load file }
163
+ Dir['./test/**/test_*.rb'].each{ |file| require file[0..-4] }
157
164
  end
158
165
 
159
166
  desc 'Run tests with shell'
@@ -0,0 +1,42 @@
1
+
2
+ if respond_to?(:require_relative, true)
3
+ require_relative 'common'
4
+ else
5
+ require File.dirname(__FILE__) + '/common'
6
+ end
7
+
8
+ require 'rest-core/util/config'
9
+ ::Rails = Object.new
10
+
11
+ describe RestCore::Config do
12
+
13
+ before do
14
+ @klass = RestGraph.dup
15
+ end
16
+
17
+ after do
18
+ RR.verify
19
+ end
20
+
21
+ def check
22
+ @klass.default_app_id .should == 41829
23
+ @klass.default_secret .should == 'r41829'.reverse
24
+ @klass.default_auto_decode.should == false
25
+ @klass.default_lang .should == 'zh-tw'
26
+ end
27
+
28
+ should 'honor rails config' do
29
+ mock(Rails).env { 'test' }
30
+ mock(Rails).root{ File.dirname(__FILE__) }
31
+ RestCore::Config.load_for_rails(@klass)
32
+ check
33
+ end
34
+
35
+ should 'honor config' do
36
+ RestCore::Config.load(
37
+ @klass,
38
+ "#{File.dirname(__FILE__)}/config/rest-core.yaml",
39
+ 'test')
40
+ check
41
+ end
42
+ end
@@ -39,6 +39,19 @@ describe RestCore::Oauth1Header do
39
39
  'oauth_version%3D1.0')
40
40
  end
41
41
 
42
+ should 'not use payload in multipart request for base_string' do
43
+ @env = @env.merge(RestCore::REQUEST_PAYLOAD =>
44
+ {'file' => File.open(__FILE__)})
45
+ @auth.base_string(@env, @oauth_params).should.equal(
46
+ 'POST&https%3A%2F%2Fapi.twitter.com%2Foauth%2Frequest_token&' \
47
+ 'oauth_callback%3Dhttp%253A%252F%252Flocalhost%253A3005%252F' \
48
+ 'the_dance%252Fprocess_callback%253Fservice_provider_id%253D' \
49
+ '11%26oauth_consumer_key%3DGDdmIQH6jhtmLUypg82g%26oauth_nonc' \
50
+ 'e%3DQP70eNmVz8jvdPevU3oJD2AfF7R7odC2XJcn4XlZJqk%26oauth_sig' \
51
+ 'nature_method%3DHMAC-SHA1%26oauth_timestamp%3D1272323042%26' \
52
+ 'oauth_version%3D1.0')
53
+ end
54
+
42
55
  should 'have correct signature' do
43
56
  @auth.signature(@env, @oauth_params).should.equal(
44
57
  '8wUi7m5HFQy76nowoCThusfgB+Q=')
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rest-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,11 +10,11 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2011-08-16 00:00:00.000000000Z
13
+ date: 2011-08-24 00:00:00.000000000Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rest-client
17
- requirement: &2153518960 !ruby/object:Gem::Requirement
17
+ requirement: &2168876060 !ruby/object:Gem::Requirement
18
18
  none: false
19
19
  requirements:
20
20
  - - ! '>='
@@ -22,10 +22,10 @@ dependencies:
22
22
  version: '0'
23
23
  type: :development
24
24
  prerelease: false
25
- version_requirements: *2153518960
25
+ version_requirements: *2168876060
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: rack
28
- requirement: &2153518480 !ruby/object:Gem::Requirement
28
+ requirement: &2168875580 !ruby/object:Gem::Requirement
29
29
  none: false
30
30
  requirements:
31
31
  - - ! '>='
@@ -33,10 +33,10 @@ dependencies:
33
33
  version: '0'
34
34
  type: :development
35
35
  prerelease: false
36
- version_requirements: *2153518480
36
+ version_requirements: *2168875580
37
37
  - !ruby/object:Gem::Dependency
38
38
  name: yajl-ruby
39
- requirement: &2153518000 !ruby/object:Gem::Requirement
39
+ requirement: &2168875100 !ruby/object:Gem::Requirement
40
40
  none: false
41
41
  requirements:
42
42
  - - ! '>='
@@ -44,10 +44,10 @@ dependencies:
44
44
  version: '0'
45
45
  type: :development
46
46
  prerelease: false
47
- version_requirements: *2153518000
47
+ version_requirements: *2168875100
48
48
  - !ruby/object:Gem::Dependency
49
49
  name: json
50
- requirement: &2153517520 !ruby/object:Gem::Requirement
50
+ requirement: &2168874620 !ruby/object:Gem::Requirement
51
51
  none: false
52
52
  requirements:
53
53
  - - ! '>='
@@ -55,10 +55,10 @@ dependencies:
55
55
  version: '0'
56
56
  type: :development
57
57
  prerelease: false
58
- version_requirements: *2153517520
58
+ version_requirements: *2168874620
59
59
  - !ruby/object:Gem::Dependency
60
60
  name: json_pure
61
- requirement: &2153517040 !ruby/object:Gem::Requirement
61
+ requirement: &2168874140 !ruby/object:Gem::Requirement
62
62
  none: false
63
63
  requirements:
64
64
  - - ! '>='
@@ -66,10 +66,10 @@ dependencies:
66
66
  version: '0'
67
67
  type: :development
68
68
  prerelease: false
69
- version_requirements: *2153517040
69
+ version_requirements: *2168874140
70
70
  - !ruby/object:Gem::Dependency
71
71
  name: ruby-hmac
72
- requirement: &2153516560 !ruby/object:Gem::Requirement
72
+ requirement: &2168873660 !ruby/object:Gem::Requirement
73
73
  none: false
74
74
  requirements:
75
75
  - - ! '>='
@@ -77,10 +77,10 @@ dependencies:
77
77
  version: '0'
78
78
  type: :development
79
79
  prerelease: false
80
- version_requirements: *2153516560
80
+ version_requirements: *2168873660
81
81
  - !ruby/object:Gem::Dependency
82
82
  name: webmock
83
- requirement: &2153516080 !ruby/object:Gem::Requirement
83
+ requirement: &2168873180 !ruby/object:Gem::Requirement
84
84
  none: false
85
85
  requirements:
86
86
  - - ! '>='
@@ -88,10 +88,10 @@ dependencies:
88
88
  version: '0'
89
89
  type: :development
90
90
  prerelease: false
91
- version_requirements: *2153516080
91
+ version_requirements: *2168873180
92
92
  - !ruby/object:Gem::Dependency
93
93
  name: bacon
94
- requirement: &2153515600 !ruby/object:Gem::Requirement
94
+ requirement: &2168906220 !ruby/object:Gem::Requirement
95
95
  none: false
96
96
  requirements:
97
97
  - - ! '>='
@@ -99,10 +99,10 @@ dependencies:
99
99
  version: '0'
100
100
  type: :development
101
101
  prerelease: false
102
- version_requirements: *2153515600
102
+ version_requirements: *2168906220
103
103
  - !ruby/object:Gem::Dependency
104
104
  name: rr
105
- requirement: &2153515120 !ruby/object:Gem::Requirement
105
+ requirement: &2168905740 !ruby/object:Gem::Requirement
106
106
  none: false
107
107
  requirements:
108
108
  - - ! '>='
@@ -110,10 +110,10 @@ dependencies:
110
110
  version: '0'
111
111
  type: :development
112
112
  prerelease: false
113
- version_requirements: *2153515120
113
+ version_requirements: *2168905740
114
114
  - !ruby/object:Gem::Dependency
115
115
  name: rake
116
- requirement: &2153514640 !ruby/object:Gem::Requirement
116
+ requirement: &2168905260 !ruby/object:Gem::Requirement
117
117
  none: false
118
118
  requirements:
119
119
  - - ! '>='
@@ -121,7 +121,7 @@ dependencies:
121
121
  version: '0'
122
122
  type: :development
123
123
  prerelease: false
124
- version_requirements: *2153514640
124
+ version_requirements: *2168905260
125
125
  description: ! 'A modular Ruby REST client collection/infrastructure.
126
126
 
127
127
 
@@ -152,10 +152,7 @@ email:
152
152
  - dev (XD) cardinalblue.com
153
153
  executables: []
154
154
  extensions: []
155
- extra_rdoc_files:
156
- - CONTRIBUTORS
157
- - LICENSE
158
- - TODO.md
155
+ extra_rdoc_files: []
159
156
  files:
160
157
  - .gitignore
161
158
  - .gitmodules
@@ -164,12 +161,13 @@ files:
164
161
  - Gemfile
165
162
  - LICENSE
166
163
  - NOTE.md
167
- - README
168
164
  - README.md
169
165
  - Rakefile
170
166
  - TODO.md
171
167
  - example/facebook.rb
172
168
  - example/github.rb
169
+ - example/linkedin.rb
170
+ - example/twitter.rb
173
171
  - lib/rest-core.rb
174
172
  - lib/rest-core/app/ask.rb
175
173
  - lib/rest-core/app/rest-client.rb
@@ -195,38 +193,35 @@ files:
195
193
  - lib/rest-core/middleware/oauth1_header.rb
196
194
  - lib/rest-core/middleware/oauth2_query.rb
197
195
  - lib/rest-core/middleware/timeout.rb
196
+ - lib/rest-core/util/config.rb
198
197
  - lib/rest-core/util/hmac.rb
199
198
  - lib/rest-core/version.rb
200
199
  - lib/rest-core/wrapper.rb
201
- - lib/rest-graph/config_util.rb
200
+ - pending/test_multi.rb
201
+ - pending/test_test_util.rb
202
202
  - rest-core.gemspec
203
203
  - task/.gitignore
204
204
  - task/gemgem.rb
205
205
  - test/common.rb
206
- - test/config/rest-graph.yaml
207
- - test/pending/test_load_config.rb
208
- - test/pending/test_multi.rb
209
- - test/pending/test_test_util.rb
206
+ - test/config/rest-core.yaml
210
207
  - test/test_api.rb
211
208
  - test/test_cache.rb
212
209
  - test/test_default.rb
213
210
  - test/test_error.rb
214
211
  - test/test_handler.rb
212
+ - test/test_load_config.rb
215
213
  - test/test_misc.rb
216
214
  - test/test_oauth.rb
217
215
  - test/test_oauth1_header.rb
218
216
  - test/test_old.rb
219
217
  - test/test_page.rb
220
218
  - test/test_parse.rb
221
- - test/test_rest-graph.rb
222
219
  - test/test_serialize.rb
223
220
  - test/test_timeout.rb
224
221
  homepage: https://github.com/cardinalblue/rest-core
225
222
  licenses: []
226
223
  post_install_message:
227
- rdoc_options:
228
- - --main
229
- - README
224
+ rdoc_options: []
230
225
  require_paths:
231
226
  - lib
232
227
  required_ruby_version: !ruby/object:Gem::Requirement
@@ -248,20 +243,17 @@ signing_key:
248
243
  specification_version: 3
249
244
  summary: A modular Ruby REST client collection/infrastructure.
250
245
  test_files:
251
- - test/pending/test_load_config.rb
252
- - test/pending/test_multi.rb
253
- - test/pending/test_test_util.rb
254
246
  - test/test_api.rb
255
247
  - test/test_cache.rb
256
248
  - test/test_default.rb
257
249
  - test/test_error.rb
258
250
  - test/test_handler.rb
251
+ - test/test_load_config.rb
259
252
  - test/test_misc.rb
260
253
  - test/test_oauth.rb
261
254
  - test/test_oauth1_header.rb
262
255
  - test/test_old.rb
263
256
  - test/test_page.rb
264
257
  - test/test_parse.rb
265
- - test/test_rest-graph.rb
266
258
  - test/test_serialize.rb
267
259
  - test/test_timeout.rb
data/README DELETED
@@ -1,83 +0,0 @@
1
- # rest-core [![Build Status](http://travis-ci.org/godfat/rest-core.png)](http://travis-ci.org/godfat/rest-core)
2
- by Cardinal Blue <http://cardinalblue.com>
3
-
4
- ## LINKS:
5
-
6
- * [github](https://github.com/cardinalblue/rest-core)
7
- * [rubygems](http://rubygems.org/gems/rest-core)
8
- * [rdoc](http://rdoc.info/projects/cardinalblue/rest-core)
9
- * [mailing list](http://groups.google.com/group/rest-core/topics)
10
-
11
- ## DESCRIPTION:
12
-
13
- A modular Ruby REST client collection/infrastructure.
14
-
15
- In this era of web services and mashups, we have seen a blooming of REST
16
- APIs. One might wonder, how do we use these APIs easily and elegantly?
17
- Since REST is very simple compared to SOAP, it is not hard to build a
18
- dedicated client ourselves.
19
-
20
- We have developed [rest-core][] with composable middlewares to build a
21
- REST client, based on the effort from [rest-graph][]. In the cases of
22
- common APIs such as Facebook, Github, and Twitter, developers can simply
23
- use the built-in dedicated clients provided by rest-core, or do it yourself
24
- for any other REST APIs.
25
-
26
- [rest-core]: http://github.com/cardinalblue/rest-core
27
- [rest-graph]: http://github.com/cardinalblue/rest-graph
28
-
29
- ## REQUIREMENTS:
30
-
31
- * Tested with MRI (official ruby) 1.9.2, 1.8.7, and trunk
32
- * Tested with Rubinius (rbx) 1.2.3
33
- * Tested with JRuby 1.6.2
34
-
35
- ## INSTALLATION:
36
-
37
- gem install rest-core
38
-
39
- Or if you want development version, put this in Gemfile:
40
-
41
- gem 'rest-core', :git => 'git://github.com/cardinalblue/rest-core.git'
42
-
43
- ## EXAMPLE:
44
-
45
- RestCore::Builder.client('YourClient') do
46
- use DefaultSite , 'https://api.github.com/users/'
47
- use JsonDecode , true
48
- use CommonLogger, method(:puts)
49
- use Cache , {}, nil
50
- run RestClient
51
- end
52
-
53
- client = YourClient.new
54
- client.get('godfat')
55
- client.get('godfat')
56
-
57
- client.site = 'http://github.com/api/v2/json/user/show/'
58
- client.get('godfat')
59
- client.get('godfat')
60
-
61
- See [example][] for more complex examples, and [build-in clients][] for even
62
- more complex examples.
63
-
64
- [example]: https://github.com/cardinalblue/rest-core/tree/master/example
65
- [build-in clients]: https://github.com/cardinalblue/rest-core/tree/master/lib/rest-core/client
66
-
67
- ## LICENSE:
68
-
69
- Apache License 2.0
70
-
71
- Copyright (c) 2011, Cardinal Blue
72
-
73
- Licensed under the Apache License, Version 2.0 (the "License");
74
- you may not use this file except in compliance with the License.
75
- You may obtain a copy of the License at
76
-
77
- <http://www.apache.org/licenses/LICENSE-2.0>
78
-
79
- Unless required by applicable law or agreed to in writing, software
80
- distributed under the License is distributed on an "AS IS" BASIS,
81
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
82
- See the License for the specific language governing permissions and
83
- limitations under the License.
@@ -1,43 +0,0 @@
1
-
2
- require 'erb'
3
- require 'yaml'
4
-
5
- require 'rest-graph/core'
6
-
7
- module RestGraph::ConfigUtil
8
- extend self
9
-
10
- def load_config_for_all
11
- RestGraph::ConfigUtil.load_config_for_rails if
12
- Object.const_defined?(:Rails)
13
- end
14
-
15
- def load_config_for_rails app=Rails
16
- root = app.root
17
- file = ["#{root}/config/rest-graph.yaml", # YAML should use .yaml
18
- "#{root}/config/rest-graph.yml"].find{|path| File.exist?(path)}
19
- return unless file
20
-
21
- RestGraph::ConfigUtil.load_config(file, Rails.env)
22
- end
23
-
24
- def load_config file, env
25
- config = YAML.load(ERB.new(File.read(file)).result(binding))
26
- defaults = config[env]
27
- return unless defaults
28
-
29
- mod = Module.new
30
- mod.module_eval(defaults.inject([]){ |r, (k, v)|
31
- # quote strings, leave others free (e.g. false, numbers, etc)
32
- r << <<-RUBY
33
- def default_#{k}
34
- #{v.kind_of?(String) ? "'#{v}'" : v}
35
- end
36
- RUBY
37
- }.join, __FILE__, __LINE__)
38
-
39
- RestGraph.send(:extend, mod)
40
- end
41
- end
42
-
43
- RestGraph.send(:extend, RestGraph::ConfigUtil)
@@ -1,42 +0,0 @@
1
-
2
- if respond_to?(:require_relative, true)
3
- require_relative 'common'
4
- else
5
- require File.dirname(__FILE__) + '/common'
6
- end
7
-
8
- require 'rest-graph/config_util'
9
-
10
- describe RestGraph::ConfigUtil do
11
-
12
- after do
13
- RR.verify
14
- end
15
-
16
- should 'honor rails config' do
17
- ::Rails = Object.new
18
- mock(Rails).env { 'test' }.times(2)
19
- mock(Rails).root{ File.dirname(__FILE__) }.times(2)
20
-
21
- check = lambda{
22
- RestGraph.default_app_id.should == 41829
23
- RestGraph.default_secret.should == 'r41829'.reverse
24
- RestGraph.default_auto_decode.should == false
25
- RestGraph.default_lang.should == 'zh-tw'
26
- }
27
-
28
- [RestGraph::ConfigUtil, RestGraph].each{ |const|
29
- TestHelper.ensure_rollback{
30
- const.load_config_for_rails
31
- check.call
32
- }
33
-
34
- TestHelper.ensure_rollback{
35
- const.load_config(
36
- "#{File.dirname(__FILE__)}/config/rest-graph.yaml",
37
- 'test')
38
- check.call
39
- }
40
- }
41
- end
42
- end
@@ -1,10 +0,0 @@
1
-
2
- $LOAD_PATH << File.dirname(__FILE__)
3
- $LOAD_PATH.uniq!
4
-
5
- Dir["#{File.dirname(__FILE__)}/*.rb"].each{ |file|
6
- next if file == __FILE__
7
- next if ARGV.map{ |path| File.expand_path(path)
8
- }.include?(File.expand_path(file))
9
- require File.basename(file).sub(/\..+$/, '')
10
- }