up_the_irons-immutable 0.2 → 0.3

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.
data/README CHANGED
@@ -3,8 +3,8 @@ Immutable
3
3
  =========
4
4
 
5
5
  :Author: Garry Dolley
6
- :Date: 09-17-2008
7
- :Version: v0.1
6
+ :Date: 09-21-2008
7
+ :Version: v0.2
8
8
 
9
9
  The ``Immutable`` module provides a method (immutable_method) that let's one
10
10
  declare method(s) as immutable. That is, other code will not be able to
@@ -22,7 +22,12 @@ Alpha
22
22
  This code is very new and despite a pretty comprehensive spec, I'm sure there's
23
23
  cases where someone can figure out how to defeat this. So be it. :)
24
24
 
25
- Additionally, I've tested this code on GNU/Linux with Ruby 1.8.6 only.
25
+ This code has been tested on the following systems:
26
+
27
+ * GNU/Linux (Ubuntu 8.04.1) with Ruby 1.8.6
28
+ * FreeBSD 7.0 with Ruby 1.8.6
29
+ * OpenBSD 4.3 with Ruby 1.8.6
30
+ * Mac OS X 10.5 with Ruby 1.8.6
26
31
 
27
32
  Please see the Author section below and report any problems.
28
33
 
@@ -177,6 +182,9 @@ Use whichever style you prefer.
177
182
  To Do
178
183
  -----
179
184
 
185
+ Make method_added immutable, as well as immutable_method. Write tests to try
186
+ to "defeat" an immutable method, see if we can prevent it.
187
+
180
188
  I finished my TODOs, cool.
181
189
 
182
190
  Author
@@ -204,7 +212,7 @@ look like markup, even though it is.
204
212
  Copyright
205
213
  ---------
206
214
 
207
- Copyright (c) 2008 Garry C. Dolley
215
+ Copyright (c) 2008,2009 Garry C. Dolley
208
216
 
209
217
  Immutable is free software; you can redistribute it and/or modify it under the
210
218
  terms of the GNU General Public License as published by the Free Software
data/immutable.gemspec CHANGED
@@ -1,12 +1,11 @@
1
1
  spec = Gem::Specification.new do |s|
2
- s.name = "immutable"
3
- s.version = "0.2"
4
- s.author = "Garry Dolley"
5
- s.email = "gdolley@ucla.edu"
6
- s.homepage = "http://github.com/up_the_irons/immutable/tree/master"
7
- s.platform = Gem::Platform::RUBY
8
- s.summary = "Declare methods as immutable, somewhat like Java's 'final' keyword but still allowing child classes to override."
9
-
10
- s.files = ['lib/immutable.rb', 'spec/immutable_spec.rb', 'immutable.gemspec', 'README', 'COPYING']
11
- s.has_rdoc = false
2
+ s.name = "immutable"
3
+ s.version = "0.3"
4
+ s.authors = ["Garry Dolley", "Lucas de Castro"]
5
+ s.email = "gdolley@ucla.edu"
6
+ s.homepage = "http://github.com/up_the_irons/immutable/tree/master"
7
+ s.description = "Declare methods as immutable."
8
+ s.summary = "Declare methods as immutable, somewhat like Java's 'final' keyword but still allowing child classes to override."
9
+ s.files = ['lib/immutable.rb', 'spec/immutable_spec.rb', 'immutable.gemspec', 'README', 'COPYING']
10
+ s.has_rdoc = false
12
11
  end
data/lib/immutable.rb CHANGED
@@ -1,36 +1,50 @@
1
1
  module Immutable
2
2
  class CannotOverrideMethod < StandardError; end
3
3
 
4
+ # Random ID changed at each interpreter load
5
+ UNIQ = "_#{object_id.abs}"
6
+
4
7
  def self.included(mod)
5
8
  mod.extend(ClassMethods)
6
9
  end
7
10
 
8
11
  module ClassMethods
9
12
  def immutable_method(*args)
13
+ # Initialize variables
14
+ @immutable_methods = [] if @immutable_methods.nil?
15
+ @silent_immutable_methods = [] if @silent_immutable_methods.nil?
16
+ instance_variable_set("@#{UNIQ}_in_method_added", false)
17
+
10
18
  opts = args.last.is_a?(Hash) ? args.pop : {}
11
19
 
12
20
  args.each do |method|
13
- alias_method "orig_#{method}", method
21
+ alias_method "#{UNIQ}_old_#{method}", method
14
22
  end
23
+
24
+ # Build list of immutable methods
25
+ @immutable_methods += args
26
+ @silent_immutable_methods += args if opts[:silent]
15
27
 
16
- @args = args; @opts = opts
28
+ @opts = opts
17
29
  module_eval do
18
30
  def self.method_added(sym)
19
- if @args
20
- @args.each do |method|
21
- if method && sym == method.to_sym && !called_by_method_added
22
- unless @opts[:silent]
31
+ if @immutable_methods
32
+ @immutable_methods.each do |method|
33
+ if method && sym == method.to_sym && !in_method_added?
34
+ unless @silent_immutable_methods.include?(method)
23
35
  raise CannotOverrideMethod, "Cannot override the immutable method: #{sym}"
24
36
  end
25
-
26
- self.module_eval <<-"end;"
27
- def #{method.to_s}(*args, &block)
28
- orig_#{method.to_s}(*args, &block)
29
- end
30
- end;
37
+
38
+ allow_method_override do
39
+ self.module_eval <<-"end;"
40
+ def #{method}(*args, &block)
41
+ #{UNIQ}_old_#{method}(*args, &block)
42
+ end
43
+ end;
44
+ end
31
45
  end
32
46
  end # @args.each
33
- end # @args
47
+ end # @immutable_methods
34
48
  end # def self.method_added()
35
49
 
36
50
  def self.method_undefined(sym)
@@ -42,9 +56,15 @@ module Immutable
42
56
  end
43
57
  end # module_eval
44
58
 
45
- def self.called_by_method_added
46
- # This is a little brittle, suggestions?
47
- caller[3] =~ /eval.*in.*method_added/
59
+ def self.allow_method_override
60
+ instance_variable_set("@#{UNIQ}_in_method_added", true)
61
+ yield
62
+ ensure
63
+ instance_variable_set("@#{UNIQ}_in_method_added", false)
64
+ end
65
+
66
+ def self.in_method_added?
67
+ instance_variable_get("@#{UNIQ}_in_method_added")
48
68
  end
49
69
  end # def immutable_method()
50
70
 
@@ -27,15 +27,6 @@ Foo2 = Foo.clone
27
27
  Foo3 = Foo.clone
28
28
 
29
29
  describe "Module Foo" do
30
- def test_it(mod, method)
31
- @value = Object.instance_eval do
32
- include mod
33
- send(method)
34
- end
35
-
36
- @value.should == :fast
37
- end
38
-
39
30
  describe "after redefining" do
40
31
  it "should not let foo() be redefined" do
41
32
  redefine(Foo, :foo)
@@ -185,6 +176,11 @@ module Boo
185
176
  :fast
186
177
  end
187
178
 
179
+ def boofoo
180
+ :boofoo_fast
181
+ end
182
+
183
+ immutable_method :boofoo, :silent => true
188
184
  immutable_method :boo
189
185
  end
190
186
 
@@ -196,6 +192,66 @@ describe "Exceptions" do
196
192
  end.should raise_error(Immutable::CannotOverrideMethod, /Cannot override the immutable method: boo$/)
197
193
  end
198
194
  end
195
+
196
+ it "should not raise if :silent => true" do
197
+ redefine(Boo, :boofoo)
198
+ test_it(Boo, :boofoo, :boofoo_fast)
199
+ end
200
+ end
201
+
202
+ #########
203
+ # Other #
204
+ #########
205
+
206
+ module Bear
207
+ include Immutable
208
+
209
+ def foo
210
+ :foo_fast
211
+ end
212
+
213
+ def bar
214
+ :bar_fast
215
+ end
216
+
217
+ def baz
218
+ :baz_fast
219
+ end
220
+
221
+ def boo
222
+ :boo_fast
223
+ end
224
+
225
+ # Make sure we can make independent calls to immutable_method
226
+ immutable_method :foo
227
+ immutable_method :bar
228
+ immutable_method :baz, :silent => true
229
+ immutable_method :boo
230
+ end
231
+
232
+ describe "Multiple independent calls to immutable_method()" do
233
+ it "should still recognize foo() is immutable" do
234
+ lambda do
235
+ redefine(Bear, :foo)
236
+ end.should raise_error(Immutable::CannotOverrideMethod, /Cannot override the immutable method: foo$/)
237
+ end
238
+
239
+ it "should still recognize bar() is immutable" do
240
+ lambda do
241
+ redefine(Bear, :bar)
242
+ end.should raise_error(Immutable::CannotOverrideMethod, /Cannot override the immutable method: bar$/)
243
+ end
244
+
245
+ it "should still recognize baz() is immutable" do
246
+ redefine(Bear, :baz)
247
+ test_it(Bear, :baz, :baz_fast)
248
+ end
249
+
250
+ it "should still recognize boo() is immutable" do
251
+ lambda do
252
+ redefine(Bear, :boo)
253
+ end.should raise_error(Immutable::CannotOverrideMethod, /Cannot override the immutable method: boo$/)
254
+ end
199
255
  end
200
256
 
201
257
  ##################
@@ -221,3 +277,12 @@ def remove(mod, method)
221
277
  remove_method(method)
222
278
  end
223
279
  end
280
+
281
+ def test_it(mod, method, value = :fast)
282
+ @value = Object.instance_eval do
283
+ include mod
284
+ send(method)
285
+ end
286
+
287
+ @value.should == value
288
+ end
metadata CHANGED
@@ -1,19 +1,20 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: up_the_irons-immutable
3
3
  version: !ruby/object:Gem::Version
4
- version: "0.2"
4
+ version: "0.3"
5
5
  platform: ruby
6
6
  authors:
7
7
  - Garry Dolley
8
+ - Lucas de Castro
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
12
 
12
- date: 2008-08-13 00:00:00 -07:00
13
+ date: 2009-05-16 00:00:00 -07:00
13
14
  default_executable:
14
15
  dependencies: []
15
16
 
16
- description:
17
+ description: Declare methods as immutable.
17
18
  email: gdolley@ucla.edu
18
19
  executables: []
19
20
 
@@ -29,6 +30,7 @@ files:
29
30
  - COPYING
30
31
  has_rdoc: false
31
32
  homepage: http://github.com/up_the_irons/immutable/tree/master
33
+ licenses:
32
34
  post_install_message:
33
35
  rdoc_options: []
34
36
 
@@ -49,7 +51,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
49
51
  requirements: []
50
52
 
51
53
  rubyforge_project:
52
- rubygems_version: 1.2.0
54
+ rubygems_version: 1.3.5
53
55
  signing_key:
54
56
  specification_version: 2
55
57
  summary: Declare methods as immutable, somewhat like Java's 'final' keyword but still allowing child classes to override.