superators19 0.9.2

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt ADDED
@@ -0,0 +1,5 @@
1
+ == 1.0.0 / 2007-08-14
2
+
3
+ * 1 major enhancement
4
+ * Birthday!
5
+
data/README.md ADDED
@@ -0,0 +1,47 @@
1
+ # superators
2
+
3
+ ### by Jay Phillips
4
+ ### http://jicksta.com
5
+
6
+ ## DESCRIPTION:
7
+
8
+ Superators are a superset of new Ruby operators you can create and use.
9
+
10
+ ## FEATURES/PROBLEMS:
11
+
12
+ * 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.
13
+
14
+ * 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.
15
+
16
+ * 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:
17
+
18
+ /^(\*\*|\*|\/|%|\+|\-|<<|>>|&|\||\^|<=>|>=|<=|<|>|===|==|=~)(\-|~|\+)+$/
19
+
20
+ ## SYNOPSIS:
21
+
22
+ Below is a simple example monkey patch which adds the "<---" operator to all Ruby Arrays.
23
+
24
+ class Array
25
+ superator "<---" do |operand|
26
+ if operand.kind_of? Array
27
+ self + operand.map { |x| x.inspect }
28
+ else
29
+ operand.inspect
30
+ end
31
+ end
32
+ end
33
+
34
+ ## REQUIREMENTS:
35
+
36
+ * Only requirement is Ruby.
37
+
38
+ ## INSTALL:
39
+
40
+ * sudo gem install superators
41
+ * require 'superators'
42
+
43
+ ## LICENSE:
44
+
45
+ This software is licensed in the public domain. You may do whatever you wish with it.
46
+
47
+ You are allowed to use this library during dodo poaching as well.
data/Rakefile ADDED
@@ -0,0 +1,17 @@
1
+ # -*- ruby -*-
2
+ $:.unshift(File.dirname(__FILE__)) unless
3
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
4
+
5
+ require 'bundler'
6
+
7
+ Bundler::GemHelper.install_tasks
8
+
9
+ require 'rspec'
10
+ require 'rspec/core'
11
+ require 'rspec/core/rake_task'
12
+
13
+ RSpec::Core::RakeTask.new(:spec) do |spec|
14
+ spec.pattern = "spec/**/*_spec.rb"
15
+ spec.verbose = true
16
+ spec.rspec_opts = ['--color']
17
+ end
@@ -0,0 +1,7 @@
1
+ $:.unshift File.expand_path(File.dirname(__FILE__))
2
+
3
+ require 'superators19/version'
4
+ require 'superators19/macro'
5
+ require 'superators19/monkey_patch'
6
+
7
+ include SuperatorMixin
@@ -0,0 +1,131 @@
1
+ module SuperatorMixin
2
+ BINARY_RUBY_OPERATORS = %w"** * / % + - << >> & | ^ <=> >= <= < > === == =~"
3
+ UNARY_RUBY_OPERATORS = %w"-@ ~@ +@"
4
+
5
+ BINARY_OPERATOR_PATTERN = BINARY_RUBY_OPERATORS.map { |x| Regexp.escape(x) }.join "|"
6
+ UNARY_OPERATOR_PATTERN = UNARY_RUBY_OPERATORS.map { |x| Regexp.escape(x) }.join "|"
7
+ UNARY_OPERATOR_PATTERN_WITHOUT_AT_SIGN = UNARY_OPERATOR_PATTERN.gsub '@', ''
8
+
9
+ VALID_SUPERATOR = /^(#{BINARY_OPERATOR_PATTERN})(#{UNARY_OPERATOR_PATTERN_WITHOUT_AT_SIGN})+$/
10
+
11
+ def superator_send(sup, operand)
12
+ meth = method(superator_definition_name_for(sup))
13
+ begin
14
+ # If the user supplied a block that doesn't take any arguments, Ruby 1.9
15
+ # objects if we try to pass it an argument
16
+ if meth.arity.zero?
17
+ meth.call
18
+ else
19
+ meth.call(operand)
20
+ end
21
+ rescue NoMethodError
22
+ # Checking for respond_to_superator? is relatively slow, so only do this
23
+ # if calling the superator didn't work out as expected
24
+ if not respond_to_superator? sup
25
+ raise NoMethodError, "Superator #{sup} has not been defined on #{self.class}"
26
+ else
27
+ raise
28
+ end
29
+ end
30
+ end
31
+
32
+ def respond_to_superator?(sup)
33
+ respond_to? superator_definition_name_for(sup)
34
+ end
35
+
36
+ def defined_superators
37
+ methods.grep(/^superator_definition_/).map { |m| superator_decode(m.to_s) }
38
+ end
39
+
40
+ protected
41
+
42
+ def superator(operator, &block)
43
+ raise ArgumentError, "block not supplied" unless block_given?
44
+ raise ArgumentError, "Not a valid superator!" unless superator_valid?(operator)
45
+
46
+ real_operator = real_operator_from_superator operator
47
+
48
+ class_eval do
49
+ # Step in front of the old operator's dispatching.
50
+ alias_for_real_method = superator_alias_for real_operator
51
+
52
+ if instance_methods.any? {|m| m.to_s == real_operator} && !respond_to_superator?(operator)
53
+ alias_method alias_for_real_method, real_operator
54
+ end
55
+
56
+ define_method superator_definition_name_for(operator), &block
57
+
58
+ # When we get to the method defining, we have to know whether the superator had to be aliased
59
+ # or if it's new entirely.
60
+ define_method(real_operator) do |operand|
61
+ if operand.kind_of?(SuperatorFlag) && operand.superator_queue.any?
62
+ sup = operand.superator_queue.unshift(real_operator).join
63
+ operand.superator_queue.clear
64
+
65
+ superator_send(sup, operand)
66
+ else
67
+ # If the method_alias is defined
68
+ if respond_to? alias_for_real_method
69
+ __send__(alias_for_real_method, operand)
70
+ else
71
+ raise NoMethodError, "undefined method #{real_operator} for #{operand.inspect}:#{operand.class}"
72
+ end
73
+ end
74
+ end
75
+
76
+ end
77
+
78
+ def undef_superator(sup)
79
+ if respond_to_superator?(sup)
80
+ real_operator = real_operator_from_superator sup
81
+ real_operator_alias = superator_alias_for sup
82
+
83
+ (class << self; self; end).instance_eval do
84
+ undef_method superator_definition_name_for(sup)
85
+ if respond_to? real_operator_alias
86
+ alias_method real_operator, real_operator_alias if defined_superators.empty?
87
+ else
88
+ undef_method real_operator
89
+ end
90
+ end
91
+ else
92
+ raise NoMethodError, "undefined superator #{sup} for #{self.inspect}:#{self.class}"
93
+ end
94
+ end
95
+ end
96
+
97
+ private
98
+
99
+ def superator_encode(str)
100
+ tokenizer = /#{BINARY_OPERATOR_PATTERN}|#{UNARY_OPERATOR_PATTERN_WITHOUT_AT_SIGN}/
101
+ r = str.scan(tokenizer).map do |op|
102
+ op.enum_for(:each_byte).to_a.join "_"
103
+ end
104
+ r.join "__"
105
+ end
106
+
107
+ def superator_decode(str)
108
+ tokens = str.match /^(superator_(definition|alias_for))?((_?\d{2,3})+)((__\d{2,3})+)$/
109
+ if tokens
110
+ (tokens[3].split("_") + tokens[5].split("__")).reject { |x| x.empty? }.map { |s| s.to_i.chr }.join
111
+ end
112
+ end
113
+
114
+ def real_operator_from_superator(sup)
115
+ sup[/^#{BINARY_OPERATOR_PATTERN}/]
116
+ end
117
+
118
+ def superator_alias_for(name)
119
+ "superator_alias_for_#{superator_encode(name)}"
120
+ end
121
+
122
+ def superator_definition_name_for(sup)
123
+ "superator_definition_#{superator_encode(sup)}"
124
+ end
125
+
126
+ def superator_valid?(operator)
127
+ operator =~ VALID_SUPERATOR
128
+ end
129
+ end
130
+
131
+ module SuperatorFlag;end
@@ -0,0 +1,24 @@
1
+ class Object
2
+ attr_reader :superator_queue
3
+
4
+ def -@
5
+ extend SuperatorFlag
6
+ @superator_queue ||= []
7
+ @superator_queue.unshift '-'
8
+ self
9
+ end
10
+
11
+ def +@
12
+ extend SuperatorFlag
13
+ @superator_queue ||= []
14
+ @superator_queue.unshift '+'
15
+ self
16
+ end
17
+
18
+ def ~@
19
+ extend SuperatorFlag
20
+ @superator_queue ||= []
21
+ @superator_queue.unshift '~'
22
+ self
23
+ end
24
+ end
@@ -0,0 +1,4 @@
1
+ module Superators
2
+ MAJOR,MINOR,TINY = 0,9,2
3
+ VERSION = [MAJOR,MINOR,TINY].join '.'
4
+ end
@@ -0,0 +1,320 @@
1
+ require 'superators19'
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
+
58
+ end
59
+
60
+ # This one is going to be very difficult to implement. method_added() maybe?
61
+ it "should work even when the superator's 'main' method is redefined after the superator macro" do
62
+ victim = Class.new do
63
+ def <(_) throw :first end
64
+ superator("<~~") { throw :superator }
65
+ def <(_) throw :last end
66
+ end
67
+ lambda { victim.new() < Object.new }.should throw_symbol(:last)
68
+ lambda { victim.new() <~~ Object.new }.should throw_symbol(:superator)
69
+ end
70
+
71
+ it "should allow the 'real' operator to be called within the superator definition" do
72
+ victim = "Super"
73
+ class << victim
74
+ superator "++" do |operand|
75
+ upcase + operand.upcase
76
+ end
77
+ superator "-~+~-" do |operand|
78
+ self + operand
79
+ end
80
+ end
81
+ (victim ++ "ators").should == "SUPERATORS"
82
+ lambda { victim -~+~- "man" }.should_not raise_error
83
+ end
84
+
85
+ end
86
+
87
+ describe "Defined binary superators" do
88
+
89
+ it "should be available to subclasses" do
90
+ superclass = Class.new do
91
+ superator("<<--") { throw :superclass }
92
+ end
93
+ lambda { Class.new(superclass).new <<-- "Foobar" }.should throw_symbol(:superclass)
94
+ end
95
+
96
+ it "should be overridable in subclasses" do
97
+ parent = Class.new do
98
+ superator("<~") { throw :parent }
99
+ end
100
+
101
+ child = Class.new(parent) do
102
+ superator("<~") { throw :child }
103
+ end
104
+
105
+ lambda { parent.new() <~ Object.new }.should throw_symbol(:parent)
106
+ lambda { child.new() <~ Object.new }.should throw_symbol(:child)
107
+ end
108
+
109
+ it "should redefine an already-defined superator of the same class" do
110
+ lambda do
111
+ Class.new do
112
+ superator("<---") { throw :first }
113
+ superator("<---") { throw :last }
114
+ end.new <--- Object.new
115
+ end.should throw_symbol(:last)
116
+ end
117
+
118
+ end
119
+
120
+ describe "The superator_send method" do
121
+
122
+ it "should execute the block within the object's instance (so self is the instance, not the class)" do
123
+ Class.new do
124
+ superator "-+-" do |other|
125
+ self.should_not be_kind_of(Class)
126
+ end
127
+ end
128
+ end
129
+
130
+ it "should receive the proper arguments" do
131
+ x = Class.new do
132
+ superator("-+-+~++~") {}
133
+ end
134
+ victim, operand = x.new, "a clue"
135
+ victim.should_receive(:superator_send).once.with("-+-+~++~", operand)
136
+ victim -+-+~++~ operand
137
+ end
138
+
139
+ it "should exist on an Object instance" do
140
+ Object.new.should respond_to(:superator_send)
141
+ end
142
+
143
+ it "should raise NameError if an invalid superator is given" do
144
+ lambda do
145
+ Object.new.superator_send("17BB&DB & !! @", Object.new.extend(SuperatorFlag))
146
+ end.should raise_error(NameError)
147
+ end
148
+
149
+ end
150
+
151
+ describe "The respond_to_superator? method" do
152
+
153
+ it "should be available on all objects" do
154
+ Object.new.should respond_to(:respond_to_superator?)
155
+ end
156
+
157
+ it "should return true if a superator was defined for the object's class" do
158
+ Class.new do
159
+ superator("<--") {}
160
+ end.new.respond_to_superator?("<--").should be_true
161
+ end
162
+
163
+ it "should return true if a superator was defined for the object's superclass" do
164
+ parent = Class.new do
165
+ superator("<=~~") {}
166
+ end
167
+ Class.new(parent) {}.new.respond_to_superator?("<=~~").should be_true
168
+ end
169
+
170
+ it "should return false or nil if a superator was not defined" do
171
+ result = Class.new.new.respond_to_superator?("<" + ("-" * 100))
172
+ (!! result).should be_false
173
+ end
174
+
175
+ it "should return false for arguments only similar to (not the same as) the defined superator(s)" do
176
+ labrat = Class.new do
177
+ superator("<" + ('~' * 10)) {}
178
+ end.new
179
+ (!! labrat.respond_to_superator?('<' + ('~' * 11))).should be_false
180
+ (!! labrat.respond_to_superator?('<' + ('~' * 9))).should be_false
181
+ end
182
+
183
+ end
184
+
185
+ describe "The undef_superator method" do
186
+
187
+ it "should properly delete a superator" do
188
+ klass = Class.new do
189
+ superator("<----") {}
190
+ end
191
+ lambda do
192
+ obj = klass.new
193
+ obj.undef_superator "<----"
194
+ obj <---- Object.new
195
+ end.should raise_error(NameError)
196
+ end
197
+
198
+ it "should make respond_to_superator?() return false" do
199
+ sup = "<<---"
200
+ klass = Class.new do
201
+ superator(sup) {}
202
+ end
203
+ obj = klass.new
204
+ obj.undef_superator sup
205
+ obj.respond_to_superator?(sup).should be_false
206
+ end
207
+
208
+ end
209
+
210
+ describe "The monkey patch" do
211
+
212
+ it "should create a superators attr_reader" do
213
+ Object.new.should respond_to(:superator_queue)
214
+ Object.new.should_not respond_to(:superator_queue=)
215
+ end
216
+
217
+ end
218
+
219
+ describe "The defined_superators() method" do
220
+
221
+ it "should return an array of superator Strings when called on an object" do
222
+ sups = %w"-- ++ +- -+"
223
+ klass = Class.new do
224
+ sups.each do |sup|
225
+ superator(sup) {}
226
+ end
227
+ end
228
+ defined = klass.new.defined_superators
229
+
230
+ defined.should be_kind_of(Array)
231
+ defined.size.should == sups.size
232
+ sups.each { |sup| defined.should include(sup) }
233
+ end
234
+
235
+ it "should include superators defined in a superclass" do
236
+ parent = Class.new do
237
+ superator("--") {}
238
+ end
239
+ Class.new(parent).new.defined_superators.should include("--")
240
+ end
241
+
242
+ end
243
+
244
+ describe "The 'real' operator finding algorithm" do
245
+
246
+ it "should work with minus and unary negation" do
247
+ real_operator_from_superator("---").should == "-"
248
+ end
249
+
250
+ it "should work with plus and unary plus" do
251
+ real_operator_from_superator("+++").should == "+"
252
+ end
253
+
254
+ it "should return nil when given only unary tildes" do
255
+ real_operator_from_superator("~~~").should be_nil
256
+ end
257
+
258
+ it "should work properly with the operators that are expanded versions of other operators" do
259
+ real_operator_from_superator("<<--").should == "<<"
260
+ real_operator_from_superator("<~~-").should == "<"
261
+ real_operator_from_superator("**+~").should == "**"
262
+ real_operator_from_superator("*+~~").should == "*"
263
+ real_operator_from_superator("<=~~").should == "<="
264
+ real_operator_from_superator(">=+-").should == ">="
265
+ real_operator_from_superator("=~~~").should == "=~"
266
+ real_operator_from_superator("<=>+").should == "<=>"
267
+ real_operator_from_superator("===+").should == "==="
268
+ real_operator_from_superator("==+-").should == "=="
269
+ end
270
+ end
271
+
272
+ describe "Superator method en-/decoding" do
273
+
274
+ before do
275
+ @uses = { "<<~~" => "60_60__126__126",
276
+ "<=>~" => "60_61_62__126",
277
+ "----" => "45__45__45__45",
278
+ "+-~+-~" => "43__45__126__43__45__126" }
279
+ end
280
+
281
+ it "should return the same value after encoding and decoding" do
282
+ @uses.keys.each do |operator|
283
+ superator_decode(superator_encode(operator)).should == operator
284
+ end
285
+ end
286
+
287
+ it "should encode binary superators properly" do
288
+ @uses.each_pair { |key, value| superator_encode(key).should == value }
289
+ end
290
+
291
+ it "should decode binary superators properly" do
292
+ @uses.each_pair { |key, value| superator_decode(value).should == key }
293
+ end
294
+
295
+ it "should create proper method definition name" do
296
+ op = "|+-"
297
+ superator_definition_name_for(op).should =~ /#{superator_encode(op)}$/
298
+ end
299
+
300
+ it "should be containable in a method definition" do
301
+ lambda do
302
+ eval ":jay_#{superator_encode("<<+~--")}"
303
+ end.should_not raise_error(SyntaxError)
304
+ end
305
+
306
+ end
307
+
308
+ describe "A superator's arguments" do
309
+ # These specs are included for someone to potentially fix. Their failing
310
+ # constitutes a bug, though there's no clear way to implement this.
311
+
312
+ # These are types that can have no eigenclass. One way to store Kernel#caller
313
+ # stacktraces against the Fixnum value on which the unary methods were executed.
314
+ it "should allow a Fixnum as an operand" # This *MAY* never work
315
+ it "should allow a Symbol as an operand"
316
+ it "should allow true as an operand"
317
+ it "should allow false as an operand"
318
+ it "should allow nil as an operand"
319
+
320
+ end
@@ -0,0 +1,18 @@
1
+ $:.push File.expand_path("../lib", __FILE__)
2
+ require "superators19/version"
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "superators19"
6
+ s.version = Superators::VERSION
7
+ s.authors = ["Jay Phillips", "Neil Conway", "Scott Gonyea"]
8
+ s.email = ["jay@codemecca.com"]
9
+ s.homepage = "https://github.com/neilconway/superators"
10
+ s.summary = %q{Superators add new sexy operators to your Ruby objects.}
11
+ s.description = %q{Superators are a superset of new Ruby operators you can create and use.}
12
+
13
+ s.add_development_dependency "rspec"
14
+
15
+ s.files = `git ls-files`.split("\n")
16
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
+ s.require_paths = ["lib"]
18
+ end
metadata ADDED
@@ -0,0 +1,89 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: superators19
3
+ version: !ruby/object:Gem::Version
4
+ hash: 63
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 9
9
+ - 2
10
+ version: 0.9.2
11
+ platform: ruby
12
+ authors:
13
+ - Jay Phillips
14
+ - Neil Conway
15
+ - Scott Gonyea
16
+ autorequire:
17
+ bindir: bin
18
+ cert_chain: []
19
+
20
+ date: 2012-03-12 00:00:00 Z
21
+ dependencies:
22
+ - !ruby/object:Gem::Dependency
23
+ name: rspec
24
+ prerelease: false
25
+ requirement: &id001 !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - ">="
29
+ - !ruby/object:Gem::Version
30
+ hash: 3
31
+ segments:
32
+ - 0
33
+ version: "0"
34
+ type: :development
35
+ version_requirements: *id001
36
+ description: Superators are a superset of new Ruby operators you can create and use.
37
+ email:
38
+ - jay@codemecca.com
39
+ executables: []
40
+
41
+ extensions: []
42
+
43
+ extra_rdoc_files: []
44
+
45
+ files:
46
+ - History.txt
47
+ - README.md
48
+ - Rakefile
49
+ - lib/superators19.rb
50
+ - lib/superators19/macro.rb
51
+ - lib/superators19/monkey_patch.rb
52
+ - lib/superators19/version.rb
53
+ - spec/superator_spec.rb
54
+ - superators19.gemspec
55
+ homepage: https://github.com/neilconway/superators
56
+ licenses: []
57
+
58
+ post_install_message:
59
+ rdoc_options: []
60
+
61
+ require_paths:
62
+ - lib
63
+ required_ruby_version: !ruby/object:Gem::Requirement
64
+ none: false
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ hash: 3
69
+ segments:
70
+ - 0
71
+ version: "0"
72
+ required_rubygems_version: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ hash: 3
78
+ segments:
79
+ - 0
80
+ version: "0"
81
+ requirements: []
82
+
83
+ rubyforge_project:
84
+ rubygems_version: 1.8.17
85
+ signing_key:
86
+ specification_version: 3
87
+ summary: Superators add new sexy operators to your Ruby objects.
88
+ test_files:
89
+ - spec/superator_spec.rb