webbed 0.1.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.
Files changed (46) hide show
  1. data/.gitignore +22 -0
  2. data/.travis.yml +7 -0
  3. data/.yardopts +6 -0
  4. data/Gemfile +5 -13
  5. data/Guardfile +5 -0
  6. data/README.md +36 -31
  7. data/Rakefile +7 -14
  8. data/lib/webbed.rb +28 -6
  9. data/lib/webbed/generic_message.rb +27 -15
  10. data/lib/webbed/headers.rb +2 -0
  11. data/lib/webbed/helpers/entity_headers_helper.rb +62 -0
  12. data/lib/webbed/helpers/method_helper.rb +83 -0
  13. data/lib/webbed/helpers/rack_request_helper.rb +86 -0
  14. data/lib/webbed/helpers/rack_response_helper.rb +56 -0
  15. data/lib/webbed/helpers/request_headers_helper.rb +62 -0
  16. data/lib/webbed/helpers/request_uri_helper.rb +34 -0
  17. data/lib/webbed/helpers/response_headers_helper.rb +48 -0
  18. data/lib/webbed/helpers/scheme_helper.rb +26 -0
  19. data/lib/webbed/http_version.rb +88 -33
  20. data/lib/webbed/media_type.rb +160 -0
  21. data/lib/webbed/method.rb +63 -33
  22. data/lib/webbed/request.rb +65 -21
  23. data/lib/webbed/response.rb +65 -24
  24. data/lib/webbed/status_code.rb +84 -17
  25. data/lib/webbed/version.rb +1 -1
  26. data/test/support/assertions.rb +17 -0
  27. data/test/support/runner.rb +326 -0
  28. data/test/test_helper.rb +13 -0
  29. data/test/webbed/generic_message_test.rb +44 -0
  30. data/test/webbed/headers_test.rb +31 -0
  31. data/test/webbed/helpers/entity_headers_helper_test.rb +68 -0
  32. data/test/webbed/helpers/method_helper_test.rb +151 -0
  33. data/test/webbed/helpers/rack_request_helper_test.rb +108 -0
  34. data/test/webbed/helpers/rack_response_helper_test.rb +33 -0
  35. data/test/webbed/helpers/request_headers_helper_test.rb +57 -0
  36. data/test/webbed/helpers/request_uri_helper_test.rb +32 -0
  37. data/test/webbed/helpers/response_headers_helper_test.rb +46 -0
  38. data/test/webbed/helpers/scheme_helper_test.rb +28 -0
  39. data/test/webbed/http_version_test.rb +52 -0
  40. data/test/webbed/media_type_test.rb +100 -0
  41. data/test/webbed/method_test.rb +160 -0
  42. data/test/webbed/request_test.rb +74 -0
  43. data/test/webbed/response_test.rb +86 -0
  44. data/test/webbed/status_code_test.rb +105 -0
  45. data/webbed.gemspec +31 -0
  46. metadata +128 -41
data/.gitignore ADDED
@@ -0,0 +1,22 @@
1
+ coverage
2
+ rdoc
3
+ pkg
4
+ test/tmp
5
+ test/version_tmp
6
+ tmp
7
+ pkg
8
+ *.gem
9
+ *.rbc
10
+ lib/bundler/man
11
+ spec/reports
12
+ .config
13
+ InstalledFiles
14
+ .bundle
15
+
16
+ # YARD artifacts
17
+ .yardoc
18
+ _yardoc
19
+ doc/
20
+
21
+ # Gem-specific
22
+ Gemfile.lock
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ script: 'rake test'
2
+ rvm:
3
+ - 1.8.7
4
+ - 1.9.2
5
+ - ree
6
+ - rbx
7
+ - jruby
data/.yardopts ADDED
@@ -0,0 +1,6 @@
1
+ --readme README.md
2
+ --markup markdown
3
+ --markup-provider maruku
4
+ --default-return ""
5
+ --title "Webbed Documentation"
6
+ --hide-void-return
data/Gemfile CHANGED
@@ -1,15 +1,7 @@
1
- source 'http://rubygems.org'
1
+ source :gemcutter
2
2
 
3
- gem 'webbed', :path => File.expand_path("..", __FILE__)
4
- gem 'addressable', '~> 2.1'
3
+ # This is temporary until guard-minitest accepts my patch.
4
+ gem 'guard-minitest', :git => 'git://github.com/CapnKernul/guard-minitest.git'
5
+ gem 'rb-fsevent'
5
6
 
6
- group :development do
7
- gem 'rake'
8
- end
9
-
10
- group :test do
11
- gem 'rspec', '~> 1.3', :require => 'spec'
12
- gem 'bourne'
13
- gem 'heckle'
14
- gem 'ruby2ruby', '1.2.2'
15
- end
7
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,5 @@
1
+ guard 'minitest', :notify => true do
2
+ watch(/^test\/(.*)_test.rb/)
3
+ watch(/^lib\/(.*)\.rb/) { |m| "test/#{m[1]}_test.rb" }
4
+ watch(/^test\/test_helper.rb/) { 'test' }
5
+ end
data/README.md CHANGED
@@ -1,32 +1,37 @@
1
- Webbed
2
- ======
3
- Take control of HTTP.
4
-
5
- Note on Releases
6
- ----------------
7
- Releases are tagged on GitHub and also released as gems on RubyGems. The branch `master` is pushed to whenever I add a patch or a new feature. To build from master, you can clone the code, generate the updated `gemspec`, build the gem and install.
8
-
9
- * `rake gemspec`
10
- * `gem build webbed.gemspec`
11
- * `gem install` the gem that was built
12
-
13
- Note on Patches/Pull Requests
14
- -----------------------------
15
- * Fork the project.
16
- * Make your feature addition or bug fix.
17
- * Add tests for it. This is important so I don't break it in a future version unintentionally.
18
- * Commit, do not mess with the `Rakefile`, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself in another branch so I can ignore when I pull)
19
- * Send me a pull request. Bonus points for topic branches.
20
-
21
- Requirements
22
- ------------
23
- * [Bundler](http://github.com/carlhuda/bundler)
24
- * [Addressable](http://github.com/sporkmonger/addressable)
25
-
26
- Install
27
- -------
28
- * `gem install webbed`
29
-
30
- License
31
- -------
1
+ # webbed - take control of HTTP [![StillMaintained Status](http://stillmaintained.com/CapnKernul/webbed.png)](http://stillmaintained.com/CapnKernul/webbed) [![Build Status](http://travis-ci.org/CapnKernul/webbed.png)](http://travis-ci.org/CapnKernul/webbed) #
2
+
3
+ Webbed provides useful abstractions on top of the basic parts of the HTTP
4
+ specification, most notably HTTP Requests and Responses. It is far more
5
+ full-featured than `Rack::Request` and `Rack::Response`, allowing you to easily
6
+ access the complex features of HTTP.
7
+
8
+ ## Usage ##
9
+
10
+ Coming soon. Check out the tests and documentation in the meantime.
11
+
12
+ ## Installation ##
13
+
14
+ Without bundler:
15
+
16
+ gem install webbed
17
+
18
+ With bundler:
19
+
20
+ gem 'webbed'
21
+
22
+ ## Note on Patches/Pull Requests ##
23
+
24
+ * Fork the project.
25
+ * Make your feature addition or bug fix.
26
+ * Add tests for it. This is important so I don't break it in a future version unintentionally.
27
+ * Commit, but do not mess with the `Rakefile`. If you want to have your own version, that is fine but bump the version in a commit by itself in another branch so I can ignore it when I pull.
28
+ * Send me a pull request. Bonus points for git flow feature branches.
29
+
30
+ ## Resources ##
31
+
32
+ * [GitHub Repository](https://github.com/CapnKernul/webbed)
33
+ * [Documentation](http://rubydoc.info/github/CapnKernul/webbed)
34
+
35
+ ## License ##
36
+
32
37
  Webbed is licensed under the MIT License. See `LICENSE` for details.
data/Rakefile CHANGED
@@ -1,18 +1,11 @@
1
1
  require 'bundler'
2
- Bundler.setup :default, :development, :test
2
+ Bundler::GemHelper.install_tasks
3
3
 
4
- require 'webbed'
5
- require 'spec/rake/spectask'
4
+ task :default => :test
6
5
 
7
- Spec::Rake::SpecTask.new do |t|
8
- t.libs << 'lib'
9
- end
10
-
11
- gemspec = eval(File.read('webbed.gemspec'))
12
-
13
- task :build => "#{gemspec.full_name}.gem"
14
-
15
- file "#{gemspec.full_name}.gem" => gemspec.files + ['webbed.gemspec'] do
16
- system 'gem build webbed.gemspec'
17
- system "gem install webbed-#{Webbed::VERSION}.gem"
6
+ require 'rake/testtask'
7
+ Rake::TestTask.new do |t|
8
+ t.ruby_opts += ['-rubygems']
9
+ t.libs << 'test'
10
+ t.pattern = 'test/**/*_test.rb'
18
11
  end
data/lib/webbed.rb CHANGED
@@ -1,9 +1,31 @@
1
+ # Webbed provides two important abstraction on the HTTP specification (RFC
2
+ # 2616), {Request} and {Response}. Although the core classes have the
3
+ # bare-minimum of functionality for interacting with the two classes, they have
4
+ # been extended with a variety of helper modules, adding a significant amount of
5
+ # semantic meaning to the instances of each class.
6
+ #
7
+ # Webbed can be used with or without Rack, but it includes Rack helpers for
8
+ # convenience.
9
+ #
10
+ # @todo Add exmaples of webbed in action.
1
11
  module Webbed
2
- autoload :Headers, 'webbed/headers'
3
- autoload :HTTPVersion, 'webbed/http_version'
12
+ autoload :Headers, 'webbed/headers'
13
+ autoload :HTTPVersion, 'webbed/http_version'
4
14
  autoload :GenericMessage, 'webbed/generic_message'
5
- autoload :Method, 'webbed/method'
6
- autoload :StatusCode, 'webbed/status_code'
7
- autoload :Request, 'webbed/request'
8
- autoload :Response, 'webbed/response'
15
+ autoload :MediaType, 'webbed/media_type'
16
+ autoload :Method, 'webbed/method'
17
+ autoload :StatusCode, 'webbed/status_code'
18
+ autoload :Request, 'webbed/request'
19
+ autoload :Response, 'webbed/response'
20
+
21
+ module Helpers
22
+ autoload :MethodHelper, 'webbed/helpers/method_helper'
23
+ autoload :RackRequestHelper, 'webbed/helpers/rack_request_helper'
24
+ autoload :RackResponseHelper, 'webbed/helpers/rack_response_helper'
25
+ autoload :RequestURIHelper, 'webbed/helpers/request_uri_helper'
26
+ autoload :SchemeHelper, 'webbed/helpers/scheme_helper'
27
+ autoload :RequestHeadersHelper, 'webbed/helpers/request_headers_helper'
28
+ autoload :ResponseHeadersHelper, 'webbed/helpers/response_headers_helper'
29
+ autoload :EntityHeadersHelper, 'webbed/helpers/entity_headers_helper'
30
+ end
9
31
  end
@@ -1,28 +1,40 @@
1
1
  module Webbed
2
+ # Generic methods used for both {Request}'s and {Response}'s.
3
+ #
4
+ # @abstract Include and implement `#status_line`.
2
5
  module GenericMessage
3
-
6
+ # The HTTP-Version of the message.
7
+ #
8
+ # The method automatically converts the new value to an instance of
9
+ # {HTTPVersion} if it is not already one.
10
+ #
11
+ # @return [HTTPVersion]
4
12
  attr_reader :http_version
5
- attr_accessor :entity_body
6
-
7
- # This is purely for test/spec purposes.
8
- def initialize
9
- self.http_version = 'HTTP/1.1'
10
- self.entity_body = ''
11
- end
12
-
13
- # Must be overridden!
14
- def start_line
15
- "Invalid Start Line\r\n"
16
- end
17
13
 
18
14
  def http_version=(http_version)
19
15
  @http_version = Webbed::HTTPVersion.new(http_version)
20
16
  end
21
17
 
22
- def headers
23
- @headers ||= Webbed::Headers.new
18
+ # The Headers of the message.
19
+ #
20
+ # The method automatically converts the new value to an instance of
21
+ # {Headers} if it is not already one.
22
+ #
23
+ # @return [Headers]
24
+ attr_reader :headers
25
+
26
+ def headers=(headers)
27
+ @headers = Webbed::Headers.new(headers)
24
28
  end
25
29
 
30
+ # The Entity Body of the message.
31
+ #
32
+ # @return [#to_s]
33
+ attr_accessor :entity_body
34
+
35
+ # Converts the message into an HTTP Message as per RFC 2616.
36
+ #
37
+ # @return [String]
26
38
  def to_s
27
39
  "#{start_line}#{headers.to_s}\r\n#{entity_body}"
28
40
  end
@@ -1,4 +1,6 @@
1
1
  module Webbed
2
+ # Representation of HTTP headers.
3
+ #
2
4
  # This class is stolen directly from Rack.
3
5
  class Headers < Hash
4
6
  def self.new(hash={})
@@ -0,0 +1,62 @@
1
+ module Webbed
2
+ module Helpers
3
+ # Request and Response helper for Entity Headers
4
+ module EntityHeadersHelper
5
+ # The Content-Length of the Entity (as defined in the Content-Length Header).
6
+ #
7
+ # @return [Fixnum, nil]
8
+ def content_length
9
+ headers['Content-Length'] ? headers['Content-Length'].to_i : nil
10
+ end
11
+
12
+ # Sets the Content-Length of the Entity (as defined in the Content-Length Header).
13
+ #
14
+ # @param [#to_s] content_length
15
+ def content_length=(content_length)
16
+ headers['Content-Length'] = content_length.to_s
17
+ end
18
+
19
+ # The Content-Location of the Entity (as defined in the Content-Location Header).
20
+ #
21
+ # @return [Addressable::URI, nil]
22
+ def content_location
23
+ headers['Content-Location'] ? Addressable::URI.parse(headers['Content-Location']) : nil
24
+ end
25
+
26
+ # Sets the Content-Location of the Entity (as defined in the Content-Location Header).
27
+ #
28
+ # @param [#to_s] content_location
29
+ def content_location=(content_location)
30
+ headers['Content-Location'] = content_location.to_s
31
+ end
32
+
33
+ # The Content-MD5 of the Entity (as defined in the Content-MD5 Header).
34
+ #
35
+ # @return [String, nil]
36
+ def content_md5
37
+ headers['Content-MD5']
38
+ end
39
+
40
+ # Sets the Content-MD5 of the Entity (as defined in the Content-MD5 Header).
41
+ #
42
+ # @param [String] content_md5
43
+ def content_md5=(content_md5)
44
+ headers['Content-MD5'] = content_md5
45
+ end
46
+
47
+ # The Content-Type of the Entity (as defined in the Content-Type Header).
48
+ #
49
+ # @return [MediaType, nil]
50
+ def content_type
51
+ headers['Content-Type'] ? Webbed::MediaType.new(headers['Content-Type']) : nil
52
+ end
53
+
54
+ # Sets the Content-Type of the Entity (as defined in the Content-Type Header).
55
+ #
56
+ # @param [#to_s] content_type
57
+ def content_type=(content_type)
58
+ headers['Content-Type'] = content_type.to_s
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,83 @@
1
+ module Webbed
2
+ module Helpers
3
+ # Request helper for the Method
4
+ module MethodHelper
5
+ # Whether or not the Request is safe based on the Method
6
+ #
7
+ # @return [Boolean]
8
+ def safe?
9
+ method.safe?
10
+ end
11
+
12
+ # Whether or not the Request is idempotent based on the Method
13
+ #
14
+ # @return [Boolean]
15
+ def idempotent?
16
+ method.idempotent?
17
+ end
18
+
19
+ # Whether or not the Request uses the OPTIONS Method
20
+ #
21
+ # @return [Boolean]
22
+ def options?
23
+ method == 'OPTIONS'
24
+ end
25
+
26
+ # Whether or not the Request uses the GET Method
27
+ #
28
+ # @return [Boolean]
29
+ def get?
30
+ method == 'GET'
31
+ end
32
+
33
+ # Whether or not the Request uses the HEAD Method
34
+ #
35
+ # @return [Boolean]
36
+ def head?
37
+ method == 'HEAD'
38
+ end
39
+
40
+ # Whether or not the Request uses the POST Method
41
+ #
42
+ # @return [Boolean]
43
+ def post?
44
+ method == 'POST'
45
+ end
46
+
47
+ # Whether or not the Request uses the PUT Method
48
+ #
49
+ # @return [Boolean]
50
+ def put?
51
+ method == 'PUT'
52
+ end
53
+
54
+ # Whether or not the Request uses the DELETE Method
55
+ #
56
+ # @return [Boolean]
57
+ def delete?
58
+ method == 'DELETE'
59
+ end
60
+
61
+ # Whether or not the Request uses the TRACE Method
62
+ #
63
+ # @return [Boolean]
64
+ def trace?
65
+ method == 'TRACE'
66
+ end
67
+
68
+ # Whether or not the Request uses the CONNECT Method
69
+ #
70
+ # @return [Boolean]
71
+ def connect?
72
+ method == 'CONNECT'
73
+ end
74
+
75
+ # Whether or not the Request uses the PATCH Method
76
+ #
77
+ # @return [Boolean]
78
+ def patch?
79
+ method == 'PATCH'
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,86 @@
1
+ module Webbed
2
+ module Helpers
3
+ # Request helper for converting Rack environments into Requests
4
+ module RackRequestHelper
5
+ module ClassMethods
6
+ # Create a new Request from a Rack environment
7
+ #
8
+ # This method will take the HTTP-Version, Method, Request-URI, Headers,
9
+ # Entity Body, and scheme from the Rack environment and create a new
10
+ # Request using that data. The Rack environment can be accessed through
11
+ # `#rack_env`.
12
+ #
13
+ # @param [Hash{String => String}] rack_env
14
+ # @return [Request]
15
+ # @note The Rack environment will never be modified.
16
+ def from_rack(rack_env)
17
+ env = rack_env.dup
18
+ method = env.delete('REQUEST_METHOD')
19
+ request_uri = "#{env['PATH_INFO']}?#{env['QUERY_STRING']}"
20
+ http_version = env.delete('HTTP_VERSION') || 'HTTP/1.1'
21
+ scheme = env.delete('rack.url_scheme')
22
+ entity_body = env.delete('rack.input')
23
+
24
+ headers = env.inject({}) do |memo, h|
25
+ memo[$1.gsub('_', '-')] = h[1] if h[0] =~ /^HTTP_(.*)/
26
+ memo
27
+ end
28
+
29
+ headers['Content-Type'] = env.delete('CONTENT_TYPE')
30
+ headers['Content-Length'] = env.delete('CONTENT_LENGTH')
31
+
32
+ request = new(method, request_uri, headers, entity_body, {
33
+ :http_version => http_version,
34
+ :scheme => scheme
35
+ })
36
+
37
+ request.rack_env = rack_env
38
+
39
+ request
40
+ end
41
+ end
42
+
43
+ module InstanceMethods
44
+ # The Rack environment of the Request
45
+ #
46
+ # @return [Hash{String => String}]
47
+ attr_accessor :rack_env
48
+
49
+ # Generate the Rack enivronment equivalent of the Request.
50
+ #
51
+ # If the Request was created with an environment, the generated
52
+ # environment will be merged with the original environment. The original
53
+ # will not be modified.
54
+ #
55
+ # @return [Hash{String => String}]
56
+ def to_rack
57
+ env = rack_env ? rack_env.dup : {}
58
+ env['REQUEST_METHOD'] = method.to_s
59
+ env['PATH_INFO'] = request_uri.path
60
+ env['QUERY_STRING'] = request_uri.query
61
+ env['HTTP_VERSION'] = http_version.to_s
62
+ env['rack.url_scheme'] = scheme
63
+ env['rack.input'] = entity_body
64
+
65
+ rack_headers = headers.dup
66
+ env['CONTENT_TYPE'] = rack_headers.delete('Content-Type')
67
+ env['CONTENT_LENGTH'] = rack_headers.delete('Content-Length')
68
+
69
+ rack_headers.each do |header, value|
70
+ header = header.upcase.gsub('-', '_')
71
+ env["HTTP_#{header}"] = value
72
+ end
73
+
74
+ env
75
+ end
76
+ end
77
+
78
+ # @see ClassMethods
79
+ # @see InstanceMethods
80
+ def self.included(base)
81
+ base.send :include, InstanceMethods
82
+ base.send :extend, ClassMethods
83
+ end
84
+ end
85
+ end
86
+ end