rest-core 0.0.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.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
- }