rubyunderscore 0.1.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.
data/README.md ADDED
@@ -0,0 +1,60 @@
1
+ RubyUnderscore
2
+ ==============
3
+
4
+ [Closures](http://metaphysicaldeveloper.wordpress.com/2009/05/02/closures-collections-and-some-functional-programming/)
5
+ are very useful tools, and ruby
6
+ [Enumerable](http://ruby-doc.org/core-1.8.7/classes/Enumerable.html) mixin makes them even
7
+ more useful.
8
+
9
+ However, as you decompose more and more your iterations into a sequence of
10
+ [maps](http://ruby-doc.org/core-1.8.7/classes/Enumerable.html#M001146),
11
+ [selects](http://ruby-doc.org/core-1.8.7/classes/Enumerable.html#M001143),
12
+ [rejects](http://ruby-doc.org/core-1.8.7/classes/Enumerable.html#M001144),
13
+ [group_bys](http://ruby-doc.org/core-1.8.7/classes/Enumerable.html#M001150) and
14
+ [reduces](http://ruby-doc.org/core-1.8.7/classes/Enumerable.html#M001148), more commonly
15
+ you see simple blocks such as:
16
+
17
+ collection.map { |x| x.invoke }
18
+ dates.select { |d| d.greater_than(old_date) }
19
+ classes.reject { |c| c.subclasses.include?(Array) }
20
+
21
+ RubyUnderscore modify classes so that you can also use a short notation for simple closures. With such, the above examples can be written as:
22
+
23
+ collection.map _.invoke
24
+ dates.select _.greater_than old_date
25
+ classes.reject _.subclasses.include? Array
26
+
27
+ Just replace the iterating argument with the underscore symbol (*_*), and ditch the
28
+ parenthesis. [More info](http://metaphysicaldeveloper.wordpress.com/2010/10/31/rubyunderscore-a-bit-of-arc-and-scala-in-ruby/)
29
+
30
+ Quick Example
31
+ ----
32
+ The example consists of getting all instance methods of String, Array, Class that end with 'd?'
33
+
34
+ require 'ruby_underscore'
35
+
36
+ class MethodFinder
37
+ include RubyUnderscore::Base
38
+
39
+ def find_interrogation_methods
40
+ [String, Array, Class].map(_.public_instance_methods.grep /d\?$/).flatten.sort.uniq
41
+ end
42
+ end
43
+ p MethodFinder.new.find_interrogation_methods
44
+
45
+
46
+ Using Ruby Underscore
47
+ ----
48
+ As in the example above, simply by including the module include RubyUnderscore::Base on the
49
+ class, all methods (class methods as well) will allow you to use the underscore symbol to
50
+ write simple blocks.
51
+
52
+
53
+ Meta
54
+ ----
55
+
56
+ Created by Daniel Ribeiro
57
+
58
+ Released under the MIT License: http://www.opensource.org/licenses/mit-license.php
59
+
60
+ http://github.com/danielribeiro/RubyUnderscore
data/Rakefile ADDED
@@ -0,0 +1,44 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'rake/clean'
4
+ require 'rake/gempackagetask'
5
+ require 'rake/rdoctask'
6
+ require 'rake/testtask'
7
+ require 'spec/rake/spectask'
8
+
9
+ task :default => [:spec]
10
+
11
+ begin
12
+ require 'jeweler'
13
+ Jeweler::Tasks.new do |gem|
14
+ gem.name = "rubyunderscore"
15
+ gem.summary = %Q{Simple way to create simple blocks}
16
+ gem.description = %Q{It allows you to create simple blocks by using underscore symbol}
17
+ gem.email = "danrbr+rubyunderscore@gmail.com"
18
+ gem.homepage = "http://github.com/danielribeiro/RubyUnderscore"
19
+ gem.authors = ["Daniel Ribeiro"]
20
+ gem.add_dependency 'ParseTree', '=3.0.5'
21
+ gem.add_dependency 'ruby2ruby'
22
+ gem.files = FileList["[A-Z]*", "{bin,lib}/**/*"]
23
+ # gem.add_development_dependency "thoughtbot-shoulda", ">= 0"
24
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
25
+ end
26
+ Jeweler::GemcutterTasks.new
27
+ rescue LoadError
28
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
29
+ end
30
+
31
+ require 'rake/rdoctask'
32
+ Rake::RDocTask.new do |rdoc|
33
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
34
+
35
+ rdoc.rdoc_dir = 'rdoc'
36
+ rdoc.title = "rubyunderscore #{version}"
37
+ rdoc.rdoc_files.include('README*')
38
+ rdoc.rdoc_files.include('lib/**/*.rb')
39
+ end
40
+
41
+ Spec::Rake::SpecTask.new do |t|
42
+ t.spec_files = FileList['spec/**/*.rb']
43
+ t.libs << Dir["lib"]
44
+ end
data/TODO ADDED
@@ -0,0 +1,7 @@
1
+ - Better readme
2
+ - gem and publish
3
+
4
+ - enhance class
5
+ - changer of ruby files (source)
6
+ - debug support
7
+ - rdoc
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,21 @@
1
+ require 'tree_converters'
2
+
3
+ module RubyUnderscore
4
+ module Base
5
+ module ClassMethods
6
+ def method_added(method_name)
7
+ super
8
+ UnderscoreEnhancer.new.enhance self, method_name
9
+ end
10
+
11
+ def singleton_method_added(method_name)
12
+ super
13
+ metaclass = class << self; self; end
14
+ UnderscoreEnhancer.new.enhance metaclass, method_name
15
+ end
16
+ end
17
+ def self.included(receiver)
18
+ receiver.extend ClassMethods
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,233 @@
1
+ #gem 'ParseTree', '=3.0.5'
2
+ require 'pp'
3
+ require 'sexp_processor'
4
+ require 'ruby2ruby'
5
+ require 'unified_ruby'
6
+ require 'parse_tree'
7
+ module RubyUnderscore
8
+ class AbstractProcessor < SexpProcessor
9
+ def initialize
10
+ super
11
+ @alternate = SexpProcessor.new
12
+ end
13
+
14
+ def proceed(exp)
15
+ @alternate.process exp
16
+ end
17
+
18
+ def assert_empty(meth, exp, exp_orig)
19
+
20
+ end
21
+ end
22
+
23
+ module EnhancerHelper
24
+ # Deep Clone or arrays. Focused on arrays to be converted into sexp.
25
+ def clone(array)
26
+ a = []
27
+ array.each do | x |
28
+ a << if x.is_a? Array
29
+ clone(x)
30
+ elsif x.is_a? Symbol or x.is_a? Fixnum or x.is_a? Regexp
31
+ x
32
+ else
33
+ x.clone
34
+ end
35
+ end
36
+ a
37
+ end
38
+
39
+ def chain(sexp, *processorClasses)
40
+ processorClasses.inject(sexp) do |memo, clas|
41
+ clas.new.process memo
42
+ end
43
+ end
44
+
45
+ def needsEnhancing(clas, method)
46
+ sexpNeedsEnhancing sexpOf clas, method
47
+ end
48
+
49
+
50
+ def sexpOf(clas, method)
51
+ ParseTree.translate clas, method
52
+ end
53
+
54
+ def sexpNeedsEnhancing(sexp)
55
+ sexpEnhancingCount(sexp) > 0
56
+ end
57
+
58
+ def sexpEnhancingCount(sexp)
59
+ return 0 if sexp.nil?
60
+ parser = EnhancerDetector.new
61
+ parser.process clone sexp
62
+ return parser.enhanceCount
63
+ end
64
+
65
+ def assertSexpIs(sexp, type)
66
+ raise "Wrong sexp type: #{sexp.first}" unless sexp.first == type
67
+ end
68
+
69
+ class EnhancerDetector < AbstractProcessor
70
+ attr_reader :enhanceCount
71
+
72
+ def initialize
73
+ super
74
+ @enhanceCount = 0
75
+ end
76
+
77
+ def process_vcall(sexp)
78
+ @enhanceCount += 1 if sexp[1] == :_
79
+ return s *sexp
80
+ end
81
+ end
82
+ end
83
+
84
+ class AbstractSexp
85
+ attr_reader :sexp, :enhancer
86
+
87
+ include EnhancerHelper
88
+ def initialize(sexp, enhancer)
89
+ assertSexpIs sexp, type
90
+ @sexp = sexp
91
+ @enhancer = enhancer
92
+ deconstruct sexp[1..-1]
93
+ end
94
+
95
+ def s(*args)
96
+ enhancer.s *args
97
+ end
98
+
99
+ def process(arg)
100
+ enhancer.process arg
101
+ end
102
+
103
+ def regularEnhance
104
+ params = asArray
105
+ params.push(process(args)) if args
106
+ s *params
107
+ end
108
+
109
+ def subtreeNeedsEnhance?
110
+ enhancer.vcallCount && enhancer.vcallCount > 0
111
+ end
112
+
113
+ def decVcallCount
114
+ enhancer.vcallCount -= 1
115
+ end
116
+
117
+ def enhance
118
+ count = sexpEnhancingCount args
119
+ return regularEnhance unless count > 0
120
+ enhancer.vcallCount = count if enhancer.vcallCount.nil?
121
+ self.args = process args
122
+ if enhancer.vcallCount == 0
123
+ enhancer.vcallCount = nil
124
+ end
125
+ return regularEnhance unless subtreeNeedsEnhance?
126
+ decVcallCount
127
+ s :iter, s(*asArray), s(:dasgn_curr, enhancer.variableName), argumentList
128
+ end
129
+
130
+ def argumentList
131
+ args[1]
132
+ end
133
+
134
+
135
+ def deconstruct(sexp)
136
+ raise "subclass responsibility"
137
+ end
138
+
139
+ def type
140
+ raise "subclass responsibility"
141
+ end
142
+
143
+ def asArray
144
+ raise "subclass responsibility"
145
+ end
146
+
147
+ end
148
+
149
+ class Fcall < AbstractSexp
150
+ attr_accessor :method, :args
151
+ def type
152
+ :fcall
153
+ end
154
+
155
+ def deconstruct(sexp)
156
+ @method, @args = sexp
157
+ end
158
+
159
+
160
+ def asArray
161
+ [type, method]
162
+ end
163
+
164
+
165
+ end
166
+
167
+ class Call < AbstractSexp
168
+ attr_accessor :method, :args, :target
169
+ def type
170
+ :call
171
+ end
172
+
173
+ def deconstruct(sexp)
174
+ @target, @method, @args = sexp
175
+ end
176
+
177
+ def asArray
178
+ [type, process(target), method]
179
+ end
180
+ end
181
+
182
+
183
+ class VcallEnhancer < AbstractProcessor
184
+ attr_accessor :vcallCount
185
+ include EnhancerHelper
186
+ public :s
187
+
188
+ def initialize
189
+ super
190
+ self.vcallCount = nil
191
+ end
192
+
193
+ def variableName
194
+ :"__vcall_enhancer_i"
195
+ end
196
+
197
+
198
+ def process_vcall(sexp)
199
+ return s *sexp unless sexp[1] == :_
200
+ s(:dvar, variableName)
201
+ end
202
+
203
+ def process_call(sexp)
204
+ changeGenericCall sexp
205
+ end
206
+
207
+ def process_fcall(sexp)
208
+ changeGenericCall sexp
209
+ end
210
+
211
+ def changeGenericCall(sexp)
212
+ sexpClass(sexp.first).new(sexp, self).enhance
213
+ end
214
+ protected
215
+
216
+ def sexpClass(type)
217
+ return Call if type == :call
218
+ return Fcall if type == :fcall
219
+ raise "Unknown sexp: #{type}"
220
+ end
221
+ end
222
+
223
+
224
+ class UnderscoreEnhancer
225
+ include EnhancerHelper
226
+
227
+ def enhance(clas, method)
228
+ sexp = sexpOf clas, method
229
+ return unless sexpNeedsEnhancing sexp
230
+ clas.class_eval chain sexp, VcallEnhancer, Unifier, Ruby2Ruby
231
+ end
232
+ end
233
+ end
@@ -0,0 +1,76 @@
1
+ $LOAD_PATH.unshift File.join(File.dirname(__FILE__),'..','lib')
2
+ require 'ruby_underscore'
3
+
4
+
5
+ class Enhanced
6
+ include RubyUnderscore::Base
7
+ def self.aPublicClassMethod
8
+ [__method__].map _.to_s.concat('Enhanced')
9
+ end
10
+
11
+ def aPublic
12
+ [__method__].map _.to_s.concat('Enhanced')
13
+ end
14
+
15
+ def aPrivate
16
+ [__method__].map _.to_s.concat('Enhanced')
17
+ end
18
+ end
19
+
20
+ describe RubyUnderscore do
21
+ it "should enhance all methods of a class that includes RubyUnderscore::Base" do
22
+ Enhanced.new.aPublic.should == ["aPublicEnhanced"]
23
+ end
24
+
25
+ it "should enhance private methods as well" do
26
+ Enhanced.new.aPrivate.should == ["aPrivateEnhanced"]
27
+ end
28
+
29
+ it "should enhance class methods" do
30
+ Enhanced.aPublicClassMethod.should == ["aPublicClassMethodEnhanced"]
31
+ end
32
+
33
+ it "the original method_added of the class must call super" do
34
+ cl = Class.new do
35
+ @@original_method_added_invoked = false
36
+ def self.method_added(method_name)
37
+ super
38
+ @@original_method_added_invoked = true
39
+ end
40
+ include RubyUnderscore::Base
41
+
42
+ def dummy
43
+ 'dum'
44
+ end
45
+
46
+ def self.original_method_added_invoked
47
+ @@original_method_added_invoked
48
+ end
49
+ end
50
+ cl.original_method_added_invoked.should be_true
51
+ end
52
+
53
+
54
+ it "will work even if the class defines singleton_method_added, but invokes super" do
55
+ cl = Class.new do
56
+ @@original_singleton_method_added_invoked = false
57
+ def self.singleton_method_added(method_name)
58
+ super
59
+ @@original_singleton_method_added_invoked = true
60
+ end
61
+ include RubyUnderscore::Base
62
+
63
+ def self.dummy
64
+ [__method__].map _.to_s.concat('Enhanced')
65
+ end
66
+
67
+ def self.original_singleton_method_added_invoked
68
+ @@original_singleton_method_added_invoked
69
+ end
70
+ end
71
+ cl.original_singleton_method_added_invoked.should be_true
72
+ cl.dummy.should == ["dummyEnhanced"]
73
+ end
74
+
75
+ end
76
+
@@ -0,0 +1,179 @@
1
+ $LOAD_PATH.unshift File.join(File.dirname(__FILE__),'..','lib')
2
+ require 'tree_converters'
3
+
4
+ # Making it simple to test.
5
+ class RubyUnderscore::VcallEnhancer
6
+ def variableName
7
+ :x
8
+ end
9
+ end
10
+
11
+
12
+ class Array
13
+ def mapProc(proc)
14
+ map &proc
15
+ end
16
+ end
17
+
18
+ class Checks
19
+ def doesntNeedEnhancing
20
+ (1..20).map do |i|
21
+ i + 1
22
+ end
23
+ end
24
+
25
+ def stillNoEnhancing
26
+ _(9)
27
+ end
28
+
29
+ def needsEnhacing
30
+ [0].map _
31
+ end
32
+
33
+ def alsoNeedsEnhacing
34
+ [0].map _.to_s
35
+ end
36
+ end
37
+
38
+ module OutputHelper
39
+ def map(&block)
40
+ [0].map &block
41
+ end
42
+
43
+ def idblock(&block)
44
+ block
45
+ end
46
+
47
+ def identity_of(arg)
48
+ arg
49
+ end
50
+ end
51
+
52
+ class Input
53
+ include OutputHelper
54
+ def simple
55
+ [0].map _
56
+ end
57
+
58
+ def methodCall
59
+ [0].map _.to_s
60
+ end
61
+
62
+ def longComplexMethodChain
63
+ [0].map _.to_i.to_s.center(40, '-').to_s
64
+ end
65
+
66
+ def nested
67
+ ["1"].concat([0].map _.to_s)
68
+ end
69
+
70
+ def fcall
71
+ map _.to_s
72
+ end
73
+
74
+ def multiple_fcall
75
+ identity_of map _.to_s
76
+ end
77
+
78
+ def fcall_call_mix
79
+ identity_of [0].map _.to_s
80
+ end
81
+
82
+ def call_fcall_mix
83
+ [0].mapProc idblock _.to_s
84
+ end
85
+
86
+ def nested_enhancement_on_matrix
87
+ [[[:a00], [:a01]], [[:a10], [:a11]]].each(_.each(_.push(:last)))
88
+ end
89
+
90
+ def hasItAll
91
+ identity_of [[[:a00], [:a01]], [[:a10], [:a11]]].each(_.each(_.push(:last)))
92
+ identity_of [0].map _.to_s
93
+ end
94
+
95
+ end
96
+
97
+ class Expected
98
+ include OutputHelper
99
+ def simple
100
+ [0].map { |x| x }
101
+ end
102
+
103
+ def methodCall
104
+ [0].map { |x| x.to_s }
105
+ end
106
+
107
+ def longComplexMethodChain
108
+ [0].map { |x| x.to_i.to_s.center(40, '-').to_s }
109
+ end
110
+
111
+ def nested
112
+ ["1"].concat([0].map { |x| x.to_s } )
113
+ end
114
+
115
+ def fcall
116
+ map { |x| x.to_s }
117
+ end
118
+
119
+ def multiple_fcall
120
+ identity_of map { |x| x.to_s }
121
+ end
122
+
123
+ def fcall_call_mix
124
+ identity_of [0].map { |x| x.to_s }
125
+ end
126
+
127
+ def call_fcall_mix
128
+ [0].mapProc idblock { |x| x.to_s }
129
+ end
130
+
131
+ def nested_enhancement_on_matrix
132
+ [[[:a00], [:a01]], [[:a10], [:a11]]].each { |x| x.each { |x| x.push(:last)} }
133
+ end
134
+
135
+ def hasItAll
136
+ identity_of [[[:a00], [:a01]], [[:a10], [:a11]]].each { |x| x.each { |x| x.push(:last)} }
137
+ identity_of [0].map { |x| x.to_s }
138
+ end
139
+ end
140
+
141
+ describe 'TreeConverters' do
142
+ attr_reader :un
143
+
144
+ before(:each) do
145
+ @un = RubyUnderscore::UnderscoreEnhancer.new
146
+ end
147
+
148
+ def assert_same_after_enhancing(method)
149
+ un.enhance Input, method
150
+ input = un.sexpOf Input, method
151
+ output = un.sexpOf Expected, method
152
+ input.should == output
153
+ Input.new.send(method).should == Expected.new.send(method)
154
+ end
155
+
156
+ it "detects correctly what needs and what doesn't need enhancing" do
157
+ un.needsEnhancing(Checks, :doesntNeedEnhancing).should be_false
158
+ un.needsEnhancing(Checks, :stillNoEnhancing).should be_false
159
+ un.needsEnhancing(Checks, :needsEnhacing).should be_true
160
+ un.needsEnhancing(Checks, :alsoNeedsEnhacing).should be_true
161
+ end
162
+
163
+
164
+ it "can count correctly how many vcalls need to be enhanced" do
165
+ sexp = un.sexpOf Input, :nested_enhancement_on_matrix
166
+ un.sexpEnhancingCount(sexp).should == 2
167
+ end
168
+
169
+
170
+ [:simple, :methodCall, :longComplexMethodChain,
171
+ :nested, :fcall, :multiple_fcall, :fcall_call_mix,
172
+ :call_fcall_mix, :nested_enhancement_on_matrix, :hasItAll].
173
+ each do |m|
174
+ it m.to_s do
175
+ assert_same_after_enhancing m
176
+ end
177
+ end
178
+ end
179
+
metadata ADDED
@@ -0,0 +1,105 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rubyunderscore
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Daniel Ribeiro
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-11-06 00:00:00 -02:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: ParseTree
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - "="
28
+ - !ruby/object:Gem::Version
29
+ hash: 13
30
+ segments:
31
+ - 3
32
+ - 0
33
+ - 5
34
+ version: 3.0.5
35
+ type: :runtime
36
+ version_requirements: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ name: ruby2ruby
39
+ prerelease: false
40
+ requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ hash: 3
46
+ segments:
47
+ - 0
48
+ version: "0"
49
+ type: :runtime
50
+ version_requirements: *id002
51
+ description: It allows you to create simple blocks by using underscore symbol
52
+ email: danrbr+rubyunderscore@gmail.com
53
+ executables: []
54
+
55
+ extensions: []
56
+
57
+ extra_rdoc_files:
58
+ - README.md
59
+ - TODO
60
+ files:
61
+ - README.md
62
+ - Rakefile
63
+ - TODO
64
+ - VERSION
65
+ - lib/ruby_underscore.rb
66
+ - lib/tree_converters.rb
67
+ - spec/tree_converters_spec.rb
68
+ - spec/ruby_underscore_spec.rb
69
+ has_rdoc: true
70
+ homepage: http://github.com/danielribeiro/RubyUnderscore
71
+ licenses: []
72
+
73
+ post_install_message:
74
+ rdoc_options:
75
+ - --charset=UTF-8
76
+ require_paths:
77
+ - lib
78
+ required_ruby_version: !ruby/object:Gem::Requirement
79
+ none: false
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ hash: 3
84
+ segments:
85
+ - 0
86
+ version: "0"
87
+ required_rubygems_version: !ruby/object:Gem::Requirement
88
+ none: false
89
+ requirements:
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ hash: 3
93
+ segments:
94
+ - 0
95
+ version: "0"
96
+ requirements: []
97
+
98
+ rubyforge_project:
99
+ rubygems_version: 1.3.7
100
+ signing_key:
101
+ specification_version: 3
102
+ summary: Simple way to create simple blocks
103
+ test_files:
104
+ - spec/tree_converters_spec.rb
105
+ - spec/ruby_underscore_spec.rb