mshakhan-fruby 0.0.1
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/LICENSE +15 -0
- data/README.rdoc +18 -0
- data/Rakefile +54 -0
- data/lib/definer.rb +80 -0
- data/lib/ext.rb +49 -0
- data/lib/fruby.rb +7 -0
- data/lib/matcher.rb +41 -0
- data/lib/utils.rb +25 -0
- data/lib/version.rb +3 -0
- data/spec/definer_spec.rb +35 -0
- data/spec/matcher_spec.rb +24 -0
- data/spec/utils_spec.rb +18 -0
- metadata +85 -0
data/LICENSE
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
2
|
+
Version 2, December 2004
|
3
|
+
|
4
|
+
Copyright (C) 2004 Sam Hocevar
|
5
|
+
14 rue de Plaisance, 75014 Paris, France
|
6
|
+
Everyone is permitted to copy and distribute verbatim or modified
|
7
|
+
copies of this license document, and changing it is allowed as long
|
8
|
+
as the name is changed.
|
9
|
+
|
10
|
+
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
11
|
+
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
12
|
+
|
13
|
+
0. You just DO WHAT THE FUCK YOU WANT TO.
|
14
|
+
|
15
|
+
http://sam.zoy.org/wtfpl/COPYING
|
data/README.rdoc
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
== Description
|
2
|
+
|
3
|
+
Some functional programming features for ruby.
|
4
|
+
|
5
|
+
== Usage
|
6
|
+
|
7
|
+
Now implemented only *defun* method. You can use it like this:
|
8
|
+
|
9
|
+
module Math
|
10
|
+
class << self
|
11
|
+
include FRuby::Definer
|
12
|
+
defun(:fact, Fixnum) { |n| n * fact(n - 1) }
|
13
|
+
defun(:fact, 0) { 1 }
|
14
|
+
defun(:fact, 1) { 1 }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
Math::fact(5) #=> 120
|
data/Rakefile
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'rake/clean'
|
4
|
+
require 'rake/gempackagetask'
|
5
|
+
require 'rake/rdoctask'
|
6
|
+
require 'lib/version'
|
7
|
+
|
8
|
+
GEM_NAME = 'fruby'
|
9
|
+
|
10
|
+
spec = Gem::Specification.new do |s|
|
11
|
+
s.name = GEM_NAME
|
12
|
+
s.version = FRuby::VERSION
|
13
|
+
s.has_rdoc = true
|
14
|
+
s.extra_rdoc_files = ['README.rdoc', 'LICENSE']
|
15
|
+
s.summary = 'Some functional programming features for ruby'
|
16
|
+
s.description = s.summary
|
17
|
+
s.author = 'Mikhail Shakhanov'
|
18
|
+
s.email = 'mshakhan@gmail.com'
|
19
|
+
s.files = %w(LICENSE README.rdoc Rakefile) + Dir.glob("{lib,spec}/**/*")
|
20
|
+
s.require_path = "lib"
|
21
|
+
|
22
|
+
s.add_dependency 'rake'
|
23
|
+
s.add_dependency 'rspec'
|
24
|
+
|
25
|
+
s.required_ruby_version = ">= 1.8.4"
|
26
|
+
end
|
27
|
+
|
28
|
+
Rake::GemPackageTask.new(spec) do |p|
|
29
|
+
p.gem_spec = spec
|
30
|
+
p.need_tar = true
|
31
|
+
p.need_zip = true
|
32
|
+
end
|
33
|
+
|
34
|
+
Rake::RDocTask.new do |rdoc|
|
35
|
+
files =['README.rdoc', 'LICENSE', 'lib/**/*.rb']
|
36
|
+
rdoc.rdoc_files.add(files)
|
37
|
+
rdoc.main = "README.rdoc"
|
38
|
+
rdoc.title = "FRuby Docs"
|
39
|
+
rdoc.rdoc_dir = 'doc/rdoc'
|
40
|
+
rdoc.options << '--line-numbers'
|
41
|
+
end
|
42
|
+
|
43
|
+
desc 'Run specs'
|
44
|
+
task :spec do
|
45
|
+
spec_path = ENV['SPEC_PATH'] || 'spec'
|
46
|
+
system("spec #{spec_path} -c")
|
47
|
+
end
|
48
|
+
|
49
|
+
desc "Generate *.gemspec file"
|
50
|
+
task :gemspec do
|
51
|
+
path = File.join(File.dirname(__FILE__), "#{GEM_NAME}.gemspec")
|
52
|
+
puts %{Writing "#{path}"}
|
53
|
+
File.open(path, "w") { |file| file.write(spec.to_ruby) }
|
54
|
+
end
|
data/lib/definer.rb
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
module FRuby
|
2
|
+
# This module implements "pattern matching" function definition style
|
3
|
+
#
|
4
|
+
# Author:: Mikhail Shakhanov (mailto:mshakhan@gmail.com)
|
5
|
+
# Copyright:: Copyright (c) 2009 mshakhan
|
6
|
+
# License:: WTFPL
|
7
|
+
module Definer
|
8
|
+
def self.included(base)
|
9
|
+
super
|
10
|
+
base.class_eval do
|
11
|
+
extend ClassMethods
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
module ClassMethods
|
16
|
+
# Defines method in "pattern-matching" style
|
17
|
+
#
|
18
|
+
# ==== Examples
|
19
|
+
#
|
20
|
+
# class Test
|
21
|
+
# include FRuby::Definer
|
22
|
+
# defun(:my_method) { 'empty args' }
|
23
|
+
# defun(:my_method, Fixnum) { |n| n }
|
24
|
+
# defun(:my_method, 0) { 'zero' }
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
# c = C.new
|
28
|
+
# c.my_method # => 'empty args'
|
29
|
+
# c.my_method(42) # => 42
|
30
|
+
# c.my_method(0) # => 'zero'
|
31
|
+
#
|
32
|
+
# ==== Parameters
|
33
|
+
#
|
34
|
+
# * <tt>name</tt> - Method name. Should be a Symbol or a String
|
35
|
+
# * <tt>formal_args</tt> - Method's formal parameters. Might be a classes or a values
|
36
|
+
# * <tt>&block</tt> - Method definition
|
37
|
+
def defun(name, *formal_args, &block)
|
38
|
+
define_or_append_condition(name, lambda { |*args|
|
39
|
+
FRuby::Matcher::match(formal_args, args)
|
40
|
+
}, &block)
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
def append_condition(method_name, condition, &block)
|
45
|
+
condition_method_name = define_condition_method(method_name, &block)
|
46
|
+
old_method_name = Utils::rand_condition_method_name(method_name)
|
47
|
+
alias_method old_method_name, method_name
|
48
|
+
private old_method_name
|
49
|
+
|
50
|
+
define_method method_name do |*args|
|
51
|
+
if condition.call(*args)
|
52
|
+
method(condition_method_name).call(*args)
|
53
|
+
else
|
54
|
+
method(old_method_name).call(*args)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def define_or_append_condition(method_name, condition, &block)
|
60
|
+
unless Utils::has_instance_method?(self, method_name)
|
61
|
+
condition_method_name = define_condition_method(method_name, &block)
|
62
|
+
define_method method_name do |*args|
|
63
|
+
if condition.call(*args)
|
64
|
+
method(condition_method_name).call(*args)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
else
|
68
|
+
append_condition(method_name, condition, &block)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def define_condition_method(method_name, &block)
|
73
|
+
condition_method_name = Utils::rand_condition_method_name(method_name)
|
74
|
+
define_method(condition_method_name, &block)
|
75
|
+
private condition_method_name.to_sym
|
76
|
+
condition_method_name
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
data/lib/ext.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
class Object
|
2
|
+
def blank?
|
3
|
+
respond_to?(:empty?) ? empty? : !self
|
4
|
+
end
|
5
|
+
|
6
|
+
def present?
|
7
|
+
!blank?
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class NilClass
|
12
|
+
def blank?
|
13
|
+
true
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class FalseClass
|
18
|
+
def blank?
|
19
|
+
true
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class TrueClass
|
24
|
+
def blank?
|
25
|
+
false
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class Array
|
30
|
+
def blank?
|
31
|
+
self.compact.empty?
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class Hash
|
36
|
+
alias_method :blank?, :empty?
|
37
|
+
end
|
38
|
+
|
39
|
+
class String
|
40
|
+
def blank?
|
41
|
+
self.strip.size == 0
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
class Numeric
|
46
|
+
def blank?
|
47
|
+
false
|
48
|
+
end
|
49
|
+
end
|
data/lib/fruby.rb
ADDED
data/lib/matcher.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
module FRuby
|
2
|
+
module Matcher extend self
|
3
|
+
def match(p1, p2)
|
4
|
+
match_by_value(p1, p2) or match_by_class(p1, p2)
|
5
|
+
end
|
6
|
+
|
7
|
+
def match_by_value(p1, p2)
|
8
|
+
if p1.is_a?(Array) and p2.is_a?(Array)
|
9
|
+
match_arrays_by_value(p1, p2)
|
10
|
+
else
|
11
|
+
p1 == p2 || (p1.blank? && p2.blank?)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def match_by_class(cls, p)
|
16
|
+
if cls.is_a?(Array) and p.is_a?(Array)
|
17
|
+
match_arrays_by_class(cls, p)
|
18
|
+
else
|
19
|
+
p.present? and cls == p.class
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def match_arrays_by_value(p1, p2)
|
24
|
+
# TODO Think about it. Does [], [nil] and nil are equals to empty args?
|
25
|
+
p1 = p1.flatten.compact
|
26
|
+
p2 = p2.flatten.compact
|
27
|
+
return false if p1.size != p2.size
|
28
|
+
return true if p1.blank? and p2.blank?
|
29
|
+
Utils::multiple_all?(p1, p2) do |a1, a2|
|
30
|
+
match_by_value(a1, a2)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def match_arrays_by_class(cls, p)
|
35
|
+
return false if cls.size != p.size
|
36
|
+
Utils::multiple_all?(cls, p) do |a1, a2|
|
37
|
+
match_by_class(a1, a2)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
data/lib/utils.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
module FRuby
|
2
|
+
module Utils extend self
|
3
|
+
def rand_condition_method_name(prefix, size = 8)
|
4
|
+
alphabet = ('a'..'z').to_a + ('A'..'Z').to_a + (0..9).to_a
|
5
|
+
"__#{prefix}_condition_#{Array.new(size) { alphabet[rand(alphabet.size)] }.join}__"
|
6
|
+
end
|
7
|
+
|
8
|
+
def has_instance_method?(obj, method_name)
|
9
|
+
obj.instance_methods.include? method_name.to_s
|
10
|
+
end
|
11
|
+
|
12
|
+
def multiple_each(*args, &block)
|
13
|
+
args.first.size.times do |index|
|
14
|
+
block.call(args.map { |arg| arg[index] })
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def multiple_all?(*args, &block)
|
19
|
+
multiple_each(*args) do |*each_args|
|
20
|
+
return false unless block.call(*each_args)
|
21
|
+
end
|
22
|
+
true
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/version.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', 'lib', 'fruby')
|
2
|
+
|
3
|
+
class DefinerTest
|
4
|
+
include FRuby::Definer
|
5
|
+
|
6
|
+
defun(:test) { :empty } # empty args (no args, nil, [], [nil])
|
7
|
+
defun(:test, String) { |s| s }
|
8
|
+
defun(:test, 0) { :zero }
|
9
|
+
end
|
10
|
+
|
11
|
+
|
12
|
+
describe FRuby::Definer do
|
13
|
+
before :each do
|
14
|
+
@inst = DefinerTest.new
|
15
|
+
end
|
16
|
+
|
17
|
+
it "Should return :empty if empty args" do
|
18
|
+
@inst.test.should == :empty
|
19
|
+
@inst.test(nil).should == :empty
|
20
|
+
@inst.test([]).should == :empty
|
21
|
+
end
|
22
|
+
|
23
|
+
it "Should return passed string if string passed" do
|
24
|
+
@inst.test('string').should == 'string'
|
25
|
+
end
|
26
|
+
|
27
|
+
it "Should return :zero string if 0 passed" do
|
28
|
+
@inst.test(0).should == :zero
|
29
|
+
end
|
30
|
+
|
31
|
+
it "Condition methods should be private" do
|
32
|
+
DefinerTest.public_instance_methods.grep(/^test/).size.should == 1
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', 'lib', 'fruby')
|
2
|
+
|
3
|
+
describe FRuby::Matcher do
|
4
|
+
it "Should match equal objects" do
|
5
|
+
FRuby::Matcher::match(0, 0).should == true
|
6
|
+
FRuby::Matcher::match([0, 1, 2], [0, 1, 2]).should == true
|
7
|
+
end
|
8
|
+
|
9
|
+
it "Should match by class" do
|
10
|
+
FRuby::Matcher::match(Fixnum, 0).should == true
|
11
|
+
FRuby::Matcher::match([Fixnum, String], [0, 'a']).should == true
|
12
|
+
end
|
13
|
+
|
14
|
+
it "Should match empty objects" do # but what is empty object?
|
15
|
+
FRuby::Matcher::match([], [nil]).should == true
|
16
|
+
FRuby::Matcher::match(nil, []).should == true
|
17
|
+
FRuby::Matcher::match(nil, '').should == true
|
18
|
+
end
|
19
|
+
|
20
|
+
it "Should not match not equal objects" do
|
21
|
+
FRuby::Matcher::match(0, 1).should_not == true
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
data/spec/utils_spec.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', 'lib', 'fruby')
|
2
|
+
|
3
|
+
class UtilsTest
|
4
|
+
def method_exists; end
|
5
|
+
end
|
6
|
+
|
7
|
+
describe FRuby::Utils do
|
8
|
+
it "Should return true if class has an instance method by string and symbol" do
|
9
|
+
FRuby::Utils::has_instance_method?(UtilsTest, 'method_exists').should == true
|
10
|
+
FRuby::Utils::has_instance_method?(UtilsTest, :method_exists).should == true
|
11
|
+
end
|
12
|
+
|
13
|
+
it "Should return false if class has not an instance method by string and symbol" do
|
14
|
+
FRuby::Utils::has_instance_method?(UtilsTest, 'method_doesnt_exists').should_not == true
|
15
|
+
FRuby::Utils::has_instance_method?(UtilsTest, :method_doesnt_exists).should_not == true
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
metadata
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mshakhan-fruby
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- mshakhan
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-08-21 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: rake
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: rspec
|
27
|
+
type: :runtime
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: "0"
|
34
|
+
version:
|
35
|
+
description: Some functional programming features for ruby
|
36
|
+
email: mshakhan@gmail.com
|
37
|
+
executables: []
|
38
|
+
|
39
|
+
extensions: []
|
40
|
+
|
41
|
+
extra_rdoc_files:
|
42
|
+
- README.rdoc
|
43
|
+
- LICENSE
|
44
|
+
files:
|
45
|
+
- LICENSE
|
46
|
+
- README.rdoc
|
47
|
+
- Rakefile
|
48
|
+
- lib/definer.rb
|
49
|
+
- lib/version.rb
|
50
|
+
- lib/fruby.rb
|
51
|
+
- lib/utils.rb
|
52
|
+
- lib/matcher.rb
|
53
|
+
- lib/ext.rb
|
54
|
+
- spec/matcher_spec.rb
|
55
|
+
- spec/definer_spec.rb
|
56
|
+
- spec/utils_spec.rb
|
57
|
+
has_rdoc: true
|
58
|
+
homepage:
|
59
|
+
licenses:
|
60
|
+
post_install_message:
|
61
|
+
rdoc_options: []
|
62
|
+
|
63
|
+
require_paths:
|
64
|
+
- lib
|
65
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 1.8.4
|
70
|
+
version:
|
71
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: "0"
|
76
|
+
version:
|
77
|
+
requirements: []
|
78
|
+
|
79
|
+
rubyforge_project:
|
80
|
+
rubygems_version: 1.3.5
|
81
|
+
signing_key:
|
82
|
+
specification_version: 2
|
83
|
+
summary: Some functional programming features for ruby
|
84
|
+
test_files: []
|
85
|
+
|