rest-core 2.0.0 → 2.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -1,7 +1,2 @@
1
1
  pkg
2
- rdoc
3
2
  *.rbc
4
- .bundle
5
- .yardoc
6
- Gemfile.lock
7
- task
data/CHANGES.md CHANGED
@@ -1,6 +1,19 @@
1
1
  # CHANGES
2
2
 
3
- ## rest-core 2.0.0
3
+ ## rest-core 2.0.1 -- 2013-01-08
4
+
5
+ ### Bugs fixes
6
+
7
+ * Don't walk into parent's constants in `RC.eagerload`.
8
+ * Also rescue `NameError` in `RC.eagerload`.
9
+
10
+ ### Enhancement
11
+
12
+ * Remove unnecessary `future.wrap` in `EmHttpRequest`.
13
+ * Introduce Future#callback_in_async.
14
+ * We would never double resume the fiber, so no need to rescue `FiberError`.
15
+
16
+ ## rest-core 2.0.0 -- 2012-10-31
4
17
 
5
18
  This is a major release which introduces some incompatible changes.
6
19
  This is intended to cleanup some internal implementation and introduce
data/README.md CHANGED
@@ -83,6 +83,8 @@ YourClient = RC::Builder.client do
83
83
  end
84
84
  ```
85
85
 
86
+ ### Basic Usage:
87
+
86
88
  And use it with per-instance basis (clients could have different
87
89
  configuration, e.g. different cache time or timeout time):
88
90
 
@@ -96,17 +98,71 @@ client.get('cardinalblue') # cache miss
96
98
  client.get('cardinalblue') # cache hit
97
99
  ```
98
100
 
101
+ ### Concurrent Requests with Futures:
102
+
99
103
  You can also make concurrent requests easily:
100
104
  (see "Advanced Concurrent HTTP Requests -- Embrace the Future" for detail)
101
105
 
102
106
  ``` ruby
103
- a = [client.get('cardinalblue')['name'], client.get('godfat')['name']]
107
+ a = [client.get('cardinalblue'), client.get('godfat')]
104
108
  puts "It's not blocking... but doing concurrent requests underneath"
105
- p a # here we want the values, so it blocks here
109
+ p a.map{ |r| r['name'] } # here we want the values, so it blocks here
106
110
  puts "DONE"
107
111
  ```
108
112
 
109
- Callback mode also available:
113
+ ### Exception Handling for Futures:
114
+
115
+ Note that since the API call would only block whenever you're looking at
116
+ the response, it won't raise any exception at the time the API was called.
117
+ So if you want to block and handle the exception at the time API was called,
118
+ you would do something like this:
119
+
120
+ ``` ruby
121
+ begin
122
+ response = client.get('bad-user').tap{} # .tap{} is the point
123
+ do_the_work(response)
124
+ rescue => e
125
+ puts "Got an exception: #{e}"
126
+ end
127
+ ```
128
+
129
+ The trick here is forcing the future immediately give you the exact response,
130
+ so that rest-core could see the response and raise the exception. You can
131
+ call whatever methods on the future to force this behaviour, but since `tap{}`
132
+ is a method from `Kernel` (which is included in `Object`), it's always
133
+ available and would return the original value, so it is the easiest method
134
+ to be remembered and used.
135
+
136
+ If you know the response must be a string, then you can also use `to_s`.
137
+ Like this:
138
+
139
+ ``` ruby
140
+ begin
141
+ response = client.get('bad-user').to_s
142
+ do_the_work(response)
143
+ rescue => e
144
+ puts "Got an exception: #{e}"
145
+ end
146
+ ```
147
+
148
+ Or you can also do this:
149
+
150
+ ``` ruby
151
+ begin
152
+ response = client.get('bad-user')
153
+ response.class # simply force it to load
154
+ do_the_work(response)
155
+ rescue => e
156
+ puts "Got an exception: #{e}"
157
+ end
158
+ ```
159
+
160
+ The point is simply making a method call to force it load, whatever method
161
+ should work.
162
+
163
+ ### Concurrent Requests with Callbacks:
164
+
165
+ On the other hand, callback mode also available:
110
166
 
111
167
  ``` ruby
112
168
  client.get('cardinalblue'){ |v| p v }
@@ -115,6 +171,53 @@ client.wait # we block here to wait for the request done
115
171
  puts "DONE"
116
172
  ```
117
173
 
174
+ ### Exception Handling for Callbacks:
175
+
176
+ What about exception handling in callback mode? You know that we cannot
177
+ raise any exception in the case of using a callback. So rest-core would
178
+ pass the exception object into your callback. You can handle the exception
179
+ like this:
180
+
181
+ ``` ruby
182
+ client.get('bad-user') do |response|
183
+ if response.kind_of?(Exception)
184
+ puts "Got an exception: #{response}"
185
+ else
186
+ do_the_work(response)
187
+ end
188
+ end
189
+ puts "It's not blocking... but doing concurrent requests underneath"
190
+ client.wait # we block here to wait for the request done
191
+ puts "DONE"
192
+ ```
193
+
194
+ ### More Control with `request_full`:
195
+
196
+ You can also use `request_full` to retrieve everything including response
197
+ status, response headers, and also other rest-core options. But since using
198
+ this interface is like using Rack directly, you have to build the env
199
+ manually. To help you build the env manually, everything has a default,
200
+ including the path.
201
+
202
+ ``` ruby
203
+ client.request_full({})[RC::RESPONSE_BODY] # {"message"=>"Not Found"}
204
+ # This would print something like this:
205
+ # RestCore: Auto picked: RestCore::RestClient
206
+ # RestCore: Future picked: RestCore::Future::FutureThread
207
+ # RestCore: spent 1.135713 Requested GET https://api.github.com/users//
208
+
209
+ client.request_full(RC::REQUEST_PATH => 'cardinalblue')[RC::RESPONSE_STATUS]
210
+ client.request_full(RC::REQUEST_PATH => 'cardinalblue')[RC::RESPONSE_HEADERS]
211
+ # Headers are normalized with all upper cases and
212
+ # dashes are replaced by underscores.
213
+
214
+ # To make POST (or any other request methods) request:
215
+ client.request_full(RC::REQUEST_PATH => 'cardinalblue',
216
+ RC::REQUEST_METHOD => :post)[RC::RESPONSE_STATUS] # 404
217
+ ```
218
+
219
+ ### Examples:
220
+
118
221
  Runnable example is at: [example/simple.rb][]. Please see [rest-more][]
119
222
  for more complex examples to build clients, and [slides][] from
120
223
  [rubyconf.tw/2011][rubyconf.tw] for concepts.
@@ -124,6 +227,74 @@ for more complex examples to build clients, and [slides][] from
124
227
  [slides]: http://www.godfat.org/slide/2011-08-27-rest-core.html
125
228
  [rubyconf.tw]: http://rubyconf.tw/2011/#6
126
229
 
230
+ ## Playing Around:
231
+
232
+ You can also play around with `RC::Universal` client, which has installed
233
+ _all_ reasonable middlewares built-in rest-core. So the above example could
234
+ also be achieved by:
235
+
236
+ ``` ruby
237
+ require 'rest-core'
238
+ client = RC::Universal.new(:site => 'https://api.github.com/users/',
239
+ :json_response => true,
240
+ :log_method => method(:puts))
241
+ client.get('cardinalblue')
242
+ ```
243
+
244
+ `RC::Universal` is defined as:
245
+
246
+ ``` ruby
247
+ module RestCore
248
+ Universal = Builder.client do
249
+ use Timeout , 0
250
+
251
+ use DefaultSite , nil
252
+ use DefaultHeaders, {}
253
+ use DefaultQuery , {}
254
+ use DefaultPayload, {}
255
+ use JsonRequest , false
256
+ use AuthBasic , nil, nil
257
+
258
+ use FollowRedirect, 10
259
+ use CommonLogger , method(:puts)
260
+ use Cache , {}, 600 do
261
+ use ErrorHandler, nil
262
+ use ErrorDetectorHttp
263
+ use JsonResponse, false
264
+ end
265
+ end
266
+ end
267
+ ```
268
+
269
+ If you have both [rib][] and [rest-more][] installed, you can also play
270
+ around with an interactive shell, like this:
271
+
272
+ ``` shell
273
+ rib rest-core
274
+ ```
275
+
276
+ And you will be entering a rib shell, which `self` is an instance of
277
+ `RC::Universal` you can play:
278
+
279
+ rest-core>> get 'https://api.github.com/users/cardinalblue'
280
+
281
+ will print out the response from Github. You can also do this to make
282
+ calling Github easier:
283
+
284
+ rest-core>> self.site = 'https://api.github.com/users/'
285
+ rest-core>> self.json_response = true
286
+
287
+ Then it would do exactly like the original example:
288
+
289
+ rest-core>> get 'cardinalblue' # you get a nice parsed hash
290
+
291
+ This is mostly for fun and experimenting, so it's only included in
292
+ [rest-more][] and [rib][]. Please make sure you have both of them
293
+ installed before trying this.
294
+
295
+ [rib]: https://github.com/godfat/rib
296
+ [rest-more]: https://github.com/cardinalblue/rest-more
297
+
127
298
  ## List of built-in Middlewares:
128
299
 
129
300
  * `RC::AuthBasic`
@@ -177,9 +348,9 @@ end
177
348
 
178
349
  client = YourClient.new
179
350
  puts "rest-client with threads doing concurrent requests"
180
- a = [client.get('cardinalblue')['name'], client.get('godfat')['name']]
351
+ a = [client.get('cardinalblue'), client.get('godfat')]
181
352
  puts "It's not blocking... but doing concurrent requests underneath"
182
- p a # here we want the values, so it blocks here
353
+ p a.map{ |r| r['name'] } # here we want the values, so it blocks here
183
354
  puts "DONE"
184
355
  ```
185
356
 
@@ -237,7 +408,8 @@ client = YourClient.new
237
408
  puts "eventmachine with threads doing concurrent requests"
238
409
  EM.run{
239
410
  Thread.new{
240
- p [client.get('cardinalblue')['name'], client.get('godfat')['name']]
411
+ a = [client.get('cardinalblue'), client.get('godfat')]
412
+ p a.map{ |r| r['name'] } # here we want the values, so it blocks here
241
413
  puts "DONE"
242
414
  EM.stop
243
415
  }
@@ -261,7 +433,8 @@ client = YourClient.new
261
433
  puts "eventmachine with fibers doing concurrent requests"
262
434
  EM.run{
263
435
  Fiber.new{
264
- p [client.get('cardinalblue')['name'], client.get('godfat')['name']]
436
+ a = [client.get('cardinalblue'), client.get('godfat')]
437
+ p a.map{ |r| r['name'] } # here we want the values, so it blocks here
265
438
  puts "DONE"
266
439
  EM.stop
267
440
  }
@@ -12,9 +12,9 @@ end
12
12
 
13
13
  client = YourClient.new
14
14
  puts "rest-client with threads doing concurrent requests"
15
- a = [client.get('cardinalblue')['name'], client.get('godfat')['name']]
15
+ a = [client.get('cardinalblue'), client.get('godfat')]
16
16
  puts "It's not blocking... but doing concurrent requests underneath"
17
- p a # here we want the values, so it blocks here
17
+ p a.map{ |r| r['name'] } # here we want the values, so it blocks here
18
18
  puts "DONE"
19
19
 
20
20
  puts; puts
@@ -22,7 +22,8 @@ puts; puts
22
22
  puts "eventmachine with threads doing concurrent requests"
23
23
  EM.run{
24
24
  Thread.new{
25
- p [client.get('cardinalblue')['name'], client.get('godfat')['name']]
25
+ a = [client.get('cardinalblue'), client.get('godfat')]
26
+ p a.map{ |r| r['name'] } # here we want the values, so it blocks here
26
27
  puts "DONE"
27
28
  EM.stop
28
29
  }
@@ -34,7 +35,8 @@ puts; puts
34
35
  puts "eventmachine with fibers doing concurrent requests"
35
36
  EM.run{
36
37
  Fiber.new{
37
- p [client.get('cardinalblue')['name'], client.get('godfat')['name']]
38
+ a = [client.get('cardinalblue'), client.get('godfat')]
39
+ p a.map{ |r| r['name'] } # here we want the values, so it blocks here
38
40
  puts "DONE"
39
41
  EM.stop
40
42
  }.resume
@@ -16,9 +16,9 @@ p client.get('cardinalblue') # cache hit
16
16
  client.cache = false
17
17
 
18
18
  puts "concurrent requests"
19
- a = [client.get('cardinalblue')['name'], client.get('godfat')['name']]
19
+ a = [client.get('cardinalblue'), client.get('godfat')]
20
20
  puts "It's not blocking... but doing concurrent requests underneath"
21
- p a # here we want the values, so it blocks here
21
+ p a.map{ |r| r['name'] } # here we want the values, so it blocks here
22
22
  puts "DONE"
23
23
 
24
24
  puts "callback"
@@ -66,12 +66,16 @@ def_use_case 'pure_ruby_nested_concurrent_requests' do
66
66
  %w[rubytaiwan godfat].each{ |user|
67
67
  c.get("/users/#{user}/repos", :per_page => 100){ |repos|
68
68
  rs = repos.reject{ |r| r['fork'] }
69
+ rs = [{}] if rs.size == 1 # out of API limit :(
69
70
  most_watched = rs.max_by{ |r| r['watchers'] }['name']
70
71
  most_size = rs.max_by{ |r| r['size'] }['name']
71
72
 
72
73
  watch_contri = c.get("/repos/#{user}/#{most_watched}/contributors")
73
74
  size_contri = c.get("/repos/#{user}/#{most_size}/contributors")
74
75
 
76
+ watch_contri = [{}] if watch_contri.size == 1 # out of API limit :(
77
+ size_contri = [{}] if size_contri.size == 1 # out of API limit :(
78
+
75
79
  most_watched_most_contri = watch_contri.max_by{ |c| c['contributions'] }
76
80
  most_size_most_contri = size_contri.max_by{ |c| c['contributions'] }
77
81
 
@@ -71,10 +71,10 @@ module RestCore
71
71
  def self.eagerload const=self, loaded={}
72
72
  return if loaded[const.name]
73
73
  loaded[const.name] = true
74
- const.constants.each{ |n|
74
+ const.constants(false).each{ |n|
75
75
  begin
76
76
  c = const.const_get(n)
77
- rescue LoadError => e
77
+ rescue LoadError, NameError => e
78
78
  warn "RestCore: WARN: #{e} for #{const}\n" \
79
79
  " from #{e.backtrace.grep(/top.+required/).first}"
80
80
  end
@@ -17,18 +17,17 @@ class RestCore::EmHttpRequest
17
17
  merge(env[REQUEST_HEADERS]))
18
18
 
19
19
  client.callback{
20
- future.wrap{ # callbacks are run in main thread, so we need to wrap it
21
- future.on_load(client.response,
22
- client.response_header.status,
23
- client.response_header)}}
20
+ future.on_load(client.response,
21
+ client.response_header.status,
22
+ client.response_header)}
24
23
 
25
- client.errback{future.wrap{ future.on_error(client.error) }}
24
+ client.errback{future.on_error(client.error) }
26
25
 
27
26
  env[TIMER].on_timeout{
28
27
  (client.instance_variable_get(:@callbacks)||[]).clear
29
28
  (client.instance_variable_get(:@errbacks )||[]).clear
30
29
  client.close
31
- future.wrap{ future.on_error(env[TIMER].error) }
30
+ future.on_error(env[TIMER].error)
32
31
  } if env[TIMER]
33
32
 
34
33
  env.merge(RESPONSE_BODY => future.proxy_body,
@@ -58,21 +58,22 @@ class RestCore::Future
58
58
  ["Future picked: #{self.class}"]))
59
59
  end
60
60
 
61
+ def callback_in_async
62
+ callback
63
+ rescue Exception => e
64
+ # nothing we can do here for an asynchronous exception,
65
+ # so we just log the error
66
+ logger = method(:warn) # TODO: add error_log_method
67
+ logger.call "RestCore: ERROR: #{e}\n from #{e.backtrace.inspect}"
68
+ end
69
+
61
70
  def on_load body, status, headers
62
71
  env[TIMER].cancel if env[TIMER]
63
72
  synchronize{
64
73
  self.body, self.status, self.headers = body, status, headers
65
- begin
66
- # under ASYNC callback, should call immediate
67
- next_tick{ callback } if immediate
68
- rescue Exception => e
69
- # nothing we can do here for an asynchronous exception,
70
- # so we just log the error
71
- logger = method(:warn) # TODO: add error_log_method
72
- logger.call "RestCore: ERROR: #{e}\n" \
73
- " from #{e.backtrace.inspect}"
74
- end
75
74
  }
75
+ # under ASYNC callback, should call immediately
76
+ next_tick{ callback_in_async } if immediate
76
77
  resume # client or response might be waiting
77
78
  end
78
79
 
@@ -22,14 +22,7 @@ class RestCore::Future::FutureFiber < RestCore::Future
22
22
  fibers.clear
23
23
  current_fibers.each{ |f|
24
24
  next unless f.alive?
25
- next_tick{
26
- begin
27
- f.resume
28
- rescue FiberError
29
- # whenever timeout, it would be already resumed,
30
- # and we have no way to tell if it's already resumed or not!
31
- end
32
- }
25
+ next_tick{ f.resume }
33
26
  }
34
27
  resume
35
28
  end
@@ -1,4 +1,4 @@
1
1
 
2
2
  module RestCore
3
- VERSION = '2.0.0'
3
+ VERSION = '2.0.1'
4
4
  end
@@ -2,13 +2,13 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = "rest-core"
5
- s.version = "2.0.0"
5
+ s.version = "2.0.1"
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
  "Cardinal Blue",
10
10
  "Lin Jen-Shin (godfat)"]
11
- s.date = "2012-10-31"
11
+ s.date = "2013-01-08"
12
12
  s.description = "Modular Ruby clients interface for REST APIs\n\nThere has been an explosion in the number of REST APIs available today.\nTo address the need for a way to access these APIs easily and elegantly,\nwe have developed rest-core, which consists of composable middleware\nthat allows you to build a REST client for any REST API. Or in the case of\ncommon APIs such as Facebook, Github, and Twitter, you can simply use the\ndedicated clients provided by [rest-more][].\n\n[rest-more]: https://github.com/cardinalblue/rest-more"
13
13
  s.email = ["dev (XD) cardinalblue.com"]
14
14
  s.files = [
@@ -71,6 +71,8 @@ Gem::Specification.new do |s|
71
71
  "lib/rest-core/version.rb",
72
72
  "lib/rest-core/wrapper.rb",
73
73
  "rest-core.gemspec",
74
+ "task/.gitignore",
75
+ "task/gemgem.rb",
74
76
  "test/test_auth_basic.rb",
75
77
  "test/test_builder.rb",
76
78
  "test/test_cache.rb",
@@ -92,7 +94,7 @@ Gem::Specification.new do |s|
92
94
  "test/test_wrapper.rb"]
93
95
  s.homepage = "https://github.com/cardinalblue/rest-core"
94
96
  s.require_paths = ["lib"]
95
- s.rubygems_version = "1.8.24"
97
+ s.rubygems_version = "1.8.23"
96
98
  s.summary = "Modular Ruby clients interface for REST APIs"
97
99
  s.test_files = [
98
100
  "test/test_auth_basic.rb",
@@ -0,0 +1 @@
1
+ *.rbc
@@ -0,0 +1,267 @@
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
+ s.description = description.join
16
+ s.summary = description.first
17
+
18
+ s.rubygems_version = Gem::VERSION
19
+ s.date = Time.now.strftime('%Y-%m-%d')
20
+ s.files = gem_files
21
+ s.test_files = gem_files.grep(%r{^test/(.+?/)*test_.+?\.rb$})
22
+ s.executables = Dir['bin/*'].map{ |f| File.basename(f) }
23
+ s.require_paths = %w[lib]
24
+ })
25
+ spec.homepage ||= "https://github.com/godfat/#{spec.name}"
26
+ spec
27
+ end
28
+
29
+ def readme
30
+ path = %w[README.md README].find{ |name|
31
+ File.exist?("#{Gemgem.dir}/#{name}")
32
+ }
33
+ @readme ||=
34
+ if path
35
+ ps = "##{File.read(path)}".
36
+ scan(/((#+)[^\n]+\n\n.+?(?=\n\n\2[^#\n]+\n))/m).map(&:first)
37
+ ps.inject({'HEADER' => ps.first}){ |r, s, i|
38
+ r[s[/\w+/]] = s
39
+ r
40
+ }
41
+ else
42
+ {}
43
+ end
44
+ end
45
+
46
+ def description
47
+ @description ||= (readme['DESCRIPTION']||'').sub(/.+\n\n/, '').lines.to_a
48
+ end
49
+
50
+ def changes
51
+ path = %w[CHANGES.md CHANGES].find{ |name|
52
+ File.exist?("#{Gemgem.dir}/#{name}")
53
+ }
54
+ @changes ||=
55
+ if path
56
+ date = '\d+{4}\-\d+{2}\-\d{2}'
57
+ File.read(path).match(
58
+ /([^\n]+#{date}\n\n(.+?))(?=\n\n[^\n]+#{date}\n|\Z)/m)[1]
59
+ else
60
+ ''
61
+ end
62
+ end
63
+
64
+ def ann_md
65
+ "#{readme['HEADER'].sub(/([\w\-]+)/, "[\\1](#{spec.homepage})")}\n\n" \
66
+ "##{readme['DESCRIPTION'][/[^\n]+\n\n[^\n]+/]}\n\n" \
67
+ "### CHANGES:\n\n" \
68
+ "###{changes}\n\n" \
69
+ "##{readme['INSTALLATION']}\n\n" +
70
+ if readme['SYNOPSIS'] then "##{readme['SYNOPSIS'][/[^\n]+\n\n[^\n]+/]}"
71
+ else '' end
72
+ end
73
+
74
+ def ann_html
75
+ gem 'nokogiri'
76
+ gem 'kramdown'
77
+
78
+ IO.popen('kramdown', 'r+') do |md|
79
+ md.puts Gemgem.ann_md
80
+ md.close_write
81
+ require 'nokogiri'
82
+ html = Nokogiri::XML.parse("<gemgem>#{md.read}</gemgem>")
83
+ html.css('*').each{ |n| n.delete('id') }
84
+ html.root.children.to_html
85
+ end
86
+ end
87
+
88
+ def ann_email
89
+ "#{readme['HEADER'].sub(/([\w\-]+)/, "\\1 <#{spec.homepage}>")}\n\n" \
90
+ "#{readme['DESCRIPTION']}\n\n" \
91
+ "#{readme['INSTALLATION']}\n\n" +
92
+ if readme['SYNOPSIS'] then "##{readme['SYNOPSIS']}\n\n" else '' end +
93
+ "## CHANGES:\n\n" \
94
+ "##{changes}\n\n"
95
+ end
96
+
97
+ def gem_tag
98
+ "#{spec.name}-#{spec.version}"
99
+ end
100
+
101
+ def write
102
+ File.open("#{dir}/#{spec.name}.gemspec", 'w'){ |f|
103
+ f << split_lines(spec.to_ruby) }
104
+ end
105
+
106
+ def split_lines ruby
107
+ ruby.gsub(/(.+?)\[(.+?)\]/){ |s|
108
+ if $2.index(',')
109
+ "#{$1}[\n #{$2.split(',').map(&:strip).join(",\n ")}]"
110
+ else
111
+ s
112
+ end
113
+ }
114
+ end
115
+
116
+ def all_files
117
+ @all_files ||= find_files(Pathname.new(dir)).map{ |file|
118
+ if file.to_s =~ %r{\.git/|\.git$}
119
+ nil
120
+ else
121
+ file.to_s
122
+ end
123
+ }.compact.sort
124
+ end
125
+
126
+ def gem_files
127
+ @gem_files ||= all_files - ignored_files
128
+ end
129
+
130
+ def ignored_files
131
+ @ignored_file ||= all_files.select{ |path| ignore_patterns.find{ |ignore|
132
+ path =~ ignore && !git_files.include?(path)}}
133
+ end
134
+
135
+ def git_files
136
+ @git_files ||= if File.exist?("#{dir}/.git")
137
+ `git ls-files`.split("\n")
138
+ else
139
+ []
140
+ end
141
+ end
142
+
143
+ # protected
144
+ def find_files path
145
+ path.children.select(&:file?).map{|file| file.to_s[(dir.size+1)..-1]} +
146
+ path.children.select(&:directory?).map{|dir| find_files(dir)}.flatten
147
+ end
148
+
149
+ def ignore_patterns
150
+ @ignore_files ||= expand_patterns(
151
+ gitignore.split("\n").reject{ |pattern|
152
+ pattern.strip == ''
153
+ }).map{ |pattern| %r{^([^/]+/)*?#{Regexp.escape(pattern)}(/[^/]+)*?$} }
154
+ end
155
+
156
+ def expand_patterns pathes
157
+ pathes.map{ |path|
158
+ if path !~ /\*/
159
+ path
160
+ else
161
+ expand_patterns(
162
+ Dir[path] +
163
+ Pathname.new(File.dirname(path)).children.select(&:directory?).
164
+ map{ |prefix| "#{prefix}/#{File.basename(path)}" })
165
+ end
166
+ }.flatten
167
+ end
168
+
169
+ def gitignore
170
+ if File.exist?(path = "#{dir}/.gitignore")
171
+ File.read(path)
172
+ else
173
+ ''
174
+ end
175
+ end
176
+ end
177
+
178
+ namespace :gem do
179
+
180
+ desc 'Install gem'
181
+ task :install => [:build] do
182
+ sh("#{Gem.ruby} -S gem install pkg/#{Gemgem.gem_tag}")
183
+ end
184
+
185
+ desc 'Build gem'
186
+ task :build => [:spec] do
187
+ sh("#{Gem.ruby} -S gem build #{Gemgem.spec.name}.gemspec")
188
+ sh("mkdir -p pkg")
189
+ sh("mv #{Gemgem.gem_tag}.gem pkg/")
190
+ end
191
+
192
+ desc 'Release gem'
193
+ task :release => [:spec, :check, :build] do
194
+ sh("git tag #{Gemgem.gem_tag}")
195
+ sh("git push")
196
+ sh("git push --tags")
197
+ sh("#{Gem.ruby} -S gem push pkg/#{Gemgem.gem_tag}.gem")
198
+ end
199
+
200
+ task :check do
201
+ ver = Gemgem.spec.version.to_s
202
+
203
+ if ENV['VERSION'].nil?
204
+ puts("\e[35mExpected " \
205
+ "\e[33mVERSION\e[35m=\e[33m#{ver}\e[0m")
206
+ exit(1)
207
+
208
+ elsif ENV['VERSION'] != ver
209
+ puts("\e[35mExpected \e[33mVERSION\e[35m=\e[33m#{ver} " \
210
+ "\e[35mbut got\n " \
211
+ "\e[33mVERSION\e[35m=\e[33m#{ENV['VERSION']}\e[0m")
212
+ exit(2)
213
+ end
214
+ end
215
+
216
+ end # of gem namespace
217
+
218
+ desc 'Run tests in memory'
219
+ task :test do
220
+ require 'bacon'
221
+ Bacon.extend(Bacon::TestUnitOutput)
222
+ Bacon.summary_on_exit
223
+ $LOAD_PATH.unshift('lib')
224
+ Dir['./test/**/test_*.rb'].each{ |file| require file[0..-4] }
225
+ end
226
+
227
+ desc 'Run tests with shell'
228
+ task 'test:shell', :RUBY_OPTS do |t, args|
229
+ files = Dir['test/**/test_*.rb'].join(' ')
230
+
231
+ cmd = [Gem.ruby, args[:RUBY_OPTS],
232
+ '-I', 'lib', '-S', 'bacon', '--quiet', files]
233
+
234
+ sh(cmd.compact.join(' '))
235
+ end
236
+
237
+ desc 'Generate ann markdown'
238
+ task 'ann:md' => ['gem:spec'] do
239
+ puts Gemgem.ann_md
240
+ end
241
+
242
+ desc 'Generate ann html'
243
+ task 'ann:html' => ['gem:spec'] do
244
+ puts Gemgem.ann_html
245
+ end
246
+
247
+ desc 'Generate ann email'
248
+ task 'ann:email' => ['gem:spec'] do
249
+ puts Gemgem.ann_email
250
+ end
251
+
252
+ desc 'Generate rdoc'
253
+ task :doc => ['gem:spec'] do
254
+ sh("yardoc -o rdoc --main README.md" \
255
+ " --files #{Gemgem.spec.extra_rdoc_files.join(',')}")
256
+ end
257
+
258
+ desc 'Remove ignored files'
259
+ task :clean => ['gem:spec'] do
260
+ trash = "~/.Trash/#{Gemgem.spec.name}/"
261
+ sh "mkdir -p #{trash}" unless File.exist?(File.expand_path(trash))
262
+ Gemgem.ignored_files.each{ |file| sh "mv #{file} #{trash}" }
263
+ end
264
+
265
+ task :default do
266
+ puts `#{Gem.ruby} -S #{$PROGRAM_NAME} -T`
267
+ end
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: 2.0.0
4
+ version: 2.0.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2012-10-31 00:00:00.000000000 Z
13
+ date: 2013-01-08 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rest-client
@@ -110,6 +110,8 @@ files:
110
110
  - lib/rest-core/version.rb
111
111
  - lib/rest-core/wrapper.rb
112
112
  - rest-core.gemspec
113
+ - task/.gitignore
114
+ - task/gemgem.rb
113
115
  - test/test_auth_basic.rb
114
116
  - test/test_builder.rb
115
117
  - test/test_cache.rb
@@ -149,7 +151,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
149
151
  version: '0'
150
152
  requirements: []
151
153
  rubyforge_project:
152
- rubygems_version: 1.8.24
154
+ rubygems_version: 1.8.23
153
155
  signing_key:
154
156
  specification_version: 3
155
157
  summary: Modular Ruby clients interface for REST APIs
@@ -173,4 +175,3 @@ test_files:
173
175
  - test/test_timeout.rb
174
176
  - test/test_universal.rb
175
177
  - test/test_wrapper.rb
176
- has_rdoc: