mozart 0.0.1 → 0.0.2

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/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"