superators19 0.9.2

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/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