final 0.1.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 (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