mozart 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/example.rb ADDED
@@ -0,0 +1,48 @@
1
+ require_relative "lib/mozart"
2
+
3
+ class BasicCalculator
4
+ include Mozart::Environment
5
+
6
+ def initialize
7
+ array = []
8
+
9
+ features << X(:push, array)
10
+
11
+ _(:data, X(:reduce, :clear, array))
12
+ end
13
+
14
+ def sum
15
+ _(:data).reduce(0, :+).tap { _(:data).clear }
16
+ end
17
+ end
18
+
19
+ stack = BasicCalculator.new
20
+ stack.push(10)
21
+ stack.push(20)
22
+ stack.push(30)
23
+
24
+ p stack.sum
25
+
26
+ stack.push(20)
27
+ stack.push(50)
28
+
29
+ p stack.sum
30
+
31
+
32
+ class Person
33
+ include Mozart::Composable
34
+
35
+ def initialize(params)
36
+ features << V(:name, :email, params)
37
+ end
38
+
39
+ def to_s
40
+ "#{name} <#{email}>"
41
+ end
42
+ end
43
+
44
+
45
+ person = Person.new(:name => "Gregory Brown",
46
+ :email => "gregory.t.brown@gmail.com")
47
+
48
+ puts person
data/lib/mozart.rb CHANGED
@@ -1,5 +1,7 @@
1
- require "mozart/version"
2
-
3
- module Mozart
4
- # Your code goes here...
5
- end
1
+ require_relative "mozart/version"
2
+ require_relative "mozart/composite"
3
+ require_relative "mozart/composable"
4
+ require_relative "mozart/value"
5
+ require_relative "mozart/context"
6
+ require_relative "mozart/single_assignment"
7
+ require_relative "mozart/environment"
@@ -0,0 +1,29 @@
1
+ require_relative "composite"
2
+
3
+ module Mozart
4
+ module Composable
5
+ private
6
+
7
+ def X(*method_names, target)
8
+ Mozart.context(*method_names).new(target)
9
+ end
10
+
11
+ def V(*field_names, params)
12
+ Mozart.value(*field_names).new(params)
13
+ end
14
+
15
+ def features
16
+ @features ||= Mozart::Composite.new
17
+ end
18
+
19
+ def respond_to_missing?(m, *a, &b)
20
+ features.receives?(m)
21
+ end
22
+
23
+ def method_missing(m, *a, &b)
24
+ return super unless features.receives?(m)
25
+
26
+ features.dispatch(m, *a, &b)
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,39 @@
1
+ module Mozart
2
+ AmbiguousMessageError = Class.new(StandardError)
3
+
4
+ class Composite
5
+ def initialize
6
+ self.parts = []
7
+ end
8
+
9
+ def <<(part)
10
+ parts << part
11
+ end
12
+
13
+ def receives?(message)
14
+ !!recipient(message)
15
+ end
16
+
17
+ def dispatch(message, *a, &b)
18
+ target = recipient(message)
19
+
20
+ unless target
21
+ raise NotImplementedError, "No recipient implements #{message}"
22
+ end
23
+
24
+ target.public_send(message, *a, &b)
25
+ end
26
+
27
+ private
28
+
29
+ def recipient(message)
30
+ target, *rest = parts.select { |part| part.respond_to?(message) }
31
+
32
+ raise AmbiguousMessageError unless rest.empty?
33
+
34
+ target
35
+ end
36
+
37
+ attr_accessor :parts
38
+ end
39
+ end
@@ -0,0 +1,23 @@
1
+ module Mozart
2
+ def self.context(*accepted_methods, &class_definition)
3
+ Class.new do
4
+ def initialize(target)
5
+ self.target = target
6
+ end
7
+
8
+ define_method :respond_to_missing? do |m, *a|
9
+ accepted_methods.include?(m)
10
+ end
11
+
12
+ def method_missing(m, *a, &b)
13
+ raise NotImplementedError unless respond_to_missing?(m)
14
+
15
+ target.public_send(m, *a, &b)
16
+ end
17
+
18
+ private
19
+
20
+ attr_accessor :target
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,6 @@
1
+ module Mozart
2
+ module Environment
3
+ include SingleAssignment
4
+ include Composable
5
+ end
6
+ end
@@ -0,0 +1,20 @@
1
+ module Mozart
2
+ module SingleAssignment
3
+ def _(*args)
4
+ @__internals__ ||= {}
5
+
6
+ case args.count
7
+ when 1
8
+ @__internals__[args.first]
9
+ when 2
10
+ if @__internals__.key?(args.first)
11
+ raise "Single assignment only!"
12
+ else
13
+ @__internals__[args.first] = args.last
14
+ end
15
+ else
16
+ raise ArgumentError
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,33 @@
1
+ module Mozart
2
+ def self.value(*field_names, &block)
3
+ Class.new do
4
+ define_method(:initialize) do |params={}|
5
+ raise ArgumentError unless params.keys == field_names
6
+
7
+ self.__data__ = {}
8
+
9
+ params.each { |k,v| __data__[k] = v }
10
+
11
+ __data__
12
+ end
13
+
14
+ def ==(other)
15
+ self.class == other.class && __data__ == other.__data__
16
+ end
17
+
18
+ alias_method :eql?, :==
19
+
20
+ def hash
21
+ __data__.hash
22
+ end
23
+
24
+ field_names.each do |name|
25
+ define_method(name) { __data__[name] }
26
+ end
27
+
28
+ protected
29
+
30
+ attr_accessor :__data__
31
+ end
32
+ end
33
+ end
@@ -1,3 +1,3 @@
1
1
  module Mozart
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
data/mozart.gemspec CHANGED
@@ -4,8 +4,8 @@ require File.expand_path('../lib/mozart/version', __FILE__)
4
4
  Gem::Specification.new do |gem|
5
5
  gem.authors = ["Gregory Brown"]
6
6
  gem.email = ["gregory.t.brown@gmail.com"]
7
- gem.description = %q{An experimental tool implementing mixins via composition}
8
- gem.summary = %q{An experimental tool implementing mixins via composition}
7
+ gem.description = %q{Tools for making object composition easier}
8
+ gem.summary = %q{Tools for making object composition easier}
9
9
  gem.homepage = "https://github.com/elm-city-craftworks/mozart"
10
10
 
11
11
  gem.files = `git ls-files`.split($\)
@@ -0,0 +1,47 @@
1
+ require "minitest/autorun"
2
+
3
+ require_relative "../lib/mozart/composite"
4
+ require_relative "../lib/mozart/value"
5
+
6
+ describe Mozart::Composite do
7
+ let(:composite) { Mozart::Composite.new }
8
+
9
+ let(:parts) do
10
+ [ Mozart.value(:foo).new(:foo => :returned_by_foo),
11
+ Mozart.value(:bar).new(:bar => :returned_by_bar) ]
12
+ end
13
+
14
+ it "knows what messages it can receive" do
15
+ parts.each { |part| composite << part }
16
+
17
+ assert composite.receives?(:foo), "Expected composite to receive foo"
18
+ assert composite.receives?(:bar), "Expected composite to receive bar"
19
+ refute composite.receives?(:baz), "Did not expect composite to receive baz"
20
+ end
21
+
22
+ it "dispatches messages to its parts" do
23
+ parts.each { |part| composite << part }
24
+
25
+ composite.dispatch(:foo).must_equal(:returned_by_foo)
26
+ composite.dispatch(:bar).must_equal(:returned_by_bar)
27
+ end
28
+
29
+ it "raises an exception for unhandled messages" do
30
+ assert_raises(NotImplementedError) do
31
+ composite.dispatch(:baz)
32
+ end
33
+ end
34
+
35
+ it "raises an exception for messages with multiple recipients" do
36
+ composite << Mozart.value(:foo).new(:foo => true)
37
+ composite << Mozart.value(:foo).new(:foo => false)
38
+
39
+ assert_raises(Mozart::AmbiguousMessageError) do
40
+ composite.dispatch(:foo)
41
+ end
42
+
43
+ assert_raises(Mozart::AmbiguousMessageError) do
44
+ composite.receives?(:foo)
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,20 @@
1
+ require "minitest/autorun"
2
+ require_relative "../lib/mozart/context"
3
+
4
+ describe "Mozart.context" do
5
+
6
+ it "must restrict method calls" do
7
+ stack_builder = Mozart.context(:push, :pop)
8
+ stack = stack_builder.new([])
9
+
10
+ stack.push(2)
11
+ stack.push(3)
12
+
13
+ stack.pop.must_equal 3
14
+ stack.pop.must_equal 2
15
+
16
+ assert_raises(NotImplementedError) do
17
+ stack[0]
18
+ end
19
+ end
20
+ end
data/test/suite.rb ADDED
@@ -0,0 +1,2 @@
1
+ require_relative "composite_test"
2
+ require_relative "value_test"
@@ -0,0 +1,39 @@
1
+ require "minitest/autorun"
2
+ require_relative "../lib/mozart/value"
3
+
4
+ describe "Mozart.value" do
5
+ it "produces Struct-like classes" do
6
+ pos_builder = Mozart.value(:x, :y)
7
+
8
+ pos1 = pos_builder.new(:x => 10, :y => 20)
9
+ pos1.x.must_equal(10)
10
+ pos1.y.must_equal(20)
11
+ end
12
+
13
+ it "implements equality" do
14
+ pos_builder = Mozart.value(:x, :y)
15
+
16
+ pos1 = pos_builder.new(:x => 10, :y => 15)
17
+ pos2 = pos_builder.new(:x => pos1.x, :y => pos1.y)
18
+ pos3 = pos_builder.new(:x => pos1.x + 1, :y => pos1.y)
19
+
20
+ pos1.must_equal(pos2)
21
+
22
+ pos1.wont_equal(pos3)
23
+ pos2.wont_equal(pos3)
24
+ end
25
+
26
+ it "implements hash" do
27
+ pos_builder = Mozart.value(:x, :y)
28
+
29
+ pos1 = pos_builder.new(:x => 10, :y => 15)
30
+ pos2 = pos_builder.new(:x => pos1.x, :y => pos1.y)
31
+ pos3 = pos_builder.new(:x => pos1.x + 1, :y => pos1.y)
32
+
33
+ data = { pos1 => true }
34
+
35
+ assert data[pos1]
36
+ assert data[pos2]
37
+ refute data[pos3]
38
+ end
39
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mozart
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,9 +9,9 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-07-05 00:00:00.000000000 Z
12
+ date: 2012-07-06 00:00:00.000000000 Z
13
13
  dependencies: []
14
- description: An experimental tool implementing mixins via composition
14
+ description: Tools for making object composition easier
15
15
  email:
16
16
  - gregory.t.brown@gmail.com
17
17
  executables: []
@@ -22,10 +22,20 @@ files:
22
22
  - Gemfile
23
23
  - LICENSE
24
24
  - README.md
25
- - Rakefile
25
+ - example.rb
26
26
  - lib/mozart.rb
27
+ - lib/mozart/composable.rb
28
+ - lib/mozart/composite.rb
29
+ - lib/mozart/context.rb
30
+ - lib/mozart/environment.rb
31
+ - lib/mozart/single_assignment.rb
32
+ - lib/mozart/value.rb
27
33
  - lib/mozart/version.rb
28
34
  - mozart.gemspec
35
+ - test/composite_test.rb
36
+ - test/context_test.rb
37
+ - test/suite.rb
38
+ - test/value_test.rb
29
39
  homepage: https://github.com/elm-city-craftworks/mozart
30
40
  licenses: []
31
41
  post_install_message:
@@ -49,5 +59,9 @@ rubyforge_project:
49
59
  rubygems_version: 1.8.24
50
60
  signing_key:
51
61
  specification_version: 3
52
- summary: An experimental tool implementing mixins via composition
53
- test_files: []
62
+ summary: Tools for making object composition easier
63
+ test_files:
64
+ - test/composite_test.rb
65
+ - test/context_test.rb
66
+ - test/suite.rb
67
+ - test/value_test.rb
data/Rakefile DELETED
@@ -1,2 +0,0 @@
1
- #!/usr/bin/env rake
2
- require "bundler/gem_tasks"