superators 0.9.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.
@@ -0,0 +1,5 @@
1
+ == 1.0.0 / 2007-08-14
2
+
3
+ * 1 major enhancement
4
+ * Birthday!
5
+
@@ -0,0 +1,10 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.txt
4
+ Rakefile
5
+ bin/superators
6
+ lib/superators.rb
7
+ lib/superators/macro.rb
8
+ lib/superators/monkey_patch.rb
9
+ lib/superators/version.rb
10
+ spec/superator_spec.rb
@@ -0,0 +1,44 @@
1
+ superators
2
+ by Jay Phillips
3
+ http://jicksta.com
4
+
5
+ == DESCRIPTION:
6
+
7
+ Superators are a superset of new Ruby operators you can create and use.
8
+
9
+ == FEATURES/PROBLEMS:
10
+
11
+ * Presently a superator operand must support having a singleton class. Because true, false, nil, Symbols, and Fixnums are all specially optimized for in MRI and cannot have singleton classes, they can't be given to a superator. There are ways this can be potentially accounted for, but nothing is in place at the moment, causing this to be classified as a bug.
12
+
13
+ * When defining a superator in a class, any operators overloaded after the superator definition will override a superator definition. For example, if you create the superator "<---" and then define the <() operator, the superator will not work. In this case, the superator's definition should be somewhere after the <() definition.
14
+
15
+ * Superators work by handling a binary Ruby operator specially and then building a chain of unary operators after it. For this reason, a superator must match the regexp /^(\*\*|\*|\/|%|\+|\-|<<|>>|&|\||\^|<=>|>=|<=|<|>|===|==|=~)(\-|~|\+)+$/.
16
+
17
+ == SYNOPSIS:
18
+
19
+ Below is a simple example monkey patch which adds the "<---" operator to all Ruby Arrays.
20
+
21
+ class Array
22
+ superator "<---" do |operand|
23
+ if operand.kind_of? Array
24
+ self + operand.map { |x| x.inspect }
25
+ else
26
+ operand.inspect
27
+ end
28
+ end
29
+ end
30
+
31
+ == REQUIREMENTS:
32
+
33
+ * Only requirement is Ruby.
34
+
35
+ == INSTALL:
36
+
37
+ * sudo gem install superators
38
+ * require 'superators'
39
+
40
+ == LICENSE:
41
+
42
+ This software is licensed in the public domain. You may do whatever you wish with it.
43
+
44
+ You are allowed to use this library during dodo poaching as well.
@@ -0,0 +1,27 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'rubygems'
4
+ require 'hoe'
5
+ require './lib/superators.rb'
6
+ require 'spec/rake/spectask'
7
+
8
+ Spec::Rake::SpecTask.new do |opts|
9
+ opts.spec_opts = %w'-c'
10
+ end
11
+
12
+ desc "Generate a HTML report of the RSpec specs"
13
+ Spec::Rake::SpecTask.new "report" do |opts|
14
+ opts.spec_opts = %w'--format html:report.html'
15
+ end
16
+
17
+ Hoe.new('superators', Superators::VERSION) do |p|
18
+ p.rubyforge_name = 'superators'
19
+ p.author = 'Jay Phillips'
20
+ p.email = 'jay -at- codemecca.com'
21
+ p.summary = 'Superators add new sexy operators to your Ruby objects.'
22
+ p.description = p.paragraphs_of('README.txt', 2..5).join("\n\n")
23
+ p.url = p.paragraphs_of('README.txt', 0).first.split(/\n/)[1..-1]
24
+ p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
25
+ end
26
+
27
+ # vim: syntax=Ruby
File without changes
@@ -0,0 +1,7 @@
1
+ $:.unshift File.expand_path(File.dirname(__FILE__))
2
+
3
+ require 'superators/version'
4
+ require 'superators/macro'
5
+ require 'superators/monkey_patch'
6
+
7
+ include SuperatorMixin
@@ -0,0 +1,118 @@
1
+ module SuperatorMixin
2
+
3
+ BINARY_RUBY_OPERATORS = %w"** * / % + - << >> & | ^ <=> >= <= < > === == =~"
4
+ UNARY_RUBY_OPERATORS = %w"-@ ~@ +@"
5
+
6
+ BINARY_OPERATOR_PATTERN = BINARY_RUBY_OPERATORS.map { |x| Regexp.escape(x) }.join "|"
7
+ UNARY_OPERATOR_PATTERN = UNARY_RUBY_OPERATORS.map { |x| Regexp.escape(x) }.join "|"
8
+ UNARY_OPERATOR_PATTERN_WITHOUT_AT_SIGN = UNARY_OPERATOR_PATTERN.map { |x| x.gsub('@', '') }
9
+
10
+ VALID_SUPERATOR = /^(#{BINARY_OPERATOR_PATTERN})(#{UNARY_OPERATOR_PATTERN_WITHOUT_AT_SIGN})+$/
11
+
12
+ def superator_send(sup, operand)
13
+ if respond_to_superator? sup
14
+ __send__ superator_definition_name_for(sup), operand
15
+ else
16
+ raise NameError, "Superator #{sup} has not been defined on #{self.class}" unless superator_definition
17
+ end
18
+ end
19
+
20
+ def respond_to_superator?(sup)
21
+ respond_to? superator_definition_name_for(sup)
22
+ end
23
+
24
+ def defined_superators
25
+ methods.grep(/superator_definition_/).map { |m| superator_decode(m) }
26
+ end
27
+
28
+ protected
29
+
30
+ def superator(operator, &block)
31
+ raise ArgumentError, "block not supplied" unless block_given?
32
+ raise NameError, "Not a valid superator!" unless superator_valid?(operator)
33
+
34
+ real_operator = real_operator_from_superator operator
35
+
36
+ class_eval do
37
+ # Step in front of the old operator's dispatching.
38
+ alias_for_real_method = superator_alias_for real_operator
39
+
40
+ if instance_methods.include?(real_operator) && !respond_to_superator?(operator)
41
+ alias_method alias_for_real_method, real_operator
42
+ end
43
+
44
+ define_method superator_definition_name_for(operator), &block
45
+
46
+ # When we get to the method defining, we have to know whether the superator had to be aliased
47
+ # or if it's new entirely.
48
+ define_method(real_operator) do |operand|
49
+ if operand.kind_of? SuperatorFlag
50
+ sup = operand.superator_queue.unshift(real_operator).join
51
+ operand.superator_queue.clear
52
+
53
+ superator_send(sup, operand)
54
+ else
55
+ # If the method_alias is defined
56
+ if respond_to? alias_for_real_method
57
+ __send__(alias_for_real_method, operand)
58
+ else
59
+ raise NameError, "undefined method #{real_operator} for #{operand.inspect}:#{operand.class}"
60
+ end
61
+ end
62
+ end
63
+
64
+ end
65
+
66
+ def undef_superator(sup)
67
+ if respond_to_superator?(sup)
68
+ real_operator = real_operator_from_superator sup
69
+ real_operator_alias = superator_alias_for sup
70
+
71
+ (class << self; self; end).instance_eval do
72
+ undef_method superator_definition_name_for(sup)
73
+ if respond_to? real_operator_alias
74
+ alias_method real_operator, real_operator_alias if defined_superators.empty?
75
+ else
76
+ undef_method real_operator
77
+ end
78
+ end
79
+ else
80
+ raise NameError, "undefined superator #{sup} for #{self.inspect}:#{self.class}"
81
+ end
82
+ end
83
+ end
84
+
85
+ private
86
+
87
+ def superator_encode(str)
88
+ tokenizer = /#{BINARY_OPERATOR_PATTERN}|#{UNARY_OPERATOR_PATTERN_WITHOUT_AT_SIGN}/
89
+ str.scan(tokenizer).map { |op| op.split('').map { |s| s[0] }.join "_" }.join "__"
90
+ end
91
+
92
+ def superator_decode(str)
93
+ tokens = str.match /^(superator_(definition|alias_for))?((_?\d{2,3})+)((__\d{2,3})+)$/
94
+ #puts *tokens
95
+ if tokens
96
+ (tokens[3].split("_" ) + tokens[5].split('__')).reject { |x| x.empty? }.map { |s| s.to_i.chr }.join
97
+ end
98
+ end
99
+
100
+ def real_operator_from_superator(sup)
101
+ sup[/^#{BINARY_OPERATOR_PATTERN}/]
102
+ end
103
+
104
+ def superator_alias_for(name)
105
+ "superator_alias_for_#{superator_encode(name)}"
106
+ end
107
+
108
+ def superator_definition_name_for(sup)
109
+ "superator_definition_#{superator_encode(sup)}"
110
+ end
111
+
112
+ def superator_valid?(operator)
113
+ operator =~ VALID_SUPERATOR
114
+ end
115
+
116
+ end
117
+
118
+ module SuperatorFlag;end
@@ -0,0 +1,26 @@
1
+ class Object
2
+
3
+ attr_reader :superator_queue
4
+
5
+ def -@
6
+ extend SuperatorFlag
7
+ @superator_queue ||= []
8
+ @superator_queue.unshift '-'
9
+ self
10
+ end
11
+
12
+ def +@
13
+ extend SuperatorFlag
14
+ @superator_queue ||= []
15
+ @superator_queue.unshift '+'
16
+ self
17
+ end
18
+
19
+ def ~@
20
+ extend SuperatorFlag
21
+ @superator_queue ||= []
22
+ @superator_queue.unshift '~'
23
+ self
24
+ end
25
+
26
+ end
@@ -0,0 +1,4 @@
1
+ class Superators
2
+ MAJOR,MINOR,TINY = 0,9,0
3
+ VERSION = [MAJOR,MINOR,TINY].join '.'
4
+ end
@@ -0,0 +1,304 @@
1
+ require 'lib/superators'
2
+
3
+ describe "The 'superator' macro" do
4
+
5
+ it "should allow two superators to begin with the first 'real' operator" do
6
+ superatored = Class.new do
7
+ superator("--") { throw :superator_dash_dash }
8
+ superator("-~") { throw :superator_dash_tilde }
9
+ end
10
+ lambda { superatored.new() -- Object.new }.should throw_symbol(:superator_dash_dash)
11
+ lambda { superatored.new() -~ Object.new }.should throw_symbol(:superator_dash_tilde)
12
+ end
13
+
14
+ it "should raise an error when the 'real' operator isn't a valid Ruby operator" do
15
+ lambda do
16
+ Class.new do
17
+ superator('x--') {}
18
+ end
19
+ end.should raise_error
20
+ end
21
+
22
+ it "should raise a NameError when a superator is defined for a class that did not have the 'real' operator but the 'real' operator is used" do
23
+ superatored = Class.new do
24
+ superator('**--') {}
25
+ end
26
+ lambda do
27
+ superatored.new ** Object.new
28
+ end.should raise_error(NameError)
29
+ end
30
+
31
+ it "should be callable on a Class instance (e.g. on 'self' in a class definition)" do
32
+ Class.new.should respond_to('superator')
33
+ x = Class.new do
34
+ self.class.superator("-~+~-") { throw :class_superator }
35
+ end
36
+ lambda { x -~+~- Object.new }.should throw_symbol(:class_superator)
37
+ lambda { x.new() -~+~- Object.new }.should raise_error
38
+ end
39
+
40
+ it "should work when defined in an eigenclass" do
41
+ victim = Object.new
42
+ class << victim
43
+ superator('<<~~') { throw :eigenclass }
44
+ end
45
+ lambda { victim <<~~ "monkey" }.should throw_symbol(:eigenclass)
46
+ end
47
+
48
+ it "should preserve the old 'real' operator" do
49
+ victim = Class.new do
50
+ def <<(_)
51
+ throw :original_method
52
+ end
53
+ superator('<<~~') { throw :superator }
54
+ end.new
55
+ lambda { victim << Object.new }.should throw_symbol(:original_method)
56
+ lambda { victim <<~~ Object.new }.should throw_symbol(:superator)
57
+ end
58
+
59
+ # This one is going to be very difficult to implement. method_added() maybe?
60
+ it "should work even when the superator's 'main' method is redefined after the superator macro" do
61
+ victim = Class.new do
62
+ def <(_) throw :first end
63
+ superator("<~~") { throw :superator }
64
+ def <(_) throw :last end
65
+ end
66
+ lambda { victim.new() < Object.new }.should throw_symbol(:last)
67
+ lambda { victim.new() <~~ Object.new }.should throw_symbol(:superator)
68
+ end
69
+
70
+ end
71
+
72
+ describe "Defined binary superators" do
73
+
74
+ it "should be available to subclasses" do
75
+ superclass = Class.new do
76
+ superator("<<--") { throw :superclass }
77
+ end
78
+ lambda { Class.new(superclass).new <<-- "Foobar" }.should throw_symbol(:superclass)
79
+ end
80
+
81
+ it "should be overridable in subclasses" do
82
+ parent = Class.new do
83
+ superator("<~") { throw :parent }
84
+ end
85
+
86
+ child = Class.new(parent) do
87
+ superator("<~") { throw :child }
88
+ end
89
+
90
+ lambda { parent.new() <~ Object.new }.should throw_symbol(:parent)
91
+ lambda { child.new() <~ Object.new }.should throw_symbol(:child)
92
+ end
93
+
94
+ it "should redefine an already-defined superator of the same class" do
95
+ lambda do
96
+ Class.new do
97
+ superator("<---") { throw :first }
98
+ superator("<---") { throw :last }
99
+ end.new <--- Object.new
100
+ end.should throw_symbol(:last)
101
+ end
102
+ end
103
+
104
+ describe "The superator_send method" do
105
+
106
+ it "should execute the block within the object's instance (so self is the instance, not the class)" do
107
+ Class.new do
108
+ superator "-+-" do |other|
109
+ self.should_not be_kind_of(Class)
110
+ end
111
+ end
112
+ end
113
+
114
+ it "should receive the proper arguments" do
115
+ x = Class.new do
116
+ superator("-+-+~++~") {}
117
+ end
118
+ victim, operand = x.new, "a clue"
119
+ victim.should_receive(:superator_send).once.with("-+-+~++~", operand)
120
+ victim -+-+~++~ operand
121
+ end
122
+
123
+ it "should exist on an Object instance" do
124
+ Object.new.should respond_to(:superator_send)
125
+ end
126
+
127
+ it "should raise NameError if an invalid superator is given" do
128
+ lambda do
129
+ Object.new.superator_send("17BB&DB & !! @", Object.new.extend(SuperatorFlag))
130
+ end.should raise_error(NameError)
131
+ end
132
+
133
+ end
134
+
135
+ describe "The respond_to_superator? method" do
136
+
137
+ it "should be available on all objects" do
138
+ Object.new.should respond_to(:respond_to_superator?)
139
+ end
140
+
141
+ it "should return true if a superator was defined for the object's class" do
142
+ Class.new do
143
+ superator("<--") {}
144
+ end.new.respond_to_superator?("<--").should be_true
145
+ end
146
+
147
+ it "should return true if a superator was defined for the object's superclass" do
148
+ parent = Class.new do
149
+ superator("<=~~") {}
150
+ end
151
+ Class.new(parent) {}.new.respond_to_superator?("<=~~").should be_true
152
+ end
153
+
154
+ it "should return false or nil if a superator was not defined" do
155
+ result = Class.new.new.respond_to_superator?("<" + ("-" * 100))
156
+ (!! result).should be_false
157
+ end
158
+
159
+ it "should return false for arguments only similar to (not the same as) the defined superator(s)" do
160
+ labrat = Class.new do
161
+ superator("<" + ('~' * 10)) {}
162
+ end.new
163
+ (!! labrat.respond_to_superator?('<' + ('~' * 11))).should be_false
164
+ (!! labrat.respond_to_superator?('<' + ('~' * 9))).should be_false
165
+ end
166
+
167
+ end
168
+
169
+ describe "The undef_superator method" do
170
+
171
+ it "should properly delete a superator" do
172
+ klass = Class.new do
173
+ superator("<----") {}
174
+ end
175
+ lambda do
176
+ obj = klass.new
177
+ obj.undef_superator "<----"
178
+ obj <---- Object.new
179
+ end.should raise_error(NameError)
180
+ end
181
+
182
+ it "should make respond_to_superator?() return false" do
183
+ sup = "<<---"
184
+ klass = Class.new do
185
+ superator(sup) {}
186
+ end
187
+ obj = klass.new
188
+ obj.undef_superator sup
189
+ obj.respond_to_superator?(sup).should be_false
190
+ end
191
+
192
+ end
193
+
194
+ describe "The monkey patch" do
195
+
196
+ it "should create a superators attr_reader" do
197
+ Object.new.should respond_to(:superator_queue)
198
+ Object.new.should_not respond_to(:superator_queue=)
199
+ end
200
+
201
+ end
202
+
203
+ describe "The defined_superators() method" do
204
+
205
+ it "should return an array of superator Strings when called on an object" do
206
+ sups = %w"-- ++ +- -+"
207
+ klass = Class.new do
208
+ sups.each do |sup|
209
+ superator(sup) {}
210
+ end
211
+ end
212
+ defined = klass.new.defined_superators
213
+
214
+ defined.should be_kind_of(Array)
215
+ defined.size.should == sups.size
216
+ sups.each { |sup| defined.should include(sup) }
217
+ end
218
+
219
+ it "should include superators defined in a superclass" do
220
+ parent = Class.new do
221
+ superator("--") {}
222
+ end
223
+ Class.new(parent).new.defined_superators.should include("--")
224
+ end
225
+
226
+ end
227
+
228
+ describe "The 'real' operator finding algorithm" do
229
+
230
+ it "should work with minus and unary negation" do
231
+ real_operator_from_superator("---").should == "-"
232
+ end
233
+
234
+ it "should work with plus and unary plus" do
235
+ real_operator_from_superator("+++").should == "+"
236
+ end
237
+
238
+ it "should return nil when given only unary tildes" do
239
+ real_operator_from_superator("~~~").should be_nil
240
+ end
241
+
242
+ it "should work properly with the operators that are expanded versions of other operators" do
243
+ real_operator_from_superator("<<--").should == "<<"
244
+ real_operator_from_superator("<~~-").should == "<"
245
+ real_operator_from_superator("**+~").should == "**"
246
+ real_operator_from_superator("*+~~").should == "*"
247
+ real_operator_from_superator("<=~~").should == "<="
248
+ real_operator_from_superator(">=+-").should == ">="
249
+ real_operator_from_superator("=~~~").should == "=~"
250
+ real_operator_from_superator("<=>+").should == "<=>"
251
+ real_operator_from_superator("===+").should == "==="
252
+ real_operator_from_superator("==+-").should == "=="
253
+ end
254
+ end
255
+
256
+ describe "Superator method en-/decoding" do
257
+
258
+ before do
259
+ @uses = { "<<~~" => "60_60__126__126",
260
+ "<=>~" => "60_61_62__126",
261
+ "----" => "45__45__45__45",
262
+ "+-~+-~" => "43__45__126__43__45__126" }
263
+ end
264
+
265
+ it "should return the same value after encoding and decoding" do
266
+ @uses.keys.each do |operator|
267
+ superator_decode(superator_encode(operator)).should == operator
268
+ end
269
+ end
270
+
271
+ it "should encode binary superators properly" do
272
+ @uses.each_pair { |key, value| superator_encode(key).should == value }
273
+ end
274
+
275
+ it "should decode binary superators properly" do
276
+ @uses.each_pair { |key, value| superator_decode(value).should == key }
277
+ end
278
+
279
+ it "should create proper method definition name" do
280
+ op = "|+-"
281
+ superator_definition_name_for(op).should =~ /#{superator_encode(op)}$/
282
+ end
283
+
284
+ it "should be containable in a method definition" do
285
+ lambda do
286
+ eval ":jay_#{superator_encode("<<+~--")}"
287
+ end.should_not raise_error(SyntaxError)
288
+ end
289
+
290
+ end
291
+
292
+ describe "A superator's arguments" do
293
+ # These specs are included for someone to potentially fix. Their failing
294
+ # constitutes a bug, though there's no clear way to implement this.
295
+
296
+ # These are types that can have no eigenclass. One way to store Kernel#caller
297
+ # stacktraces against the Fixnum value on which the unary methods were executed.
298
+ it "should allow a Fixnum as an operand" # This *MAY* never work
299
+ it "should allow a Symbol as an operand"
300
+ it "should allow true as an operand"
301
+ it "should allow false as an operand"
302
+ it "should allow nil as an operand"
303
+
304
+ end
metadata ADDED
@@ -0,0 +1,66 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.9.4
3
+ specification_version: 1
4
+ name: superators
5
+ version: !ruby/object:Gem::Version
6
+ version: 0.9.0
7
+ date: 2007-08-22 00:00:00 +01:00
8
+ summary: Superators add new sexy operators to your Ruby objects.
9
+ require_paths:
10
+ - lib
11
+ email: jay -at- codemecca.com
12
+ homepage: " by Jay Phillips"
13
+ rubyforge_project: superators
14
+ description: "== FEATURES/PROBLEMS: * Presently a superator operand must support having a singleton class. Because true, false, nil, Symbols, and Fixnums are all specially optimized for in MRI and cannot have singleton classes, they can't be given to a superator. There are ways this can be potentially accounted for, but nothing is in place at the moment, causing this to be classified as a bug. * When defining a superator in a class, any operators overloaded after the superator definition will override a superator definition. For example, if you create the superator \"<---\" and then define the <() operator, the superator will not work. In this case, the superator's definition should be somewhere after the <() definition. * Superators work by handling a binary Ruby operator specially and then building a chain of unary operators after it. For this reason, a superator must match the regexp /^(\\*\\*|\\*|\\/|%|\\+|\\-|<<|>>|&|\\||\\^|<=>|>=|<=|<|>|===|==|=~)(\\-|~|\\+)+$/. == SYNOPSIS:"
15
+ autorequire:
16
+ default_executable:
17
+ bindir: bin
18
+ has_rdoc: true
19
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
20
+ requirements:
21
+ - - ">"
22
+ - !ruby/object:Gem::Version
23
+ version: 0.0.0
24
+ version:
25
+ platform: ruby
26
+ signing_key:
27
+ cert_chain:
28
+ post_install_message:
29
+ authors:
30
+ - Jay Phillips
31
+ files:
32
+ - History.txt
33
+ - Manifest.txt
34
+ - README.txt
35
+ - Rakefile
36
+ - bin/superators
37
+ - lib/superators.rb
38
+ - lib/superators/macro.rb
39
+ - lib/superators/monkey_patch.rb
40
+ - lib/superators/version.rb
41
+ - spec/superator_spec.rb
42
+ test_files: []
43
+
44
+ rdoc_options:
45
+ - --main
46
+ - README.txt
47
+ extra_rdoc_files:
48
+ - History.txt
49
+ - Manifest.txt
50
+ - README.txt
51
+ executables:
52
+ - superators
53
+ extensions: []
54
+
55
+ requirements: []
56
+
57
+ dependencies:
58
+ - !ruby/object:Gem::Dependency
59
+ name: hoe
60
+ version_requirement:
61
+ version_requirements: !ruby/object:Gem::Version::Requirement
62
+ requirements:
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ version: 1.3.0
66
+ version: