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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: bdf26fc9c7904a96681b438f0ea47a2061eea493
4
- data.tar.gz: 3f5d73b121875564e705d8685c0cbad45db42cd8
3
+ metadata.gz: 0b404645ae1a53a0ff6b39a80c7628d17e2cfc10
4
+ data.tar.gz: 0eae1de8f57ed18707aa2507422ec422ac3df331
5
5
  SHA512:
6
- metadata.gz: f5b044385e4b14c01511ee53286ff51839c5a949e10b04e090dafd6dfc7702bf14f2b5b1ac5573ed8bcc58f0a1755aef5d27dcc1a4e5ee7f4f9b8a940570e770
7
- data.tar.gz: 88b3693bf215390c9b9c74524e5d6b93eaa74a4b67fa9a0809b3dfd36bb45af746ebdd09e0b53cf995125043b3b936011a514583574e3452dbe9f89b152bd75d
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: "bundle exec rspec" do
28
- require "guard/rspec/dsl"
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] || "spec/acceptance"
68
+ Dir[File.join("**/#{m[1]}.feature")][0] || 'spec/acceptance'
69
69
  end
70
70
  end
data/Rakefile CHANGED
@@ -1,2 +1 @@
1
- require "bundler/gem_tasks"
2
-
1
+ require 'bundler/gem_tasks'
@@ -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 = "beethoven"
7
+ spec.name = 'beethoven'
8
8
  spec.version = Beethoven::VERSION
9
- spec.authors = ["Matt Parsons"]
10
- spec.email = ["parsonsmatt@gmail.com"]
11
- spec.summary = %q{Make it a bit easier to compose classes in Ruby}
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 = "https://www.github.com/parsonsmatt/beethoven"
18
- spec.license = "MIT"
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 = ["lib"]
23
+ spec.require_paths = ['lib']
24
24
 
25
- spec.add_development_dependency "bundler", "~> 1.7"
26
- spec.add_development_dependency "rake", "~> 10.0"
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'
@@ -1,6 +1,26 @@
1
- require "beethoven/version"
2
- require "beethoven/composer"
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
@@ -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
@@ -1,4 +1,6 @@
1
- class Class
1
+ # Require `beethoven/object` to monkey patch Object with the class composition
2
+ # operators.
3
+ class Object
2
4
  def *(other)
3
5
  Beethoven::Composer.new(other, self)
4
6
  end
@@ -0,0 +1,10 @@
1
+ # Require `beethoven/proc` to monkey patch Proc with these composition helpers.
2
+ class Proc
3
+ def *(other)
4
+ -> x { call(other.call(x)) }
5
+ end
6
+
7
+ def |(other)
8
+ -> x { other.call(call(x)) }
9
+ end
10
+ end
@@ -1,3 +1,4 @@
1
+ # Version
1
2
  module Beethoven
2
- VERSION = "0.0.2"
3
+ VERSION = '0.1.0'
3
4
  end
@@ -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
@@ -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 :: _ -> a
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( (F * G).new(5).b ).to eq( F.new(G.new(5)).b )
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( (F * H * G).new(5).b ).to eq( F.new(H.new(G.new(5))).b )
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( (G | F).new(5).b ).to eq( F.new(G.new(5)).b )
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( (G | H | F).new(5).b ).to eq( F.new(H.new(G.new(5))).b )
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
@@ -1,6 +1,10 @@
1
- example_id | status | run_time |
2
- --------------------------------- | ------ | --------------- |
3
- ./spec/beethoven_spec.rb[1:1:1:1] | passed | 0.00009 seconds |
4
- ./spec/beethoven_spec.rb[1:1:1:2] | passed | 0.00006 seconds |
5
- ./spec/beethoven_spec.rb[1:1:2:1] | passed | 0.00006 seconds |
6
- ./spec/beethoven_spec.rb[1:1:2:2] | passed | 0.00029 seconds |
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 |
@@ -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 = "spec/examples.txt"
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.2
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-06-30 00:00:00.000000000 Z
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
@@ -1,11 +0,0 @@
1
- module Beethoven
2
- module Composable
3
- def *(other)
4
- new(other, self)
5
- end
6
-
7
- def |(other)
8
- new(self, other)
9
- end
10
- end
11
- end