up_the_irons-immutable 0.2 → 0.3

Sign up to get free protection for your applications and to get access to all the features.
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.