rest-core 0.0.1

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.
Files changed (65) hide show
  1. data/.gitignore +6 -0
  2. data/.gitmodules +3 -0
  3. data/.travis.yml +9 -0
  4. data/CONTRIBUTORS +11 -0
  5. data/Gemfile +8 -0
  6. data/LICENSE +201 -0
  7. data/NOTE.md +48 -0
  8. data/README +83 -0
  9. data/README.md +83 -0
  10. data/Rakefile +26 -0
  11. data/TODO.md +17 -0
  12. data/example/facebook.rb +145 -0
  13. data/example/github.rb +21 -0
  14. data/lib/rest-core.rb +48 -0
  15. data/lib/rest-core/app/ask.rb +11 -0
  16. data/lib/rest-core/app/rest-client.rb +24 -0
  17. data/lib/rest-core/builder.rb +24 -0
  18. data/lib/rest-core/client.rb +278 -0
  19. data/lib/rest-core/client/github.rb +19 -0
  20. data/lib/rest-core/client/linkedin.rb +57 -0
  21. data/lib/rest-core/client/rest-graph.rb +262 -0
  22. data/lib/rest-core/client/twitter.rb +59 -0
  23. data/lib/rest-core/client_oauth1.rb +25 -0
  24. data/lib/rest-core/event.rb +17 -0
  25. data/lib/rest-core/middleware.rb +53 -0
  26. data/lib/rest-core/middleware/cache.rb +80 -0
  27. data/lib/rest-core/middleware/common_logger.rb +27 -0
  28. data/lib/rest-core/middleware/default_headers.rb +11 -0
  29. data/lib/rest-core/middleware/default_query.rb +11 -0
  30. data/lib/rest-core/middleware/default_site.rb +15 -0
  31. data/lib/rest-core/middleware/defaults.rb +44 -0
  32. data/lib/rest-core/middleware/error_detector.rb +16 -0
  33. data/lib/rest-core/middleware/error_detector_http.rb +11 -0
  34. data/lib/rest-core/middleware/error_handler.rb +19 -0
  35. data/lib/rest-core/middleware/json_decode.rb +83 -0
  36. data/lib/rest-core/middleware/oauth1_header.rb +81 -0
  37. data/lib/rest-core/middleware/oauth2_query.rb +19 -0
  38. data/lib/rest-core/middleware/timeout.rb +13 -0
  39. data/lib/rest-core/util/hmac.rb +22 -0
  40. data/lib/rest-core/version.rb +4 -0
  41. data/lib/rest-core/wrapper.rb +55 -0
  42. data/lib/rest-graph/config_util.rb +43 -0
  43. data/rest-core.gemspec +162 -0
  44. data/task/.gitignore +1 -0
  45. data/task/gemgem.rb +184 -0
  46. data/test/common.rb +29 -0
  47. data/test/config/rest-graph.yaml +7 -0
  48. data/test/pending/test_load_config.rb +42 -0
  49. data/test/pending/test_multi.rb +123 -0
  50. data/test/pending/test_test_util.rb +86 -0
  51. data/test/test_api.rb +98 -0
  52. data/test/test_cache.rb +62 -0
  53. data/test/test_default.rb +27 -0
  54. data/test/test_error.rb +66 -0
  55. data/test/test_handler.rb +87 -0
  56. data/test/test_misc.rb +75 -0
  57. data/test/test_oauth.rb +42 -0
  58. data/test/test_oauth1_header.rb +46 -0
  59. data/test/test_old.rb +116 -0
  60. data/test/test_page.rb +110 -0
  61. data/test/test_parse.rb +131 -0
  62. data/test/test_rest-graph.rb +10 -0
  63. data/test/test_serialize.rb +44 -0
  64. data/test/test_timeout.rb +25 -0
  65. metadata +267 -0
@@ -0,0 +1,22 @@
1
+
2
+ require 'openssl'
3
+
4
+ module RestCore; end
5
+ module RestCore::Hmac
6
+ module_function
7
+ # Fallback to ruby-hmac gem in case system openssl
8
+ # lib doesn't support SHA256 (OSX 10.5)
9
+ def sha256 key, data
10
+ OpenSSL::HMAC.digest('sha256', key, data)
11
+ rescue RuntimeError
12
+ require 'hmac-sha2'
13
+ HMAC::SHA256.digest(key, data)
14
+ end
15
+
16
+ def sha1 key, data
17
+ OpenSSL::HMAC.digest('sha1', key, data)
18
+ rescue RuntimeError
19
+ require 'hmac-sha1'
20
+ HMAC::SHA1.digest(key, data)
21
+ end
22
+ end
@@ -0,0 +1,4 @@
1
+
2
+ module RestCore
3
+ VERSION = '0.0.1'
4
+ end
@@ -0,0 +1,55 @@
1
+
2
+ require 'rest-core'
3
+
4
+ module RestCore::Wrapper
5
+ include RestCore
6
+ def self.included mod
7
+ mod.send(:attr_reader, :init, :middles, :wrapped)
8
+ end
9
+
10
+ def initialize &block
11
+ @middles ||= []
12
+ instance_eval(&block) if block_given?
13
+ @wrapped ||= to_app(@init || Ask)
14
+ end
15
+
16
+ def use middle, *args, &block
17
+ middles << [middle, args, block]
18
+ end
19
+
20
+ def run app
21
+ @init = app
22
+ end
23
+
24
+ def members
25
+ middles.map{ |(middle, args, block)|
26
+ if middle.public_method_defined?(:wrapped)
27
+ # TODO: this is hacky... try to avoid calling new!
28
+ middle.members + middle.new(Ask.new, *args, &block).members
29
+ else
30
+ middle.members
31
+ end
32
+ }.flatten
33
+ end
34
+
35
+ def to_app init=@init
36
+ # === foldr m.new app middles
37
+ middles.reverse.inject(init.new){ |app_, (middle, args, block)|
38
+ begin
39
+ middle.new(app_, *partial_deep_copy(args), &block)
40
+ rescue ArgumentError => e
41
+ raise ArgumentError.new("#{middle}: #{e}")
42
+ end
43
+ }
44
+ end
45
+
46
+ module_function
47
+ def partial_deep_copy obj
48
+ case obj
49
+ when Array; obj.map{ |o| partial_deep_copy(o) }
50
+ when Hash ; obj.inject({}){ |r, (k, v)| r[k] = partial_deep_copy(v); r }
51
+ when Numeric, Symbol, TrueClass, FalseClass, NilClass; obj
52
+ else begin obj.dup; rescue TypeError; obj; end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,43 @@
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)
@@ -0,0 +1,162 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{rest-core}
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 = [
9
+ %q{Cardinal Blue},
10
+ %q{Lin Jen-Shin (godfat)}]
11
+ s.date = %q{2011-08-16}
12
+ s.description = %q{A modular Ruby REST client collection/infrastructure.
13
+
14
+ In this era of web services and mashups, we have seen a blooming of REST
15
+ APIs. One might wonder, how do we use these APIs easily and elegantly?
16
+ Since REST is very simple compared to SOAP, it is not hard to build a
17
+ dedicated client ourselves.
18
+
19
+ We have developed [rest-core][] with composable middlewares to build a
20
+ REST client, based on the effort from [rest-graph][]. In the cases of
21
+ common APIs such as Facebook, Github, and Twitter, developers can simply
22
+ use the built-in dedicated clients provided by rest-core, or do it yourself
23
+ for any other REST APIs.
24
+
25
+ [rest-core]: http://github.com/cardinalblue/rest-core
26
+ [rest-graph]: http://github.com/cardinalblue/rest-graph}
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
+ s.files = [
34
+ %q{.gitignore},
35
+ %q{.gitmodules},
36
+ %q{.travis.yml},
37
+ %q{CONTRIBUTORS},
38
+ %q{Gemfile},
39
+ %q{LICENSE},
40
+ %q{NOTE.md},
41
+ %q{README},
42
+ %q{README.md},
43
+ %q{Rakefile},
44
+ %q{TODO.md},
45
+ %q{example/facebook.rb},
46
+ %q{example/github.rb},
47
+ %q{lib/rest-core.rb},
48
+ %q{lib/rest-core/app/ask.rb},
49
+ %q{lib/rest-core/app/rest-client.rb},
50
+ %q{lib/rest-core/builder.rb},
51
+ %q{lib/rest-core/client.rb},
52
+ %q{lib/rest-core/client/github.rb},
53
+ %q{lib/rest-core/client/linkedin.rb},
54
+ %q{lib/rest-core/client/rest-graph.rb},
55
+ %q{lib/rest-core/client/twitter.rb},
56
+ %q{lib/rest-core/client_oauth1.rb},
57
+ %q{lib/rest-core/event.rb},
58
+ %q{lib/rest-core/middleware.rb},
59
+ %q{lib/rest-core/middleware/cache.rb},
60
+ %q{lib/rest-core/middleware/common_logger.rb},
61
+ %q{lib/rest-core/middleware/default_headers.rb},
62
+ %q{lib/rest-core/middleware/default_query.rb},
63
+ %q{lib/rest-core/middleware/default_site.rb},
64
+ %q{lib/rest-core/middleware/defaults.rb},
65
+ %q{lib/rest-core/middleware/error_detector.rb},
66
+ %q{lib/rest-core/middleware/error_detector_http.rb},
67
+ %q{lib/rest-core/middleware/error_handler.rb},
68
+ %q{lib/rest-core/middleware/json_decode.rb},
69
+ %q{lib/rest-core/middleware/oauth1_header.rb},
70
+ %q{lib/rest-core/middleware/oauth2_query.rb},
71
+ %q{lib/rest-core/middleware/timeout.rb},
72
+ %q{lib/rest-core/util/hmac.rb},
73
+ %q{lib/rest-core/version.rb},
74
+ %q{lib/rest-core/wrapper.rb},
75
+ %q{lib/rest-graph/config_util.rb},
76
+ %q{rest-core.gemspec},
77
+ %q{task/.gitignore},
78
+ %q{task/gemgem.rb},
79
+ %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},
84
+ %q{test/test_api.rb},
85
+ %q{test/test_cache.rb},
86
+ %q{test/test_default.rb},
87
+ %q{test/test_error.rb},
88
+ %q{test/test_handler.rb},
89
+ %q{test/test_misc.rb},
90
+ %q{test/test_oauth.rb},
91
+ %q{test/test_oauth1_header.rb},
92
+ %q{test/test_old.rb},
93
+ %q{test/test_page.rb},
94
+ %q{test/test_parse.rb},
95
+ %q{test/test_rest-graph.rb},
96
+ %q{test/test_serialize.rb},
97
+ %q{test/test_timeout.rb}]
98
+ s.homepage = %q{https://github.com/cardinalblue/rest-core}
99
+ s.rdoc_options = [
100
+ %q{--main},
101
+ %q{README}]
102
+ s.require_paths = [%q{lib}]
103
+ s.rubygems_version = %q{1.8.7}
104
+ s.summary = %q{A modular Ruby REST client collection/infrastructure.}
105
+ 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
+ %q{test/test_api.rb},
110
+ %q{test/test_cache.rb},
111
+ %q{test/test_default.rb},
112
+ %q{test/test_error.rb},
113
+ %q{test/test_handler.rb},
114
+ %q{test/test_misc.rb},
115
+ %q{test/test_oauth.rb},
116
+ %q{test/test_oauth1_header.rb},
117
+ %q{test/test_old.rb},
118
+ %q{test/test_page.rb},
119
+ %q{test/test_parse.rb},
120
+ %q{test/test_rest-graph.rb},
121
+ %q{test/test_serialize.rb},
122
+ %q{test/test_timeout.rb}]
123
+
124
+ if s.respond_to? :specification_version then
125
+ s.specification_version = 3
126
+
127
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
128
+ s.add_development_dependency(%q<rest-client>, [">= 0"])
129
+ s.add_development_dependency(%q<rack>, [">= 0"])
130
+ s.add_development_dependency(%q<yajl-ruby>, [">= 0"])
131
+ s.add_development_dependency(%q<json>, [">= 0"])
132
+ s.add_development_dependency(%q<json_pure>, [">= 0"])
133
+ s.add_development_dependency(%q<ruby-hmac>, [">= 0"])
134
+ s.add_development_dependency(%q<webmock>, [">= 0"])
135
+ s.add_development_dependency(%q<bacon>, [">= 0"])
136
+ s.add_development_dependency(%q<rr>, [">= 0"])
137
+ s.add_development_dependency(%q<rake>, [">= 0"])
138
+ else
139
+ s.add_dependency(%q<rest-client>, [">= 0"])
140
+ s.add_dependency(%q<rack>, [">= 0"])
141
+ s.add_dependency(%q<yajl-ruby>, [">= 0"])
142
+ s.add_dependency(%q<json>, [">= 0"])
143
+ s.add_dependency(%q<json_pure>, [">= 0"])
144
+ s.add_dependency(%q<ruby-hmac>, [">= 0"])
145
+ s.add_dependency(%q<webmock>, [">= 0"])
146
+ s.add_dependency(%q<bacon>, [">= 0"])
147
+ s.add_dependency(%q<rr>, [">= 0"])
148
+ s.add_dependency(%q<rake>, [">= 0"])
149
+ end
150
+ else
151
+ s.add_dependency(%q<rest-client>, [">= 0"])
152
+ s.add_dependency(%q<rack>, [">= 0"])
153
+ s.add_dependency(%q<yajl-ruby>, [">= 0"])
154
+ s.add_dependency(%q<json>, [">= 0"])
155
+ s.add_dependency(%q<json_pure>, [">= 0"])
156
+ s.add_dependency(%q<ruby-hmac>, [">= 0"])
157
+ s.add_dependency(%q<webmock>, [">= 0"])
158
+ s.add_dependency(%q<bacon>, [">= 0"])
159
+ s.add_dependency(%q<rr>, [">= 0"])
160
+ s.add_dependency(%q<rake>, [">= 0"])
161
+ end
162
+ end
@@ -0,0 +1 @@
1
+ *.rbc
@@ -0,0 +1,184 @@
1
+
2
+ require 'pathname'
3
+
4
+ module Gemgem
5
+ class << self
6
+ attr_accessor :dir, :spec
7
+ end
8
+
9
+ module_function
10
+ def create
11
+ yield(spec = Gem::Specification.new{ |s|
12
+ s.authors = ['Lin Jen-Shin (godfat)']
13
+ s.email = ['godfat (XD) godfat.org']
14
+
15
+ description = File.read("#{Gemgem.dir}/README").
16
+ match(/DESCRIPTION:\n\n(.+?)(?=\n\n[^\n]+:\n)/m)[1].
17
+ lines.to_a
18
+
19
+ s.description = description.join
20
+ s.summary = description.first
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
+ s.rubygems_version = Gem::VERSION
27
+ s.date = Time.now.strftime('%Y-%m-%d')
28
+ s.files = gem_files
29
+ s.test_files = gem_files.grep(%r{^test/(.+?/)*test_.+?\.rb$})
30
+ s.executables = Dir['bin/*'].map{ |f| File.basename(f) }
31
+ s.require_paths = %w[lib]
32
+ })
33
+ spec.homepage ||= "https://github.com/godfat/#{spec.name}"
34
+ spec
35
+ end
36
+
37
+ def gem_tag
38
+ "#{spec.name}-#{spec.version}"
39
+ end
40
+
41
+ def write
42
+ File.open("#{dir}/#{spec.name}.gemspec", 'w'){ |f|
43
+ f << split_lines(spec.to_ruby) }
44
+ end
45
+
46
+ def split_lines ruby
47
+ ruby.gsub(/(.+?)\[(.+?)\]/){ |s|
48
+ if $2.index(',')
49
+ "#{$1}[\n #{$2.split(',').map(&:strip).join(",\n ")}]"
50
+ else
51
+ s
52
+ end
53
+ }
54
+ end
55
+
56
+ def all_files
57
+ @all_files ||= find_files(Pathname.new(dir)).map{ |file|
58
+ if file.to_s =~ %r{\.git/}
59
+ nil
60
+ else
61
+ file.to_s
62
+ end
63
+ }.compact.sort
64
+ end
65
+
66
+ def gem_files
67
+ @gem_files ||= all_files - ignored_files
68
+ end
69
+
70
+ def ignored_files
71
+ @ignored_file ||= all_files.select{ |path| ignore_patterns.find{ |ignore|
72
+ path =~ ignore && !git_files.include?(path)}}
73
+ end
74
+
75
+ def git_files
76
+ @git_files ||= if File.exist?("#{dir}/.git")
77
+ `git ls-files`.split("\n")
78
+ else
79
+ []
80
+ end
81
+ end
82
+
83
+ # protected
84
+ def find_files path
85
+ path.children.select(&:file?).map{|file| file.to_s[(dir.size+1)..-1]} +
86
+ path.children.select(&:directory?).map{|dir| find_files(dir)}.flatten
87
+ end
88
+
89
+ def ignore_patterns
90
+ @ignore_files ||= expand_patterns(
91
+ File.read("#{dir}/.gitignore").split("\n").reject{ |pattern|
92
+ pattern.strip == ''
93
+ }).map{ |pattern| %r{^([^/]+/)*?#{Regexp.escape(pattern)}(/[^/]+)*?$} }
94
+ end
95
+
96
+ def expand_patterns pathes
97
+ pathes.map{ |path|
98
+ if path !~ /\*/
99
+ path
100
+ else
101
+ expand_patterns(
102
+ Dir[path] +
103
+ Pathname.new(File.dirname(path)).children.select(&:directory?).
104
+ map{ |prefix| "#{prefix}/#{File.basename(path)}" })
105
+ end
106
+ }.flatten
107
+ end
108
+ end
109
+
110
+ namespace :gem do
111
+
112
+ desc 'Install gem'
113
+ task :install => [:build] do
114
+ sh("#{Gem.ruby} -S gem install pkg/#{Gemgem.gem_tag}")
115
+ end
116
+
117
+ desc 'Build gem'
118
+ task :build => [:spec] do
119
+ sh("#{Gem.ruby} -S gem build #{Gemgem.spec.name}.gemspec")
120
+ sh("mkdir -p pkg")
121
+ sh("mv #{Gemgem.gem_tag}.gem pkg/")
122
+ end
123
+
124
+ desc 'Release gem'
125
+ task :release => [:spec, :check, :build] do
126
+ sh("git tag #{Gemgem.gem_tag}")
127
+ sh("git push")
128
+ sh("git push --tags")
129
+ sh("#{Gem.ruby} -S gem push pkg/#{Gemgem.gem_tag}.gem")
130
+ end
131
+
132
+ task :check do
133
+ ver = Gemgem.spec.version.to_s
134
+
135
+ if ENV['VERSION'].nil?
136
+ puts("\x1b[35mExpected " \
137
+ "\x1b[33mVERSION\x1b[35m=\x1b[33m#{ver}\x1b[m")
138
+ exit(1)
139
+
140
+ elsif ENV['VERSION'] != ver
141
+ puts("\x1b[35mExpected \x1b[33mVERSION\x1b[35m=\x1b[33m#{ver} " \
142
+ "\x1b[35mbut got\n " \
143
+ "\x1b[33mVERSION\x1b[35m=\x1b[33m#{ENV['VERSION']}\x1b[m")
144
+ exit(2)
145
+ end
146
+ end
147
+
148
+ end # of gem namespace
149
+
150
+ desc 'Run tests in memory'
151
+ task :test do
152
+ require 'bacon'
153
+ Bacon.extend(Bacon::TestUnitOutput)
154
+ Bacon.summary_on_exit
155
+ $LOAD_PATH.unshift('lib')
156
+ Dir['test/**/test_*.rb'].each{ |file| load file }
157
+ end
158
+
159
+ desc 'Run tests with shell'
160
+ task 'test:shell', :RUBY_OPTS do |t, args|
161
+ files = Dir['test/**/test_*.rb'].join(' ')
162
+
163
+ cmd = [Gem.ruby, args[:RUBY_OPTS],
164
+ '-I', 'lib', '-S', 'bacon', '--quiet', files]
165
+
166
+ sh(cmd.compact.join(' '))
167
+ end
168
+
169
+ desc 'Generate rdoc'
170
+ task :doc => ['gem:spec'] do
171
+ sh("yardoc -o rdoc --main README.md" \
172
+ " --files #{Gemgem.spec.extra_rdoc_files.join(',')}")
173
+ end
174
+
175
+ desc 'Removed ignored files'
176
+ task :clean => ['gem:spec'] do
177
+ trash = "~/.Trash/#{Gemgem.spec.name}/"
178
+ sh "mkdir -p #{trash}" unless File.exist?(File.expand_path(trash))
179
+ Gemgem.ignored_files.each{ |file| sh "mv #{file} #{trash}" }
180
+ end
181
+
182
+ task :default do
183
+ puts `#{Gem.ruby} -S #{$PROGRAM_NAME} -T`
184
+ end