webmachine 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +11 -3
- data/README.md +55 -27
- data/lib/webmachine/adapters/mongrel.rb +84 -0
- data/lib/webmachine/adapters/webrick.rb +12 -3
- data/lib/webmachine/adapters.rb +1 -7
- data/lib/webmachine/configuration.rb +30 -0
- data/lib/webmachine/decision/conneg.rb +7 -72
- data/lib/webmachine/decision/flow.rb +13 -11
- data/lib/webmachine/decision/fsm.rb +1 -9
- data/lib/webmachine/decision/helpers.rb +27 -7
- data/lib/webmachine/errors.rb +1 -0
- data/lib/webmachine/headers.rb +12 -3
- data/lib/webmachine/locale/en.yml +2 -2
- data/lib/webmachine/media_type.rb +117 -0
- data/lib/webmachine/resource/callbacks.rb +9 -0
- data/lib/webmachine/streaming.rb +3 -3
- data/lib/webmachine/version.rb +1 -1
- data/lib/webmachine.rb +3 -1
- data/pkg/webmachine-0.1.0/Gemfile +16 -0
- data/pkg/webmachine-0.1.0/Guardfile +11 -0
- data/pkg/webmachine-0.1.0/README.md +90 -0
- data/pkg/webmachine-0.1.0/Rakefile +31 -0
- data/pkg/webmachine-0.1.0/examples/webrick.rb +19 -0
- data/pkg/webmachine-0.1.0/lib/webmachine/adapters/webrick.rb +74 -0
- data/pkg/webmachine-0.1.0/lib/webmachine/adapters.rb +15 -0
- data/pkg/webmachine-0.1.0/lib/webmachine/decision/conneg.rb +304 -0
- data/pkg/webmachine-0.1.0/lib/webmachine/decision/flow.rb +502 -0
- data/pkg/webmachine-0.1.0/lib/webmachine/decision/fsm.rb +79 -0
- data/pkg/webmachine-0.1.0/lib/webmachine/decision/helpers.rb +80 -0
- data/pkg/webmachine-0.1.0/lib/webmachine/decision.rb +12 -0
- data/pkg/webmachine-0.1.0/lib/webmachine/dispatcher/route.rb +85 -0
- data/pkg/webmachine-0.1.0/lib/webmachine/dispatcher.rb +40 -0
- data/pkg/webmachine-0.1.0/lib/webmachine/errors.rb +37 -0
- data/pkg/webmachine-0.1.0/lib/webmachine/headers.rb +16 -0
- data/pkg/webmachine-0.1.0/lib/webmachine/locale/en.yml +28 -0
- data/pkg/webmachine-0.1.0/lib/webmachine/request.rb +56 -0
- data/pkg/webmachine-0.1.0/lib/webmachine/resource/callbacks.rb +362 -0
- data/pkg/webmachine-0.1.0/lib/webmachine/resource/encodings.rb +36 -0
- data/pkg/webmachine-0.1.0/lib/webmachine/resource.rb +48 -0
- data/pkg/webmachine-0.1.0/lib/webmachine/response.rb +49 -0
- data/pkg/webmachine-0.1.0/lib/webmachine/streaming.rb +27 -0
- data/pkg/webmachine-0.1.0/lib/webmachine/translation.rb +11 -0
- data/pkg/webmachine-0.1.0/lib/webmachine/version.rb +4 -0
- data/pkg/webmachine-0.1.0/lib/webmachine.rb +19 -0
- data/pkg/webmachine-0.1.0/spec/spec_helper.rb +13 -0
- data/pkg/webmachine-0.1.0/spec/tests.org +57 -0
- data/pkg/webmachine-0.1.0/spec/webmachine/decision/conneg_spec.rb +152 -0
- data/pkg/webmachine-0.1.0/spec/webmachine/decision/flow_spec.rb +1030 -0
- data/pkg/webmachine-0.1.0/spec/webmachine/dispatcher/route_spec.rb +109 -0
- data/pkg/webmachine-0.1.0/spec/webmachine/dispatcher_spec.rb +34 -0
- data/pkg/webmachine-0.1.0/spec/webmachine/headers_spec.rb +19 -0
- data/pkg/webmachine-0.1.0/spec/webmachine/request_spec.rb +24 -0
- data/pkg/webmachine-0.1.0/webmachine.gemspec +44 -0
- data/pkg/webmachine-0.1.0.gem +0 -0
- data/spec/webmachine/configuration_spec.rb +27 -0
- data/spec/webmachine/decision/conneg_spec.rb +18 -11
- data/spec/webmachine/decision/flow_spec.rb +2 -0
- data/spec/webmachine/decision/helpers_spec.rb +105 -0
- data/spec/webmachine/errors_spec.rb +13 -0
- data/spec/webmachine/headers_spec.rb +2 -1
- data/spec/webmachine/media_type_spec.rb +78 -0
- data/webmachine.gemspec +4 -1
- metadata +69 -11
@@ -52,15 +52,7 @@ module Webmachine
|
|
52
52
|
Webmachine.render_error(code, request, response)
|
53
53
|
when 304
|
54
54
|
response.headers.delete('Content-Type')
|
55
|
-
|
56
|
-
response.headers['ETag'] = ensure_quoted_header(etag)
|
57
|
-
end
|
58
|
-
if expires = resource.expires
|
59
|
-
response.headers['Expires'] = expires.httpdate
|
60
|
-
end
|
61
|
-
if modified = resource.last_modified
|
62
|
-
response.headers['Last-Modified'] = modified.httpdate
|
63
|
-
end
|
55
|
+
add_caching_headers
|
64
56
|
end
|
65
57
|
response.code = code
|
66
58
|
resource.finish_request
|
@@ -1,4 +1,6 @@
|
|
1
1
|
require 'webmachine/streaming'
|
2
|
+
require 'webmachine/media_type'
|
3
|
+
|
2
4
|
module Webmachine
|
3
5
|
module Decision
|
4
6
|
# Methods that assist the Decision {Flow}.
|
@@ -28,11 +30,19 @@ module Webmachine
|
|
28
30
|
resource.send(encoder, resource.send(charsetter, body))
|
29
31
|
when Enumerable
|
30
32
|
EnumerableEncoder.new(resource, encoder, charsetter, body)
|
31
|
-
when body.respond_to?(:call)
|
32
|
-
CallableEncoder.new(resource, encoder, charsetter, body)
|
33
33
|
else
|
34
|
-
|
34
|
+
if body.respond_to?(:call)
|
35
|
+
CallableEncoder.new(resource, encoder, charsetter, body)
|
36
|
+
else
|
37
|
+
resource.send(encoder, resource.send(charsetter, body))
|
38
|
+
end
|
35
39
|
end
|
40
|
+
if String === response.body
|
41
|
+
response.headers['Content-Length'] = response.body.respond_to?(:bytesize) ? response.body.bytesize.to_s : response.body.length.to_s
|
42
|
+
else
|
43
|
+
response.headers.delete 'Content-Length'
|
44
|
+
response.headers['Transfer-Encoding'] = 'chunked'
|
45
|
+
end
|
36
46
|
end
|
37
47
|
|
38
48
|
# Ensures that a header is quoted (like ETag)
|
@@ -55,10 +65,8 @@ module Webmachine
|
|
55
65
|
|
56
66
|
# Assists in receiving request bodies
|
57
67
|
def accept_helper
|
58
|
-
content_type = request.content_type || 'application/octet-stream'
|
59
|
-
|
60
|
-
metadata['mediaparams'] = mt.params
|
61
|
-
acceptable = resource.content_types_accepted.find {|ct, _| mt.type_matches?(Conneg::MediaType.parse(ct)) }
|
68
|
+
content_type = MediaType.parse(request.content_type || 'application/octet-stream')
|
69
|
+
acceptable = resource.content_types_accepted.find {|ct, _| content_type.match?(ct) }
|
62
70
|
if acceptable
|
63
71
|
resource.send(acceptable.last)
|
64
72
|
else
|
@@ -75,6 +83,18 @@ module Webmachine
|
|
75
83
|
v.unshift "Accept" if resource.content_types_provided.size > 1
|
76
84
|
end
|
77
85
|
end
|
86
|
+
|
87
|
+
def add_caching_headers
|
88
|
+
if etag = resource.generate_etag
|
89
|
+
response.headers['ETag'] = ensure_quoted_header(etag)
|
90
|
+
end
|
91
|
+
if expires = resource.expires
|
92
|
+
response.headers['Expires'] = expires.httpdate
|
93
|
+
end
|
94
|
+
if modified = resource.last_modified
|
95
|
+
response.headers['Last-Modified'] = modified.httpdate
|
96
|
+
end
|
97
|
+
end
|
78
98
|
end
|
79
99
|
end
|
80
100
|
end
|
data/lib/webmachine/errors.rb
CHANGED
@@ -12,6 +12,7 @@ module Webmachine
|
|
12
12
|
# @param [Hash] options keys to override the defaults when rendering
|
13
13
|
# the response body
|
14
14
|
def self.render_error(code, req, res, options={})
|
15
|
+
res.code = code
|
15
16
|
unless res.body
|
16
17
|
title, message = t(["errors.#{code}.title", "errors.#{code}.message"],
|
17
18
|
{ :method => req.method,
|
data/lib/webmachine/headers.rb
CHANGED
@@ -1,16 +1,25 @@
|
|
1
1
|
module Webmachine
|
2
|
-
# Case-insensitive Hash of
|
2
|
+
# Case-insensitive Hash of Request headers
|
3
3
|
class Headers < ::Hash
|
4
4
|
def [](key)
|
5
|
-
super key
|
5
|
+
super transform_key(key)
|
6
6
|
end
|
7
7
|
|
8
8
|
def []=(key,value)
|
9
|
-
super key
|
9
|
+
super transform_key(key), value
|
10
10
|
end
|
11
11
|
|
12
|
+
def delete(key)
|
13
|
+
super transform_key(key)
|
14
|
+
end
|
15
|
+
|
12
16
|
def grep(pattern)
|
13
17
|
self.class[select { |k,_| pattern === k }]
|
14
18
|
end
|
19
|
+
|
20
|
+
private
|
21
|
+
def transform_key(key)
|
22
|
+
key.to_s.downcase
|
23
|
+
end
|
15
24
|
end
|
16
25
|
end
|
@@ -19,10 +19,10 @@ en:
|
|
19
19
|
message: "The server does not support the %{method} method."
|
20
20
|
"503":
|
21
21
|
title: 503 Service Unavailable
|
22
|
-
message: The server is currently unable to
|
22
|
+
message: The server is currently unable to handle the request due to a temporary overloading or maintenance of the server.
|
23
23
|
create_path_nil: "post_is_create? returned true but create_path is nil! Define the create_path method in %{class}"
|
24
24
|
do_redirect: "Response had do_redirect but no Location header."
|
25
25
|
fsm_broke: "Decision FSM returned an unexpected value %{result} from decision %{state}."
|
26
|
-
invalid_media_type: "Invalid media type
|
26
|
+
invalid_media_type: "Invalid media type: %{type}"
|
27
27
|
not_resource_class: "%{class} is not a subclass of Webmachine::Resource"
|
28
28
|
process_post_invalid: "process_post returned %{result}"
|
@@ -0,0 +1,117 @@
|
|
1
|
+
require 'webmachine/translation'
|
2
|
+
|
3
|
+
module Webmachine
|
4
|
+
# Encapsulates a MIME media type, with logic for matching types.
|
5
|
+
class MediaType
|
6
|
+
extend Translation
|
7
|
+
# Matches valid media types
|
8
|
+
MEDIA_TYPE_REGEX = /^\s*([^;\s]+)\s*((?:;\s*\S+\s*)*)\s*$/
|
9
|
+
|
10
|
+
# Matches sub-type parameters
|
11
|
+
PARAMS_REGEX = /;\s*([^=]+)=([^;=\s]+)/
|
12
|
+
|
13
|
+
# Creates a new MediaType by parsing an alternate representation.
|
14
|
+
# @param [MediaType, String, Array<String,Hash>] obj the raw type
|
15
|
+
# to be parsed
|
16
|
+
# @return [MediaType] the parsed media type
|
17
|
+
# @raise [ArgumentError] when the type could not be parsed
|
18
|
+
def self.parse(obj)
|
19
|
+
case obj
|
20
|
+
when MediaType
|
21
|
+
obj
|
22
|
+
when MEDIA_TYPE_REGEX
|
23
|
+
type, raw_params = $1, $2
|
24
|
+
params = Hash[raw_params.scan(PARAMS_REGEX)]
|
25
|
+
new(type, params)
|
26
|
+
else
|
27
|
+
unless Array === obj && String === obj[0] && Hash === obj[1]
|
28
|
+
raise ArgumentError, t('invalid_media_type', :type => obj.inspect)
|
29
|
+
end
|
30
|
+
type = parse(obj[0])
|
31
|
+
type.params.merge!(obj[1])
|
32
|
+
type
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# @return [String] the MIME media type
|
37
|
+
attr_accessor :type
|
38
|
+
|
39
|
+
# @return [Hash] any type parameters, e.g. charset
|
40
|
+
attr_accessor :params
|
41
|
+
|
42
|
+
# @param [String] type the main media type, e.g. application/json
|
43
|
+
# @param [Hash] params the media type parameters
|
44
|
+
def initialize(type, params={})
|
45
|
+
@type, @params = type, params
|
46
|
+
end
|
47
|
+
|
48
|
+
# Detects whether the {MediaType} represents an open wildcard
|
49
|
+
# type, that is, "*/*" without any {#params}.
|
50
|
+
def matches_all?
|
51
|
+
@type == "*/*" && @params.empty?
|
52
|
+
end
|
53
|
+
|
54
|
+
def ==(other)
|
55
|
+
other = self.class.parse(other)
|
56
|
+
other.type == type && other.params == params
|
57
|
+
end
|
58
|
+
|
59
|
+
# Detects whether this {MediaType} matches the other {MediaType},
|
60
|
+
# taking into account wildcards. Sub-type parameters are treated
|
61
|
+
# strictly.
|
62
|
+
# @param [MediaType, String, Array<String,Hash>] other the other type
|
63
|
+
# @return [true,false] whether it is an acceptable match
|
64
|
+
def exact_match?(other)
|
65
|
+
other = self.class.parse(other)
|
66
|
+
type_matches?(other) && other.params == params
|
67
|
+
end
|
68
|
+
|
69
|
+
# Detects whether the {MediaType} is an acceptable match for the
|
70
|
+
# other {MediaType}, taking into account wildcards and satisfying
|
71
|
+
# all requested parameters, but allowing this type to have extra
|
72
|
+
# specificity.
|
73
|
+
# @param [MediaType, String, Array<String,Hash>] other the other type
|
74
|
+
# @return [true,false] whether it is an acceptable match
|
75
|
+
def match?(other)
|
76
|
+
other = self.class.parse(other)
|
77
|
+
type_matches?(other) && params_match?(other.params)
|
78
|
+
end
|
79
|
+
|
80
|
+
# Detects whether the passed sub-type parameters are all satisfied
|
81
|
+
# by this {MediaType}. The receiver is allowed to have other
|
82
|
+
# params than the ones specified, but all specified must be equal.
|
83
|
+
# @param [Hash] params the requested params
|
84
|
+
# @return [true,false] whether it is an acceptable match
|
85
|
+
def params_match?(other)
|
86
|
+
other.all? {|k,v| params[k] == v }
|
87
|
+
end
|
88
|
+
|
89
|
+
# Reconstitutes the type into a String
|
90
|
+
# @return [String] the type as a String
|
91
|
+
def to_s
|
92
|
+
[type, *params.map {|k,v| "#{k}=#{v}" }].join(";")
|
93
|
+
end
|
94
|
+
|
95
|
+
# @return [String] The major type, e.g. "application", "text", "image"
|
96
|
+
def major
|
97
|
+
type.split("/").first
|
98
|
+
end
|
99
|
+
|
100
|
+
# @return [String] the minor or sub-type, e.g. "json", "html", "jpeg"
|
101
|
+
def minor
|
102
|
+
type.split("/").last
|
103
|
+
end
|
104
|
+
|
105
|
+
# @param [MediaType] other the other type
|
106
|
+
# @return [true,false] whether the main media type is acceptable,
|
107
|
+
# ignoring params and taking into account wildcards
|
108
|
+
def type_matches?(other)
|
109
|
+
other = self.class.parse(other)
|
110
|
+
if ["*", "*/*", type].include?(other.type)
|
111
|
+
true
|
112
|
+
else
|
113
|
+
other.major == major && other.minor == "*"
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -239,6 +239,15 @@ module Webmachine
|
|
239
239
|
[]
|
240
240
|
end
|
241
241
|
|
242
|
+
# This should receive the chosen language and do something with
|
243
|
+
# it that is resource-specific. The default is to store the
|
244
|
+
# value in the @language instance variable.
|
245
|
+
# @param [String] lang the negotiated language
|
246
|
+
# @api callback
|
247
|
+
def language_chosen(lang)
|
248
|
+
@language = lang
|
249
|
+
end
|
250
|
+
|
242
251
|
# This should return a hash of encodings mapped to encoding
|
243
252
|
# methods for Content-Encodings your resource wants to
|
244
253
|
# provide. The encoding will be applied to the response body
|
data/lib/webmachine/streaming.rb
CHANGED
@@ -9,15 +9,15 @@ module Webmachine
|
|
9
9
|
include Enumerable
|
10
10
|
|
11
11
|
def each
|
12
|
-
body.each do |block|
|
13
|
-
yield @resource.send(@encoder, resource.send(@charsetter, block))
|
12
|
+
@body.each do |block|
|
13
|
+
yield @resource.send(@encoder, @resource.send(@charsetter, block))
|
14
14
|
end
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
18
|
class CallableEncoder < StreamingEncoder
|
19
19
|
def call
|
20
|
-
@resource.send(@encoder, @resource.send(@charsetter, body.call))
|
20
|
+
@resource.send(@encoder, @resource.send(@charsetter, @body.call))
|
21
21
|
end
|
22
22
|
|
23
23
|
def to_proc
|
data/lib/webmachine/version.rb
CHANGED
data/lib/webmachine.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'webmachine/configuration'
|
1
2
|
require 'webmachine/headers'
|
2
3
|
require 'webmachine/request'
|
3
4
|
require 'webmachine/response'
|
@@ -14,6 +15,7 @@ require 'webmachine/version'
|
|
14
15
|
module Webmachine
|
15
16
|
# Starts Webmachine serving requests
|
16
17
|
def self.run
|
17
|
-
|
18
|
+
configure unless configuration
|
19
|
+
Adapters.const_get(configuration.adapter).run
|
18
20
|
end
|
19
21
|
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
gemset = ENV['RVM_GEMSET'] || 'webmachine'
|
2
|
+
gemset = "@#{gemset}" unless gemset.to_s == ''
|
3
|
+
|
4
|
+
rvms = %W[ 1.9.2 ].map {|v| "#{v}#{gemset}" }
|
5
|
+
|
6
|
+
guard 'rspec', :cli => "--color --profile", :growl => true, :rvm => rvms do
|
7
|
+
watch(%r{^lib/webmachine/locale/.+$}) { "spec" }
|
8
|
+
watch(%r{^spec/.+_spec\.rb$})
|
9
|
+
watch(%r{^lib/(.+)\.rb$}){ |m| "spec/#{m[1]}_spec.rb" }
|
10
|
+
watch('spec/spec_helper.rb') { "spec" }
|
11
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# webmachine for Ruby [![travis](https://secure.travis-ci.org/seancribbs/webmachine-ruby.png)](http://travis-ci.org/seancribbs/webmachine-ruby)
|
2
|
+
|
3
|
+
webmachine-ruby is a port of
|
4
|
+
[Webmachine](https://github.com/basho/webmachine), which is written in
|
5
|
+
Erlang. The goal of both projects is to expose interesting parts of
|
6
|
+
the HTTP protocol to your application in a declarative way. This
|
7
|
+
means that you are less concerned with handling requests directly and
|
8
|
+
more with describing the behavior of the resources that make up your
|
9
|
+
application. Webmachine is not a web framework _per se_, but more of a
|
10
|
+
toolkit for building HTTP-friendly applications. For example, it does
|
11
|
+
not provide a templating engine or a persistence layer; those choices
|
12
|
+
are up to you.
|
13
|
+
|
14
|
+
**NOTE**: _Webmachine is NOT compatible with Rack._ This is
|
15
|
+
intentional! Rack obscures HTTP in a way that makes it hard for
|
16
|
+
Webmachine to do its job properly, and encourages people to add
|
17
|
+
middleware that might break Webmachine's behavior. Rack is also built
|
18
|
+
on the tradition of CGI, which is nice for backwards compatibility but
|
19
|
+
also an antiquated paradigm and should be scuttled (IMHO). _Rack may
|
20
|
+
be supported in the future, but only as a shim to support other web
|
21
|
+
application servers._
|
22
|
+
|
23
|
+
## Getting Started
|
24
|
+
|
25
|
+
Webmachine is very young, but it's still easy to construct an
|
26
|
+
application for it!
|
27
|
+
|
28
|
+
```ruby
|
29
|
+
require 'webmachine'
|
30
|
+
# Require any of the files that contain your resources here
|
31
|
+
require 'my_resource'
|
32
|
+
|
33
|
+
# Point all URIs at the MyResource class
|
34
|
+
Webmachine::Dispatcher.add_route(['*'], MyResource)
|
35
|
+
|
36
|
+
# Start the server, binds to port 3000 using WEBrick
|
37
|
+
Webmachine.run
|
38
|
+
```
|
39
|
+
|
40
|
+
Your resource will look something like this:
|
41
|
+
|
42
|
+
```ruby
|
43
|
+
class MyResource < Webmachine::Resource
|
44
|
+
def to_html
|
45
|
+
"<html><body>Hello, world!</body></html>"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
```
|
49
|
+
|
50
|
+
Run the first file and your application is up. That's all there is to
|
51
|
+
it! If you want to customize your resource more, look at the available
|
52
|
+
callbacks in lib/webmachine/resource/callbacks.rb. For example, you
|
53
|
+
might want to enable "gzip" compression on your resource, for which
|
54
|
+
you can simply add an `encodings_provided` callback method:
|
55
|
+
|
56
|
+
```ruby
|
57
|
+
class MyResource < Webmachine::Resource
|
58
|
+
def encodings_provided
|
59
|
+
{"gzip" => :encode_gzip, "identity" => :encode_identity}
|
60
|
+
end
|
61
|
+
|
62
|
+
def to_html
|
63
|
+
"<html><body>Hello, world!</body></html>"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
```
|
67
|
+
|
68
|
+
There are many other HTTP features exposed to your resource through
|
69
|
+
callbacks. Give them a try!
|
70
|
+
|
71
|
+
## Features
|
72
|
+
|
73
|
+
* Handles the hard parts of content negotiation, conditional
|
74
|
+
requests, and response codes for you.
|
75
|
+
* Most callbacks can interrupt the decision flow by returning an
|
76
|
+
integer response code. You generally only want to do this when new
|
77
|
+
information comes to light, requiring a modification of the response.
|
78
|
+
* Currently supports WEBrick. Other host servers are planned.
|
79
|
+
* Streaming/chunked response bodies are permitted as Enumerables or Procs.
|
80
|
+
* Unlike the Erlang original, it does real Language negotiation.
|
81
|
+
|
82
|
+
## Problems/TODOs
|
83
|
+
|
84
|
+
* Support streamed responses as Fibers.
|
85
|
+
* Configuration, command-line tools, and general polish.
|
86
|
+
* An effort has been made to make the code feel as Ruby-ish as
|
87
|
+
possible, but there is still work to do.
|
88
|
+
* Tracing is exposed as an Array of decisions visited on the response
|
89
|
+
object. You should be able to turn this off and on, and visualize
|
90
|
+
the decisions on the sequence diagram.
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rubygems/package_task'
|
3
|
+
|
4
|
+
def gemspec
|
5
|
+
$webmachine_gemspec ||= Gem::Specification.load("webmachine.gemspec")
|
6
|
+
end
|
7
|
+
|
8
|
+
Gem::PackageTask.new(gemspec) do |pkg|
|
9
|
+
pkg.need_zip = false
|
10
|
+
pkg.need_tar = false
|
11
|
+
end
|
12
|
+
|
13
|
+
task :gem => :gemspec
|
14
|
+
|
15
|
+
desc %{Validate the gemspec file.}
|
16
|
+
task :gemspec do
|
17
|
+
gemspec.validate
|
18
|
+
end
|
19
|
+
|
20
|
+
desc %{Release the gem to RubyGems.org}
|
21
|
+
task :release => :gem do
|
22
|
+
system "gem push pkg/#{gemspec.name}-#{gemspec.version}.gem"
|
23
|
+
end
|
24
|
+
|
25
|
+
require 'rspec/core'
|
26
|
+
require 'rspec/core/rake_task'
|
27
|
+
|
28
|
+
desc "Run specs"
|
29
|
+
RSpec::Core::RakeTask.new(:spec)
|
30
|
+
|
31
|
+
task :default => :spec
|