rest-core 2.0.0 → 2.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.
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: