beethoven 0.0.2 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Guardfile +8 -8
- data/Rakefile +1 -2
- data/beethoven.gemspec +9 -9
- data/lib/beethoven.rb +23 -3
- data/lib/beethoven/composer.rb +10 -9
- data/lib/beethoven/{class.rb → object.rb} +3 -1
- data/lib/beethoven/proc.rb +10 -0
- data/lib/beethoven/version.rb +2 -1
- data/spec/beethoven/composer_spec.rb +38 -0
- data/spec/beethoven_spec.rb +20 -5
- data/spec/examples.txt +10 -6
- data/spec/spec_helper.rb +1 -1
- metadata +4 -4
- data/lib/beethoven/composable.rb +0 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0b404645ae1a53a0ff6b39a80c7628d17e2cfc10
|
4
|
+
data.tar.gz: 0eae1de8f57ed18707aa2507422ec422ac3df331
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 12ba5bc073e97d842233c4087fd5aacab2aca0fdde2b0d588542c9383b66986c17f12d0a40cfe3019377d7c21490b28fd7975039a31dcc6ff8acd37a3e40bf84
|
7
|
+
data.tar.gz: 7bc74794bd6857daf9a1e04985ad35ae11979ff3988d2567f6fb1ff0df08ced87d163b479c80edc53f1bb8f4a9924d266b29226ec60485d30914dee29a8a9fab
|
data/Guardfile
CHANGED
@@ -24,8 +24,8 @@
|
|
24
24
|
# * zeus: 'zeus rspec' (requires the server to be started separately)
|
25
25
|
# * 'just' rspec: 'rspec'
|
26
26
|
|
27
|
-
guard :rspec, cmd:
|
28
|
-
require
|
27
|
+
guard :rspec, cmd: 'bundle exec rspec' do
|
28
|
+
require 'guard/rspec/dsl'
|
29
29
|
dsl = Guard::RSpec::Dsl.new(self)
|
30
30
|
|
31
31
|
# Feel free to open issues for suggestions and improvements
|
@@ -47,9 +47,9 @@ guard :rspec, cmd: "bundle exec rspec" do
|
|
47
47
|
|
48
48
|
watch(rails.controllers) do |m|
|
49
49
|
[
|
50
|
-
rspec.spec.("routing/#{m[1]}_routing"),
|
51
|
-
rspec.spec.("controllers/#{m[1]}_controller"),
|
52
|
-
rspec.spec.("acceptance/#{m[1]}")
|
50
|
+
rspec.spec.call("routing/#{m[1]}_routing"),
|
51
|
+
rspec.spec.call("controllers/#{m[1]}_controller"),
|
52
|
+
rspec.spec.call("acceptance/#{m[1]}")
|
53
53
|
]
|
54
54
|
end
|
55
55
|
|
@@ -59,12 +59,12 @@ guard :rspec, cmd: "bundle exec rspec" do
|
|
59
59
|
watch(rails.app_controller) { "#{rspec.spec_dir}/controllers" }
|
60
60
|
|
61
61
|
# Capybara features specs
|
62
|
-
watch(rails.view_dirs) { |m| rspec.spec.("features/#{m[1]}") }
|
63
|
-
watch(rails.layouts) { |m| rspec.spec.("features/#{m[1]}") }
|
62
|
+
watch(rails.view_dirs) { |m| rspec.spec.call("features/#{m[1]}") }
|
63
|
+
watch(rails.layouts) { |m| rspec.spec.call("features/#{m[1]}") }
|
64
64
|
|
65
65
|
# Turnip features and steps
|
66
66
|
watch(%r{^spec/acceptance/(.+)\.feature$})
|
67
67
|
watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) do |m|
|
68
|
-
Dir[File.join("**/#{m[1]}.feature")][0] ||
|
68
|
+
Dir[File.join("**/#{m[1]}.feature")][0] || 'spec/acceptance'
|
69
69
|
end
|
70
70
|
end
|
data/Rakefile
CHANGED
@@ -1,2 +1 @@
|
|
1
|
-
require
|
2
|
-
|
1
|
+
require 'bundler/gem_tasks'
|
data/beethoven.gemspec
CHANGED
@@ -4,26 +4,26 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
4
|
require 'beethoven/version'
|
5
5
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
|
-
spec.name =
|
7
|
+
spec.name = 'beethoven'
|
8
8
|
spec.version = Beethoven::VERSION
|
9
|
-
spec.authors = [
|
10
|
-
spec.email = [
|
11
|
-
spec.summary =
|
9
|
+
spec.authors = ['Matt Parsons']
|
10
|
+
spec.email = ['parsonsmatt@gmail.com']
|
11
|
+
spec.summary = 'Make it a bit easier to compose classes in Ruby'
|
12
12
|
spec.description = <<-EOF
|
13
13
|
Functional programming is gaining more and more mindshare in software lately. One of the main benefits of programming in the functional style is function composition. Function composition allows you to break your program into small manageable chunks that can be put together in new and interesting ways.
|
14
14
|
|
15
15
|
Object Oriented Programming is supposed to be composable, but the composition is lacking compared to FP. Perhaps Ruby's flexibility can get us part of the way there?
|
16
16
|
EOF
|
17
|
-
spec.homepage =
|
18
|
-
spec.license =
|
17
|
+
spec.homepage = 'https://www.github.com/parsonsmatt/beethoven'
|
18
|
+
spec.license = 'MIT'
|
19
19
|
|
20
20
|
spec.files = `git ls-files -z`.split("\x0")
|
21
21
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
22
22
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
23
|
-
spec.require_paths = [
|
23
|
+
spec.require_paths = ['lib']
|
24
24
|
|
25
|
-
spec.add_development_dependency
|
26
|
-
spec.add_development_dependency
|
25
|
+
spec.add_development_dependency 'bundler', '~> 1.7'
|
26
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
27
27
|
spec.add_development_dependency 'guard', '~> 2.12'
|
28
28
|
spec.add_development_dependency 'rspec', '~> 3.3'
|
29
29
|
spec.add_development_dependency 'guard-rspec', '~> 4.5'
|
data/lib/beethoven.rb
CHANGED
@@ -1,6 +1,26 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require "beethoven/class"
|
1
|
+
require 'beethoven/version'
|
2
|
+
require 'beethoven/composer'
|
4
3
|
|
4
|
+
# The Beethoven module can be used in order to provide convenience operators for
|
5
|
+
# the Beethoven::Composer class.
|
5
6
|
module Beethoven
|
7
|
+
refine Object do
|
8
|
+
def *(other)
|
9
|
+
Beethoven::Composer.new(other, self)
|
10
|
+
end
|
11
|
+
|
12
|
+
def |(other)
|
13
|
+
Beethoven::Composer.new(self, other)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
refine Proc do
|
18
|
+
def *(other)
|
19
|
+
-> x { call(other.call(x)) }
|
20
|
+
end
|
21
|
+
|
22
|
+
def |(other)
|
23
|
+
-> x { other.call(call(x)) }
|
24
|
+
end
|
25
|
+
end
|
6
26
|
end
|
data/lib/beethoven/composer.rb
CHANGED
@@ -1,19 +1,20 @@
|
|
1
1
|
module Beethoven
|
2
|
+
# Composer composes class instantiation. The first call to new refers to the
|
3
|
+
# `initialize` method, and creates an object which duck-types Class.new. When
|
4
|
+
# `Composer#new` is called, the classes are instantiated in order, where the
|
5
|
+
# argument passed to the first class is the argument passed to Composer#new,
|
6
|
+
# and each subsequent class receives the instantiated object as its argument.
|
2
7
|
class Composer
|
3
8
|
def initialize(*fs)
|
4
|
-
@fs = fs
|
9
|
+
@fs = if fs.size == 1 && fs[0].is_a?(Array)
|
10
|
+
fs[0]
|
11
|
+
else
|
12
|
+
fs
|
13
|
+
end
|
5
14
|
end
|
6
15
|
|
7
16
|
def new(x)
|
8
17
|
@fs.reduce(x) { |a, e| e.new(a) }
|
9
18
|
end
|
10
|
-
|
11
|
-
def *(other)
|
12
|
-
self.class.new(other, self)
|
13
|
-
end
|
14
|
-
|
15
|
-
def |(other)
|
16
|
-
self.class.new(self, other)
|
17
|
-
end
|
18
19
|
end
|
19
20
|
end
|
data/lib/beethoven/version.rb
CHANGED
@@ -1,4 +1,42 @@
|
|
1
1
|
require 'beethoven/composer'
|
2
2
|
|
3
3
|
RSpec.describe Beethoven::Composer do
|
4
|
+
# F :: () -> a
|
5
|
+
class F
|
6
|
+
attr_reader :a
|
7
|
+
|
8
|
+
def initialize(o)
|
9
|
+
@a = o
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
# G :: a -> b
|
14
|
+
class G
|
15
|
+
attr_reader :b
|
16
|
+
|
17
|
+
def initialize(o)
|
18
|
+
@b = o.a
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# H :: b -> c
|
23
|
+
class H
|
24
|
+
attr_reader :c
|
25
|
+
|
26
|
+
def initialize(o)
|
27
|
+
@c = o.b
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
subject { Beethoven::Composer }
|
32
|
+
|
33
|
+
describe 'composes classes' do
|
34
|
+
it 'transfers the value' do
|
35
|
+
expect(subject.new(F, G, H).new(5).c).to eq(5)
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'can take array of classes' do
|
39
|
+
expect(subject.new([F, G, H]).new(5).c).to eq(5)
|
40
|
+
end
|
41
|
+
end
|
4
42
|
end
|
data/spec/beethoven_spec.rb
CHANGED
@@ -2,6 +2,8 @@ require 'spec_helper'
|
|
2
2
|
require 'beethoven'
|
3
3
|
|
4
4
|
RSpec.describe Beethoven do
|
5
|
+
using Beethoven
|
6
|
+
|
5
7
|
describe 'class composition' do
|
6
8
|
# F's initialize takes an object with interface a and returns an object with
|
7
9
|
# interface b. Can be thought of as:
|
@@ -15,7 +17,7 @@ RSpec.describe Beethoven do
|
|
15
17
|
|
16
18
|
# G's initialize takes an object and gives it the interface a. Can be
|
17
19
|
# expressed like:
|
18
|
-
# G ::
|
20
|
+
# G :: () -> a
|
19
21
|
class G
|
20
22
|
attr_reader :a
|
21
23
|
|
@@ -36,22 +38,35 @@ RSpec.describe Beethoven do
|
|
36
38
|
|
37
39
|
describe 'with *' do
|
38
40
|
it 'composes classes like math functions' do
|
39
|
-
expect(
|
41
|
+
expect((F * G).new(5).b).to eq(F.new(G.new(5)).b)
|
40
42
|
end
|
41
43
|
|
42
44
|
it 'can be chained' do
|
43
|
-
expect(
|
45
|
+
expect((F * H * G).new(5).b).to eq(F.new(H.new(G.new(5))).b)
|
44
46
|
end
|
45
47
|
end
|
46
48
|
|
47
49
|
describe 'with |' do
|
48
50
|
it 'composes classes like pipes' do
|
49
|
-
expect(
|
51
|
+
expect((G | F).new(5).b).to eq(F.new(G.new(5)).b)
|
50
52
|
end
|
51
53
|
|
52
54
|
it 'can be chained' do
|
53
|
-
expect(
|
55
|
+
expect((G | H | F).new(5).b).to eq(F.new(H.new(G.new(5))).b)
|
54
56
|
end
|
55
57
|
end
|
56
58
|
end
|
59
|
+
|
60
|
+
describe 'proc composition' do
|
61
|
+
let(:f) { -> x { x + 1 } }
|
62
|
+
let(:g) { -> x { x * 2 } }
|
63
|
+
|
64
|
+
it 'allows lambdas to be composed' do
|
65
|
+
expect((f * g)[3]).to eq(7)
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'supports pipeline' do
|
69
|
+
expect((f | g)[3]).to eq(8)
|
70
|
+
end
|
71
|
+
end
|
57
72
|
end
|
data/spec/examples.txt
CHANGED
@@ -1,6 +1,10 @@
|
|
1
|
-
example_id
|
2
|
-
|
3
|
-
./spec/
|
4
|
-
./spec/
|
5
|
-
./spec/beethoven_spec.rb[1:1:
|
6
|
-
./spec/beethoven_spec.rb[1:1:
|
1
|
+
example_id | status | run_time |
|
2
|
+
---------------------------------------- | ------ | --------------- |
|
3
|
+
./spec/beethoven/composer_spec.rb[1:1:1] | passed | 0.0001 seconds |
|
4
|
+
./spec/beethoven/composer_spec.rb[1:1:2] | passed | 0.00055 seconds |
|
5
|
+
./spec/beethoven_spec.rb[1:1:1:1] | passed | 0.00008 seconds |
|
6
|
+
./spec/beethoven_spec.rb[1:1:1:2] | passed | 0.00009 seconds |
|
7
|
+
./spec/beethoven_spec.rb[1:1:2:1] | passed | 0.00007 seconds |
|
8
|
+
./spec/beethoven_spec.rb[1:1:2:2] | passed | 0.0001 seconds |
|
9
|
+
./spec/beethoven_spec.rb[1:2:1] | passed | 0.00039 seconds |
|
10
|
+
./spec/beethoven_spec.rb[1:2:2] | passed | 0.00014 seconds |
|
data/spec/spec_helper.rb
CHANGED
@@ -50,7 +50,7 @@ RSpec.configure do |config|
|
|
50
50
|
# Allows RSpec to persist some state between runs in order to support
|
51
51
|
# the `--only-failures` and `--next-failure` CLI options. We recommend
|
52
52
|
# you configure your source control system to ignore this file.
|
53
|
-
config.example_status_persistence_file_path =
|
53
|
+
config.example_status_persistence_file_path = 'spec/examples.txt'
|
54
54
|
|
55
55
|
# Limits the available syntax to the non-monkey patched syntax that is
|
56
56
|
# recommended. For more details, see:
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: beethoven
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matt Parsons
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-08-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -115,9 +115,9 @@ files:
|
|
115
115
|
- Rakefile
|
116
116
|
- beethoven.gemspec
|
117
117
|
- lib/beethoven.rb
|
118
|
-
- lib/beethoven/class.rb
|
119
|
-
- lib/beethoven/composable.rb
|
120
118
|
- lib/beethoven/composer.rb
|
119
|
+
- lib/beethoven/object.rb
|
120
|
+
- lib/beethoven/proc.rb
|
121
121
|
- lib/beethoven/version.rb
|
122
122
|
- spec/beethoven/composer_spec.rb
|
123
123
|
- spec/beethoven_spec.rb
|