http_status_exceptions 0.1.9

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ http_status_exceptions-*.gem
2
+ /tmp
3
+ /doc
4
+ /pkg
5
+ /coverage
6
+ .DS_Store
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 20082-2009 Willem van Bergen
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,68 @@
1
+ <b>WARNING:</b> the gem version that Github currently serves is faulty. The issue
2
+ is already fixes in the repository, but Github is not yet building new gem
3
+ versions. As soon as this is fixed, I will release a new version that resolves
4
+ the issue. For now, install version 0.1.5 which is unaffected:
5
+
6
+ sudo gem install wvanbergen-http_status_exceptions \
7
+ --source http://gems.github.com --version "= 0.1.5"
8
+
9
+ = HTTP status exception
10
+
11
+ This simple plugin will register exception classes for all HTTP status. These exceptions can then be raised from your controllers, after
12
+ which a response will be send back to the client with the desired HTTP status, possible with some other content.
13
+
14
+ You can use this plugin to access control mechanisms. You can simply raise a HTTPStatus::Forbidden if a user is not allowed to
15
+ perform a certain action. A nice looking error page will be the result. See the example below:
16
+
17
+ See the project wiki (http://github.com/wvanbergen/http_status_exceptions/wikis) for additional documentation.
18
+
19
+ == Installation
20
+
21
+ Installation is simple. Simply add the gem in your <tt>environment.rb</tt>:
22
+
23
+ Rails::Initializer.run do |config|
24
+ ...
25
+ config.gem 'wvanbergen-http_status_exceptions', :lib => 'http_status_exceptions', :source => 'http://gems.github.com'
26
+ end
27
+
28
+ Run <tt>rake gems:install</tt> to install the gem if needed.
29
+
30
+ == Configuration
31
+
32
+ You can modify where HTTP status exception looks for its template files like so:
33
+
34
+ class ApplicationController < ActionController::Base
35
+ ...
36
+ HTTPStatus::Base.template_path = 'path_to/http_status_templates'
37
+ end
38
+
39
+ You can also modify which layout is used when rendering a template by setting the <tt>template_layout</tt>:
40
+
41
+ class ApplicationController < ActionController::Base
42
+ ...
43
+ HTTPStatus::Base.template_layout = 'exception'
44
+ end
45
+
46
+ If you don't set a template_layout the current layout for the requested action will be used.
47
+
48
+ == Usage
49
+
50
+ class BlogController < ApplicationController
51
+
52
+ def destroy
53
+ raise HTTPStatus::Forbidden, 'You cannot delete blogs!' unless current_user.can_delete_blogs?
54
+ @blog.destroy
55
+ end
56
+ end
57
+
58
+ By default, this will return an empty response with the "forbidden" status code (403). If you want to add content
59
+ to the response as well, create the following view: <tt>shared/http_status/forbidden.html.erb</tt>. You can use the
60
+ <tt>@exception</tt>-object in your view:
61
+
62
+ <h1>Forbidden</h1>
63
+ <p> <%= h(@exception.message) %> </p>
64
+ <hr />
65
+ <p>HTTP status code <small> <%= @exception.status_code %>: <%= @exception.status.to_s.humanize %></small></p>
66
+
67
+ The response will only be sent if the request format is HTML because of the name of the view file. In theory you
68
+ could make a response for XML requests as well by using <tt>shared/http_status/forbidden.xml.builder</tt> as filename
data/Rakefile ADDED
@@ -0,0 +1,5 @@
1
+ Dir[File.dirname(__FILE__) + "/tasks/*.rake"].each { |file| load(file) }
2
+
3
+ GithubGem::RakeTasks.new(:gem)
4
+
5
+ task :default => :spec
@@ -0,0 +1,23 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'http_status_exceptions'
3
+
4
+ # Do not update the version and date values by hand.
5
+ # This will be done automatically by the gem release script.
6
+ s.version = "0.1.9"
7
+ s.date = "2009-10-01"
8
+
9
+ s.summary = "A Rails plugin to use exceptions for generating HTTP status responses"
10
+ s.description = "Clean up your controller code by raising exceptions that generate responses with different HTTP status codes."
11
+
12
+ s.add_runtime_dependency('actionpack', '>= 2.1.0')
13
+ s.add_development_dependency('rspec')
14
+
15
+ s.authors = ['Willem van Bergen']
16
+ s.email = ['willem@vanbergen.org']
17
+ s.homepage = 'http://github.com/wvanbergen/http_status_exceptions/wikis'
18
+
19
+ # Do not update the files and test_files values by hand.
20
+ # This will be done automatically by the gem release script.
21
+ s.files = %w(spec/spec_helper.rb http_status_exceptions.gemspec .gitignore init.rb lib/http_status_exceptions.rb Rakefile MIT-LICENSE tasks/github-gem.rake README.rdoc spec/http_status_exception_spec.rb)
22
+ s.test_files = %w(spec/http_status_exception_spec.rb)
23
+ end
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require 'http_status_exceptions.rb'
@@ -0,0 +1,146 @@
1
+ # The HTTPStatus module is the core of the http_status_exceptions gem and
2
+ # contains all functionality.
3
+ #
4
+ # The module contains <tt>HTTPStatus::Base</tt> class, which is used as a
5
+ # superclass for every HTTPStatus exception. Subclasses, like
6
+ # <tt>HTTPStatus::Forbidden</tt> or <tt>HTTPStatus::NotFound</tt> will be
7
+ # generated on demand by the <tt>HTTPStatus.const_missing</tt> method.
8
+ #
9
+ # Moreover, it contains methods to handle these exceptions and integrate this
10
+ # functionality into <tt>ActionController::Base</tt>. When this module is in
11
+ # included in the <tt>ActionController::Base</tt> class, it will call
12
+ # <tt>rescue_from</tt> on it to handle all <tt>HTTPStatus::Base</tt>
13
+ # exceptions with the <tt>HTTPStatus#http_status_exceptions</tt> method.
14
+ #
15
+ # The exception handler will try to render a response with the correct
16
+ # HTTPStatus. When no suitable template is found to render the exception with,
17
+ # it will simply respond with an empty HTTP status code.
18
+ module HTTPStatus
19
+
20
+ # The current gem release version. Do not set this value by hand, it will
21
+ # be done automatically by them gem release script.
22
+ VERSION = "0.1.9"
23
+
24
+ # The Base HTTP status exception class is used as superclass for every
25
+ # exception class that is constructed. It implements some shared
26
+ # functionality for finding the status code and determining the template
27
+ # path to render.
28
+ #
29
+ # Subclasses of this class will be generated on demand when a non-exisiting
30
+ # constant of the <tt>HTTPStatus</tt> module is requested. This is
31
+ # implemented in the <tt>HTTPStatus.const_missing</tt> method.
32
+ class Base < StandardError
33
+
34
+ # The path from which the error documents are loaded.
35
+ cattr_accessor :template_path
36
+ @@template_path = 'shared/http_status'
37
+
38
+ # The layout in which the error documents are rendered
39
+ cattr_accessor :template_layout
40
+ @@template_layout = nil # Use the standard layout template setting by default.
41
+
42
+ attr_reader :details
43
+
44
+ # Initializes the exception instance.
45
+ # <tt>message</tt>:: The exception message.
46
+ # <tt>details</tt>:: An object with details about the exception.
47
+ def initialize(message = nil, details = nil)
48
+ @details = details
49
+ super(message)
50
+ end
51
+
52
+ # Returns the HTTP status symbol corresponding to this class. This is one
53
+ # of the symbols that can be found in the map that can be found in
54
+ # <tt>ActionController::StatusCodes</tt>.
55
+ #
56
+ # This method should be overridden by subclasses, as it returns
57
+ # <tt>:internal_server_error</tt> by default. This is done automatically
58
+ # when a new exception class is being generated by
59
+ # <tt>HTTPStatus.const_missing</tt>.
60
+ def self.status
61
+ :internal_server_error
62
+ end
63
+
64
+ # Returns the HTTP status symbol (as defined by Rails) corresponding to
65
+ # this instance. By default, it calls the class method of the same name.
66
+ def status
67
+ self.class.status
68
+ end
69
+
70
+ # The numeric status code corresponding to this exception class. Uses the
71
+ # status symbol to code map in <tt>ActionController::StatusCodes</tt>.
72
+ def self.status_code
73
+ ActionController::StatusCodes::SYMBOL_TO_STATUS_CODE[self.status]
74
+ end
75
+
76
+ # The numeric status code corresponding to this exception. By default, it
77
+ # calls the class method of the same name.
78
+ def status_code
79
+ self.class.status_code
80
+ end
81
+
82
+ # The name of the template that should be used as error page for this
83
+ # exception class.
84
+ def self.template
85
+ "#{template_path}/#{status}"
86
+ end
87
+
88
+ # The name of the template that should be used as error page for this
89
+ # exception. By default, it calls the class method of the same name.
90
+ def template
91
+ self.class.template
92
+ end
93
+ end
94
+
95
+ # This function will install a rescue_from handler for HTTPStatus::Base
96
+ # exceptions in the class in which this module is included.
97
+ #
98
+ # <tt>base</tt>:: The class in which the module is included. Should be
99
+ # <tt>ActionController::Base</tt> during the initialization of the gem.
100
+ def self.included(base)
101
+ base.send(:rescue_from, HTTPStatus::Base, :with => :http_status_exception)
102
+ end
103
+
104
+ # Generates a <tt>HTTPStatus::Base</tt> subclass on demand based on the
105
+ # constant name. The constant name should correspond to one of the status
106
+ # symbols defined in <tt>ActionController::StatusCodes</tt>. The function
107
+ # will raise an exception if the constant name cannot be mapped onto one of
108
+ # the status symbols.
109
+ #
110
+ # This method will create a new subclass of <tt>HTTPStatus::Base</tt> and
111
+ # overrides the status class method of the class to return the correct
112
+ # status symbol.
113
+ #
114
+ # <tt>const</tt>:: The name of the missing constant, for which an exception
115
+ # class should be generated.
116
+ def self.const_missing(const)
117
+ status_symbol = const.to_s.underscore.to_sym
118
+ raise "Unrecognized HTTP Status name!" unless ActionController::StatusCodes::SYMBOL_TO_STATUS_CODE.has_key?(status_symbol)
119
+ klass = Class.new(HTTPStatus::Base)
120
+ klass.cattr_accessor(:status)
121
+ klass.status = status_symbol
122
+ const_set(const, klass)
123
+ return const_get(const)
124
+ end
125
+
126
+ # The default handler for raised HTTP status exceptions. It will render a
127
+ # template if available, or respond with an empty response with the HTTP
128
+ # status corresponding to the exception.
129
+ #
130
+ # You can override this method in your <tt>ApplicationController</tt> to
131
+ # handle the exceptions yourself.
132
+ #
133
+ # <tt>exception</tt>:: The HTTP status exception to handle.
134
+ def http_status_exception(exception)
135
+ @exception = exception
136
+ render_options = {:template => exception.template, :status => exception.status}
137
+ render_options[:layout] = exception.template_layout if exception.template_layout
138
+ render(render_options)
139
+ rescue ActionView::MissingTemplate
140
+ head(exception.status)
141
+ end
142
+ end
143
+
144
+ # Include the HTTPStatus module into <tt>ActionController::Base</tt> to enable
145
+ # the <tt>http_status_exception</tt> exception handler.
146
+ ActionController::Base.send(:include, HTTPStatus)
@@ -0,0 +1,97 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe HTTPStatus::Base, 'class inheritance' do
4
+ before(:all) do
5
+ ActionController::StatusCodes::SYMBOL_TO_STATUS_CODE.stub!(:has_key?).with(:testing_status).and_return(true)
6
+ @status_exception_class = HTTPStatus::TestingStatus
7
+ end
8
+
9
+ after(:all) do
10
+ HTTPStatus::Base.template_path = 'shared/http_status'
11
+ HTTPStatus.send :remove_const, 'TestingStatus'
12
+ end
13
+
14
+ it "should set the status symbol based on the class name" do
15
+ @status_exception_class.status.should == :testing_status
16
+ end
17
+
18
+ it "should use 'shared/http_status' as default view path" do
19
+ @status_exception_class.template.should == 'shared/http_status/testing_status'
20
+ end
21
+
22
+ it "should check ActionController's status code list for the status code based on the class name" do
23
+ ActionController::StatusCodes::SYMBOL_TO_STATUS_CODE.should_receive(:[]).with(:testing_status)
24
+ @status_exception_class.status_code
25
+ end
26
+
27
+ it "should use the HTTPStatus::Base.template_path setting to determine the error template" do
28
+ HTTPStatus::Base.template_path = 'testing'
29
+ @status_exception_class.template.should == 'testing/testing_status'
30
+ end
31
+
32
+ it "should raise an exception when the class name does not correspond to a HTTP status code" do
33
+ lambda { HTTPStatus::Nonsense }.should raise_error
34
+ end
35
+ end
36
+
37
+ # Run some tests for different valid subclasses.
38
+ { 'NotFound' => 404, 'Forbidden' => 403, 'PaymentRequired' => 402, 'InternalServerError' => 500}.each do |status_class, status_code|
39
+
40
+ describe "HTTPStatus::#{status_class}" do
41
+ it "should generate the HTTPStatus::#{status_class} class successfully" do
42
+ lambda { HTTPStatus.const_get(status_class) }.should_not raise_error
43
+ end
44
+
45
+ it "should create a subclass of HTTPStatus::Base for the #{status_class.underscore.humanize.downcase} status" do
46
+ HTTPStatus.const_get(status_class).ancestors.should include(HTTPStatus::Base)
47
+ end
48
+
49
+ it "should return the correct status code (#{status_code}) when using the class" do
50
+ HTTPStatus.const_get(status_class).status_code.should == status_code
51
+ end
52
+
53
+ it "should return the correct status code (#{status_code}) when using the instance" do
54
+ HTTPStatus.const_get(status_class).new.status_code.should == status_code
55
+ end
56
+ end
57
+ end
58
+
59
+ describe 'HTTPStatus#http_status_exception' do
60
+ before(:each) { @controller = Class.new(ActionController::Base).new }
61
+ after(:each) { HTTPStatus::Base.template_layout = nil}
62
+
63
+ it "should create the :http_status_exception method in ActionController" do
64
+ @controller.should respond_to(:http_status_exception)
65
+ end
66
+
67
+ it "should call :http_status_exception when an exception is raised when handling the action" do
68
+ exception = HTTPStatus::Base.new('test')
69
+ @controller.stub!(:perform_action_without_rescue).and_raise(exception)
70
+ @controller.should_receive(:http_status_exception).with(exception)
71
+ @controller.send(:perform_action)
72
+ end
73
+
74
+ it "should call render with the correct view and correct HTTP status" do
75
+ @controller.should_receive(:render).with(hash_including(
76
+ :status => :internal_server_error, :template => "shared/http_status/internal_server_error"))
77
+
78
+ @controller.http_status_exception(HTTPStatus::Base.new('test'))
79
+ end
80
+
81
+ it "should not call render with a layout by default" do
82
+ @controller.should_not_receive(:render).with(hash_including(:layout => 'testing'))
83
+ @controller.http_status_exception(HTTPStatus::Base.new('test'))
84
+ end
85
+
86
+ it "should call render with a layout set when this property is set on the exception class" do
87
+ @controller.should_receive(:render).with(hash_including(:layout => 'testing'))
88
+ HTTPStatus::Base.template_layout = 'testing'
89
+ @controller.http_status_exception(HTTPStatus::Base.new('test'))
90
+ end
91
+
92
+ it "should call head with the correct status code if render cannot found a template" do
93
+ @controller.stub!(:render).and_raise(ActionView::MissingTemplate.new([], 'template.html.erb'))
94
+ @controller.should_receive(:head).with(:internal_server_error)
95
+ @controller.http_status_exception(HTTPStatus::Base.new('test'))
96
+ end
97
+ end
@@ -0,0 +1,17 @@
1
+ $: << File.join(File.dirname(__FILE__), '..', 'lib')
2
+
3
+ require 'rubygems'
4
+ require 'spec/autorun'
5
+
6
+ require 'action_controller'
7
+
8
+ require 'http_status_exceptions'
9
+
10
+ # Include all files in the spec_helper directory
11
+ Dir[File.dirname(__FILE__) + "/lib/**/*.rb"].each do |file|
12
+ require file
13
+ end
14
+
15
+ Spec::Runner.configure do |config|
16
+ # nothing special
17
+ end
@@ -0,0 +1,323 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'rake/tasklib'
4
+ require 'date'
5
+ require 'git'
6
+
7
+ module GithubGem
8
+
9
+ # Detects the gemspc file of this project using heuristics.
10
+ def self.detect_gemspec_file
11
+ FileList['*.gemspec'].first
12
+ end
13
+
14
+ # Detects the main include file of this project using heuristics
15
+ def self.detect_main_include
16
+ if detect_gemspec_file =~ /^(\.*)\.gemspec$/ && File.exist?("lib/#{$1}.rb")
17
+ "lib/#{$1}.rb"
18
+ elsif FileList['lib/*.rb'].length == 1
19
+ FileList['lib/*.rb'].first
20
+ else
21
+ nil
22
+ end
23
+ end
24
+
25
+ class RakeTasks
26
+
27
+ attr_reader :gemspec, :modified_files, :git
28
+ attr_accessor :gemspec_file, :task_namespace, :main_include, :root_dir, :spec_pattern, :test_pattern, :remote, :remote_branch, :local_branch
29
+
30
+ # Initializes the settings, yields itself for configuration
31
+ # and defines the rake tasks based on the gemspec file.
32
+ def initialize(task_namespace = :gem)
33
+ @gemspec_file = GithubGem.detect_gemspec_file
34
+ @task_namespace = task_namespace
35
+ @main_include = GithubGem.detect_main_include
36
+ @modified_files = []
37
+ @root_dir = Dir.pwd
38
+ @test_pattern = 'test/**/*_test.rb'
39
+ @spec_pattern = 'spec/**/*_spec.rb'
40
+ @local_branch = 'master'
41
+ @remote = 'origin'
42
+ @remote_branch = 'master'
43
+
44
+ yield(self) if block_given?
45
+
46
+ @git = Git.open(@root_dir)
47
+ load_gemspec!
48
+ define_tasks!
49
+ end
50
+
51
+ protected
52
+
53
+ # Define Unit test tasks
54
+ def define_test_tasks!
55
+ require 'rake/testtask'
56
+
57
+ namespace(:test) do
58
+ Rake::TestTask.new(:basic) do |t|
59
+ t.pattern = test_pattern
60
+ t.verbose = true
61
+ t.libs << 'test'
62
+ end
63
+ end
64
+
65
+ desc "Run all unit tests for #{gemspec.name}"
66
+ task(:test => ['test:basic'])
67
+ end
68
+
69
+ # Defines RSpec tasks
70
+ def define_rspec_tasks!
71
+ require 'spec/rake/spectask'
72
+
73
+ namespace(:spec) do
74
+ desc "Verify all RSpec examples for #{gemspec.name}"
75
+ Spec::Rake::SpecTask.new(:basic) do |t|
76
+ t.spec_files = FileList[spec_pattern]
77
+ end
78
+
79
+ desc "Verify all RSpec examples for #{gemspec.name} and output specdoc"
80
+ Spec::Rake::SpecTask.new(:specdoc) do |t|
81
+ t.spec_files = FileList[spec_pattern]
82
+ t.spec_opts << '--format' << 'specdoc' << '--color'
83
+ end
84
+
85
+ desc "Run RCov on specs for #{gemspec.name}"
86
+ Spec::Rake::SpecTask.new(:rcov) do |t|
87
+ t.spec_files = FileList[spec_pattern]
88
+ t.rcov = true
89
+ t.rcov_opts = ['--exclude', '"spec/*,gems/*"', '--rails']
90
+ end
91
+ end
92
+
93
+ desc "Verify all RSpec examples for #{gemspec.name} and output specdoc"
94
+ task(:spec => ['spec:specdoc'])
95
+ end
96
+
97
+ # Defines the rake tasks
98
+ def define_tasks!
99
+
100
+ define_test_tasks! if has_tests?
101
+ define_rspec_tasks! if has_specs?
102
+
103
+ namespace(@task_namespace) do
104
+ desc "Updates the filelist in the gemspec file"
105
+ task(:manifest) { manifest_task }
106
+
107
+ desc "Builds the .gem package"
108
+ task(:build => :manifest) { build_task }
109
+
110
+ desc "Sets the version of the gem in the gemspec"
111
+ task(:set_version => [:check_version, :check_current_branch]) { version_task }
112
+ task(:check_version => :fetch_origin) { check_version_task }
113
+
114
+ task(:fetch_origin) { fetch_origin_task }
115
+ task(:check_current_branch) { check_current_branch_task }
116
+ task(:check_clean_status) { check_clean_status_task }
117
+ task(:check_not_diverged => :fetch_origin) { check_not_diverged_task }
118
+
119
+ checks = [:check_current_branch, :check_clean_status, :check_not_diverged, :check_version]
120
+ checks.unshift('spec:basic') if has_specs?
121
+ checks.unshift('test:basic') if has_tests?
122
+ checks.push << [:check_rubyforge] if gemspec.rubyforge_project
123
+
124
+ desc "Perform all checks that would occur before a release"
125
+ task(:release_checks => checks)
126
+
127
+ release_tasks = [:release_checks, :set_version, :build, :github_release]
128
+ release_tasks << [:rubyforge_release] if gemspec.rubyforge_project
129
+
130
+ desc "Release a new verison of the gem"
131
+ task(:release => release_tasks) { release_task }
132
+
133
+ task(:check_rubyforge) { check_rubyforge_task }
134
+ task(:rubyforge_release) { rubyforge_release_task }
135
+ task(:github_release => [:commit_modified_files, :tag_version]) { github_release_task }
136
+ task(:tag_version) { tag_version_task }
137
+ task(:commit_modified_files) { commit_modified_files_task }
138
+
139
+ desc "Updates the gem release tasks with the latest version on Github"
140
+ task(:update_tasks) { update_tasks_task }
141
+ end
142
+ end
143
+
144
+ # Updates the files list and test_files list in the gemspec file using the list of files
145
+ # in the repository and the spec/test file pattern.
146
+ def manifest_task
147
+ # Load all the gem's files using "git ls-files"
148
+ repository_files = git.ls_files.keys
149
+ test_files = Dir[test_pattern] + Dir[spec_pattern]
150
+
151
+ update_gemspec(:files, repository_files)
152
+ update_gemspec(:test_files, repository_files & test_files)
153
+ end
154
+
155
+ # Builds the gem
156
+ def build_task
157
+ sh "gem build -q #{gemspec_file}"
158
+ Dir.mkdir('pkg') unless File.exist?('pkg')
159
+ sh "mv #{gemspec.name}-#{gemspec.version}.gem pkg/#{gemspec.name}-#{gemspec.version}.gem"
160
+ end
161
+
162
+ # Updates the version number in the gemspec file, the VERSION constant in the main
163
+ # include file and the contents of the VERSION file.
164
+ def version_task
165
+ update_gemspec(:version, ENV['VERSION']) if ENV['VERSION']
166
+ update_gemspec(:date, Date.today)
167
+
168
+ update_version_file(gemspec.version)
169
+ update_version_constant(gemspec.version)
170
+ end
171
+
172
+ def check_version_task
173
+ raise "#{ENV['VERSION']} is not a valid version number!" if ENV['VERSION'] && !Gem::Version.correct?(ENV['VERSION'])
174
+ proposed_version = Gem::Version.new(ENV['VERSION'] || gemspec.version)
175
+ # Loads the latest version number using the created tags
176
+ newest_version = git.tags.map { |tag| tag.name.split('-').last }.compact.map { |v| Gem::Version.new(v) }.max
177
+ raise "This version (#{proposed_version}) is not higher than the highest tagged version (#{newest_version})" if newest_version && newest_version >= proposed_version
178
+ end
179
+
180
+ # Checks whether the current branch is not diverged from the remote branch
181
+ def check_not_diverged_task
182
+ raise "The current branch is diverged from the remote branch!" if git.log.between('HEAD', git.remote(remote).branch(remote_branch).gcommit).any?
183
+ end
184
+
185
+ # Checks whether the repository status ic clean
186
+ def check_clean_status_task
187
+ raise "The current working copy contains modifications" if git.status.changed.any?
188
+ end
189
+
190
+ # Checks whether the current branch is correct
191
+ def check_current_branch_task
192
+ raise "Currently not on #{local_branch} branch!" unless git.branch.name == local_branch.to_s
193
+ end
194
+
195
+ # Fetches the latest updates from Github
196
+ def fetch_origin_task
197
+ git.fetch('origin')
198
+ end
199
+
200
+ # Commits every file that has been changed by the release task.
201
+ def commit_modified_files_task
202
+ if modified_files.any?
203
+ modified_files.each { |file| git.add(file) }
204
+ git.commit("Released #{gemspec.name} gem version #{gemspec.version}")
205
+ end
206
+ end
207
+
208
+ # Adds a tag for the released version
209
+ def tag_version_task
210
+ git.add_tag("#{gemspec.name}-#{gemspec.version}")
211
+ end
212
+
213
+ # Pushes the changes and tag to github
214
+ def github_release_task
215
+ git.push(remote, remote_branch, true)
216
+ end
217
+
218
+ # Checks whether Rubyforge is configured properly
219
+ def check_rubyforge_task
220
+ # Login no longer necessary when using rubyforge 2.0.0 gem
221
+ # raise "Could not login on rubyforge!" unless `rubyforge login 2>&1`.strip.empty?
222
+ output = `rubyforge names`.split("\n")
223
+ raise "Rubyforge group not found!" unless output.any? { |line| %r[^groups\s*\:.*\b#{Regexp.quote(gemspec.rubyforge_project)}\b.*] =~ line }
224
+ raise "Rubyforge package not found!" unless output.any? { |line| %r[^packages\s*\:.*\b#{Regexp.quote(gemspec.name)}\b.*] =~ line }
225
+ end
226
+
227
+ # Task to release the .gem file toRubyforge.
228
+ def rubyforge_release_task
229
+ sh 'rubyforge', 'add_release', gemspec.rubyforge_project, gemspec.name, gemspec.version.to_s, "pkg/#{gemspec.name}-#{gemspec.version}.gem"
230
+ end
231
+
232
+ # Gem release task.
233
+ # All work is done by the task's dependencies, so just display a release completed message.
234
+ def release_task
235
+ puts
236
+ puts '------------------------------------------------------------'
237
+ puts "Released #{gemspec.name} version #{gemspec.version}"
238
+ end
239
+
240
+ private
241
+
242
+ # Checks whether this project has any RSpec files
243
+ def has_specs?
244
+ FileList[spec_pattern].any?
245
+ end
246
+
247
+ # Checks whether this project has any unit test files
248
+ def has_tests?
249
+ FileList[test_pattern].any?
250
+ end
251
+
252
+ # Loads the gemspec file
253
+ def load_gemspec!
254
+ @gemspec = eval(File.read(@gemspec_file))
255
+ end
256
+
257
+ # Updates the VERSION file with the new version
258
+ def update_version_file(version)
259
+ if File.exists?('VERSION')
260
+ File.open('VERSION', 'w') { |f| f << version.to_s }
261
+ modified_files << 'VERSION'
262
+ end
263
+ end
264
+
265
+ # Updates the VERSION constant in the main include file if it exists
266
+ def update_version_constant(version)
267
+ if main_include && File.exist?(main_include)
268
+ file_contents = File.read(main_include)
269
+ if file_contents.sub!(/^(\s+VERSION\s*=\s*)[^\s].*$/) { $1 + version.to_s.inspect }
270
+ File.open(main_include, 'w') { |f| f << file_contents }
271
+ modified_files << main_include
272
+ end
273
+ end
274
+ end
275
+
276
+ # Updates an attribute of the gemspec file.
277
+ # This function will open the file, and search/replace the attribute using a regular expression.
278
+ def update_gemspec(attribute, new_value, literal = false)
279
+
280
+ unless literal
281
+ new_value = case new_value
282
+ when Array then "%w(#{new_value.join(' ')})"
283
+ when Hash, String then new_value.inspect
284
+ when Date then new_value.strftime('%Y-%m-%d').inspect
285
+ else raise "Cannot write value #{new_value.inspect} to gemspec file!"
286
+ end
287
+ end
288
+
289
+ spec = File.read(gemspec_file)
290
+ regexp = Regexp.new('^(\s+\w+\.' + Regexp.quote(attribute.to_s) + '\s*=\s*)[^\s].*$')
291
+ if spec.sub!(regexp) { $1 + new_value }
292
+ File.open(gemspec_file, 'w') { |f| f << spec }
293
+ modified_files << gemspec_file
294
+
295
+ # Reload the gemspec so the changes are incorporated
296
+ load_gemspec!
297
+ end
298
+ end
299
+
300
+ # Updates the tasks file using the latest file found on Github
301
+ def update_tasks_task
302
+ require 'net/http'
303
+
304
+ server = 'github.com'
305
+ path = '/wvanbergen/github-gem/raw/master/tasks/github-gem.rake'
306
+
307
+ Net::HTTP.start(server) do |http|
308
+ response = http.get(path)
309
+ open(__FILE__, "w") { |file| file.write(response.body) }
310
+ end
311
+
312
+ relative_file = File.expand_path(__FILE__).sub(%r[^#{git.dir.path}/], '')
313
+ if git.status[relative_file] && git.status[relative_file].type == 'M'
314
+ git.add(relative_file)
315
+ git.commit("Updated to latest gem release management tasks.")
316
+ puts "Updated to latest version of gem release management tasks."
317
+ else
318
+ puts "Release managament tasks already are at the latest version."
319
+ end
320
+ end
321
+
322
+ end
323
+ end
metadata ADDED
@@ -0,0 +1,84 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: http_status_exceptions
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.9
5
+ platform: ruby
6
+ authors:
7
+ - Willem van Bergen
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-10-01 00:00:00 +02:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: actionpack
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 2.1.0
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: rspec
27
+ type: :development
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: "0"
34
+ version:
35
+ description: Clean up your controller code by raising exceptions that generate responses with different HTTP status codes.
36
+ email:
37
+ - willem@vanbergen.org
38
+ executables: []
39
+
40
+ extensions: []
41
+
42
+ extra_rdoc_files: []
43
+
44
+ files:
45
+ - spec/spec_helper.rb
46
+ - http_status_exceptions.gemspec
47
+ - .gitignore
48
+ - init.rb
49
+ - lib/http_status_exceptions.rb
50
+ - Rakefile
51
+ - MIT-LICENSE
52
+ - tasks/github-gem.rake
53
+ - README.rdoc
54
+ - spec/http_status_exception_spec.rb
55
+ has_rdoc: true
56
+ homepage: http://github.com/wvanbergen/http_status_exceptions/wikis
57
+ licenses: []
58
+
59
+ post_install_message:
60
+ rdoc_options: []
61
+
62
+ require_paths:
63
+ - lib
64
+ required_ruby_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: "0"
69
+ version:
70
+ required_rubygems_version: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: "0"
75
+ version:
76
+ requirements: []
77
+
78
+ rubyforge_project:
79
+ rubygems_version: 1.3.5
80
+ signing_key:
81
+ specification_version: 3
82
+ summary: A Rails plugin to use exceptions for generating HTTP status responses
83
+ test_files:
84
+ - spec/http_status_exception_spec.rb