final 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (8) hide show
  1. data/CHANGES +2 -0
  2. data/MANIFEST +7 -0
  3. data/README +62 -0
  4. data/Rakefile +27 -0
  5. data/final.gemspec +24 -0
  6. data/lib/final.rb +38 -0
  7. data/test/test_final.rb +44 -0
  8. metadata +90 -0
data/CHANGES ADDED
@@ -0,0 +1,2 @@
1
+ == 0.1.0 - 8-Apr-2011
2
+ * Initial release
data/MANIFEST ADDED
@@ -0,0 +1,7 @@
1
+ * CHANGES
2
+ * README
3
+ * MANIFEST
4
+ * Rakefile
5
+ * final.gemspec
6
+ * lib/final.rb
7
+ * test/test_final.rb
data/README ADDED
@@ -0,0 +1,62 @@
1
+ = Description
2
+ A Ruby module that can make your classes final. A final class cannot
3
+ be subclassed, and its instance methods cannot be redefined.
4
+
5
+ = Synopsis
6
+ require 'final'
7
+
8
+ class Alpha
9
+ include Final
10
+
11
+ def foo
12
+ puts "Hello"
13
+ end
14
+ end
15
+
16
+ # Error! You cannot subclass Alpha.
17
+ class Beta < Alpha; end
18
+
19
+ # Error! You cannot redefine an existing method.
20
+ class Alpha
21
+ def foo
22
+ puts "Hi"
23
+ end
24
+ end
25
+
26
+ # Not an error. You can add new methods to an existing class.
27
+ class Alpha
28
+ def bar
29
+ puts "World"
30
+ end
31
+ end
32
+
33
+ = Developer's Notes
34
+ This library is merely a proof of concept that was published as the result
35
+ of a tweet made by James Edward Gray II, where he commented that Ruby core
36
+ classes should never be subclassed. Knowing that static languages like Java
37
+ implement the "final" keyword as a way to prevent subclassing, I wanted to
38
+ see if something similar could be done for Ruby. Turns out it can.
39
+
40
+ I do not, however, necessarily advocate actually using this library in
41
+ production code. Freezing a class this way is contrary to the very nature
42
+ of Ruby and dynamic languages in general. If you're going to use it, you had
43
+ better have a very good reason for doing so!
44
+
45
+ = Caveats
46
+ Only instance methods are prevented from redefinition. Singleton methods are
47
+ not final. This change may happen in a future release.
48
+
49
+ = License
50
+ Artistic 2.0
51
+
52
+ = Copyright
53
+ (C) 2003-2011 Daniel J. Berger
54
+ All Rights Reserved.`
55
+
56
+ = Warranty
57
+ This package is provided "as is" and without any express or
58
+ implied warranties, including, without limitation, the implied
59
+ warranties of merchantability and fitness for a particular purpose.
60
+
61
+ = Author
62
+ Daniel J. Berger
data/Rakefile ADDED
@@ -0,0 +1,27 @@
1
+ require 'rake'
2
+ require 'rake/clean'
3
+ require 'rake/testtask'
4
+
5
+ CLEAN.include('**/*.gem', '**/*.rbc')
6
+
7
+ namespace :gem do
8
+ desc 'Build the final gem'
9
+ task :create => [:clean] do
10
+ spec = eval(IO.read('final.gemspec'))
11
+ Gem::Builder.new(spec).build
12
+ end
13
+
14
+ desc "Install the final gem"
15
+ task :install => [:create] do
16
+ file = Dir["*.gem"].first
17
+ sh "gem install #{file}"
18
+ end
19
+ end
20
+
21
+ Rake::TestTask.new do |t|
22
+ task :test => :clean
23
+ t.warning = true
24
+ t.verbose = true
25
+ end
26
+
27
+ task :default => :test
data/final.gemspec ADDED
@@ -0,0 +1,24 @@
1
+ require 'rubygems'
2
+
3
+ Gem::Specification.new do |gem|
4
+ gem.name = 'final'
5
+ gem.version = '0.1.0'
6
+ gem.license = 'Artistic 2.0'
7
+ gem.author = 'Daniel J. Berger'
8
+ gem.email = 'djberg96@gmail.com'
9
+ gem.homepage = 'http://www.rubyforge.org/projects/shards'
10
+ gem.summary = "An implementation of 'final' for Ruby"
11
+ gem.test_files = Dir['test/test*']
12
+ gem.has_rdoc = true
13
+ gem.files = Dir['**/*'].reject{ |f| f.include?('git') }
14
+
15
+ gem.rubyforge_project = 'shards'
16
+ gem.extra_rdoc_files = ['README', 'CHANGES', 'MANIFEST']
17
+
18
+ gem.description = <<-EOF
19
+ The final library enables you to declare your classes as final. This
20
+ prevents your class from being subclassed or having its methods redefined.
21
+ EOF
22
+
23
+ gem.add_development_dependency('test-unit', '>= 2.2.0')
24
+ end
data/lib/final.rb ADDED
@@ -0,0 +1,38 @@
1
+ # Make your class final. Once final'ized, it cannot be subclassed and
2
+ # its methods cannot be redefined.
3
+ #
4
+ module Final
5
+ # Error raised if any of the Final module's rules are violated.
6
+ class Error < RuntimeError; end
7
+
8
+ # The version of the final library.
9
+ VERSION = '0.1.0'
10
+
11
+ def self.included(mod)
12
+ # Store already defined methods.
13
+ mod.instance_eval("@final_methods = []")
14
+
15
+ # Internal accessor used in the method_added definition.
16
+ def mod.final_methods
17
+ @final_methods
18
+ end
19
+
20
+ # Prevent subclassing, except implicity subclassing from Object.
21
+ def mod.inherited(sub)
22
+ raise Error, "cannot subclass final class #{self}" unless self == Object
23
+ end
24
+
25
+ # Prevent methods from being redefined.
26
+ #--
27
+ # There's still going to be a method redefinition warning. Gosh, it
28
+ # sure would be nice if we could disable warnings.
29
+ #
30
+ def mod.method_added(sym)
31
+ if final_methods.include?(sym)
32
+ raise Error, "method '#{sym}' already defined"
33
+ else
34
+ final_methods << sym
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,44 @@
1
+ ########################################################################
2
+ # test_final.rb
3
+ #
4
+ # Test suite for the final library.
5
+ ########################################################################
6
+ require 'rubygems'
7
+ gem 'test-unit'
8
+ require 'test/unit'
9
+ require 'final'
10
+
11
+ class TC_Final < Test::Unit::TestCase
12
+ class Foo
13
+ include Final
14
+ def bar; end
15
+ end
16
+
17
+ def setup
18
+ @foo = Foo.new
19
+ end
20
+
21
+ test "version constant returns expected version number" do
22
+ assert_equal('0.1.0', Final::VERSION)
23
+ end
24
+
25
+ test "final class cannot be subclassed" do
26
+ assert_raise(Final::Error){ class Bar < Foo; end }
27
+ end
28
+
29
+ test "final class cannot have instance methods redefined" do
30
+ assert_raise(Final::Error){ class Foo; def bar; end; end }
31
+ end
32
+
33
+ test "adding a new method is not an error" do
34
+ assert_nothing_raised{ class Foo; def baz; end; end }
35
+ end
36
+
37
+ test "finalized classes do not interfere with each other" do
38
+ assert_nothing_raised{ class Foo2; include Final; def bar; end; end }
39
+ end
40
+
41
+ def teardown
42
+ @foo = nil
43
+ end
44
+ end
metadata ADDED
@@ -0,0 +1,90 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: final
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Daniel J. Berger
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-04-09 00:00:00 -06:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: test-unit
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 7
30
+ segments:
31
+ - 2
32
+ - 2
33
+ - 0
34
+ version: 2.2.0
35
+ type: :development
36
+ version_requirements: *id001
37
+ description: " The final library enables you to declare your classes as final. This\n prevents your class from being subclassed or having its methods redefined.\n"
38
+ email: djberg96@gmail.com
39
+ executables: []
40
+
41
+ extensions: []
42
+
43
+ extra_rdoc_files:
44
+ - README
45
+ - CHANGES
46
+ - MANIFEST
47
+ files:
48
+ - CHANGES
49
+ - final.gemspec
50
+ - lib/final.rb
51
+ - MANIFEST
52
+ - Rakefile
53
+ - README
54
+ - test/test_final.rb
55
+ has_rdoc: true
56
+ homepage: http://www.rubyforge.org/projects/shards
57
+ licenses:
58
+ - Artistic 2.0
59
+ post_install_message:
60
+ rdoc_options: []
61
+
62
+ require_paths:
63
+ - lib
64
+ required_ruby_version: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ hash: 3
70
+ segments:
71
+ - 0
72
+ version: "0"
73
+ required_rubygems_version: !ruby/object:Gem::Requirement
74
+ none: false
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ hash: 3
79
+ segments:
80
+ - 0
81
+ version: "0"
82
+ requirements: []
83
+
84
+ rubyforge_project: shards
85
+ rubygems_version: 1.3.7
86
+ signing_key:
87
+ specification_version: 3
88
+ summary: An implementation of 'final' for Ruby
89
+ test_files:
90
+ - test/test_final.rb