yzz 1.0.0

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 86214ab87ff3d7b69aa7dbc97c29abcd49b54d13
4
+ data.tar.gz: 25f05cecae9791711642b5ca7a8e57db6ffad447
5
+ SHA512:
6
+ metadata.gz: 4eb9074c30c908c2ede65fe40498f680af6780a339f5cae54b2b8d1c83dddba82f242f490b6d93abea4c5e10ca7a0131b222c13836b2ed6857f4320362be4212
7
+ data.tar.gz: 2c9138c9a46f60259f3bbc443f27c593f51d4a535eb7e77f608f971906e8a90f8a618f9da656e8a0148c22d7b6031221fd1142c61d28fb824d714885a2b24971
data/.gitignore ADDED
@@ -0,0 +1,20 @@
1
+ *~
2
+ .#*
3
+ \#*#
4
+ *.gem
5
+ *.rbc
6
+ .bundle
7
+ .config
8
+ .yardoc
9
+ Gemfile.lock
10
+ InstalledFiles
11
+ _yardoc
12
+ coverage
13
+ doc/
14
+ lib/bundler/man
15
+ pkg
16
+ rdoc
17
+ spec/reports
18
+ test/tmp
19
+ test/version_tmp
20
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in yzz.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 boris
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,30 @@
1
+ # Yzz
2
+
3
+ y_ted is a Zz structure domain model
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'yzz'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install yzz
18
+
19
+ ## Usage
20
+
21
+ Zz structures have been described by Ted Nelson. Yzz provides mixin that
22
+ imbues objects with zz structure qualities.
23
+
24
+ ## Contributing
25
+
26
+ 1. Fork it
27
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
28
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
29
+ 4. Push to the branch (`git push origin my-new-feature`)
30
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/lib/yzz/side.rb ADDED
@@ -0,0 +1,110 @@
1
+ #encoding: utf-8
2
+
3
+ # Ted Nelson calls zz objects 'cells', and defines posward and negward cell
4
+ # side in each dimension. This is represented by Side class here, which is
5
+ # a quadruple [zz object, dimension, direction, neighbor].
6
+ #
7
+ class Yzz::Side
8
+ OPPOSITE = { posward: :negward, negward: :posward }
9
+
10
+ attr_reader :zz, :dimension, :direction, :neighbor
11
+
12
+ # Standard constructor. 3 compulsory arguments (:zz, :dimension, :direction),
13
+ # 1 optional argument :neighbor.
14
+ #
15
+ def initialize( zz: ( raise ArgumentError, ":zz missing!" ),
16
+ dimension: ( raise ArgumentError, ":dimension missing!" ),
17
+ direction: ( raise ArgumentError, ":direction missing!" ),
18
+ neighbor: nil )
19
+ raise TypeError, "Wrong :zz type!" unless zz.is_a_zz?
20
+ @zz, @dimension, @direction = zz, dimension, direction.to_sym
21
+ raise TypeError, "Direction must be either :posward or :negward!" unless
22
+ [ :posward, :negward ].include? direction.to_sym
23
+ set_neighbor! neighbor
24
+ end
25
+
26
+ # Links a new neighbor, unlinking and returning the old one. Cares about
27
+ # the argument type (Zz descendant or nil required), and has concerns not
28
+ # to break the new neighbor's conflicting connectivity, if any.
29
+ #
30
+ def link new_neighbor
31
+ return unlink if new_neighbor.nil?
32
+ raise TypeError, "Zz object or nil expected!" unless new_neighbor.is_a_zz?
33
+
34
+ conflicter = opposite_side( of: new_neighbor ).neighbor # have concerns
35
+ return new_neighbor if conflicter == self # no neighbor change
36
+ raise TypeError, "Suggested new neighbor (#{new_neighbor}) already " +
37
+ "has a conflicting #{direction.opposite} link along dimension " +
38
+ "#{dimension} !" if conflicter.is_a_zz?
39
+
40
+ begin # FIXME: This should be an atomic transaction.
41
+ old_neighbor = set_neighbor! new_neighbor
42
+ opposite_side( of: new_neighbor ).set_neighbor! zz
43
+ end
44
+ return old_neighbor
45
+ end # def <<
46
+ alias << link
47
+
48
+ # Sets a new neighbor, crossing over the conflicting link, if present, with
49
+ # the old neighbor.
50
+ #
51
+ def crossover new_neighbor
52
+ return unlink if new_neighbor.nil?
53
+ raise TypeError, "Zz object or nil expected!" unless new_neighbor.is_a_zz?
54
+
55
+ conflicter = opposite_side( of: new_neighbor ).neighbor
56
+ return new_neighbor if conflicter == self # no neighbor change
57
+
58
+ # FIXME: this should be an atomic transaction.
59
+ begin
60
+ old_neighbor = set_neighbor! new_neighbor
61
+ opposite_side( of: new_neighbor ).set_neighbor! zz
62
+ same_side( of: conflicter ).set_neighbor! old_neighbor # cross over
63
+ opposite_side( of: old_neighbor ).set_neighbor! conflicter # cross over
64
+ end
65
+ return old_neighbor
66
+ end
67
+ alias * crossover
68
+
69
+ # Returns the string briefly describing the instance.
70
+ #
71
+ def to_s
72
+ "#<YTed::Zz::Side: #{direction} side of #{zz} along #{dimension}>"
73
+ end
74
+
75
+ # Given a zz object (named argument :of), returns its side along same
76
+ # dimension, in the direction same as self.
77
+ #
78
+ def same_side( of: raise( ArgumentError, "Zz object (:of) absent!" ) )
79
+ of.along( dimension ).send direction
80
+ end
81
+
82
+ # Given a zz object, returns its side along same dimension, in the direction
83
+ # opposite than self.
84
+ #
85
+ def opposite_side( of: zz )
86
+ of.along( dimension ).send ::Yzz::Side::OPPOSITE[ direction ]
87
+ end
88
+
89
+ # Unlinks the neighbor, returning it.
90
+ #
91
+ def unlink
92
+ unlink!.tap do |neighbor|
93
+ opposite_side( of: neighbor ).unlink! if neighbor.is_a_zz?
94
+ end
95
+ end
96
+
97
+ protected
98
+
99
+ # Sets neighbor carelessly, returning the old neighbor.
100
+ #
101
+ def set_neighbor! new_neighbor
102
+ neighbor.tap { @neighbor = new_neighbor }
103
+ end
104
+
105
+ # Unlinks the neighbor carelessly, returning it.
106
+ #
107
+ def unlink!
108
+ set_neighbor! nil
109
+ end
110
+ end # class YTed::Zz::Side
@@ -0,0 +1,51 @@
1
+ #encoding: utf-8
2
+
3
+ # Represents a pair of Zz::Side instances pertaining to a Zz object along a
4
+ # particular dimension.
5
+ #
6
+ class Yzz::SidePair
7
+ attr_reader :zz, :dimension, :negward, :posward
8
+ alias p posward
9
+ alias n negward
10
+
11
+ def initialize( zz: ( raise ArgumentError, ":zz missing!" ),
12
+ dimension: ( raise ArgumentError, ":dimension missing!" ),
13
+ negward_neighbor: nil,
14
+ posward_neighbor: nil )
15
+ @zz, @dimension = zz, dimension
16
+ @negward = Yzz::Side.new( zz: zz,
17
+ dimension: dimension,
18
+ direction: :negward,
19
+ neighbor: negward_neighbor )
20
+ @posward = Yzz::Side.new( zz: zz,
21
+ dimension: dimension,
22
+ direction: :posward,
23
+ neighbor: posward_neighbor )
24
+ end
25
+
26
+ # Links a neighbor posward.
27
+ #
28
+ def >> new_neighbor
29
+ new_neighbor.along( dimension ).tap { posward << new_neighbor }
30
+ end
31
+
32
+ # Crossovers a new neighbor posward.
33
+ #
34
+ def * new_neighbor
35
+ posward * new_neighbor
36
+ end
37
+
38
+ # Returns posward neighbor.
39
+ #
40
+ def P; posward.neighbor end
41
+
42
+ # Returns negward neighbor.
43
+ #
44
+ def N; negward.neighbor end
45
+
46
+ # Returns the string briefly describing the instance.
47
+ #
48
+ def to_s
49
+ "#<YTed::Zz::SidePair: #{zz} along dimension #{dimension} >"
50
+ end
51
+ end # class YTed::Zz::SidePair
@@ -0,0 +1,3 @@
1
+ module Yzz
2
+ VERSION = "1.0.0"
3
+ end
data/lib/yzz.rb ADDED
@@ -0,0 +1,54 @@
1
+ # -*- coding: utf-8 -*-
2
+ require_relative 'yzz/version'
3
+ require_relative 'yzz/side'
4
+ require_relative 'yzz/side_pair'
5
+
6
+ # Yzz implements Ted Nelson's zz (hyperorthogonal, Zig-Zag...) structure. It
7
+ # provides a mixin that imbues objects with zz properties.
8
+ #
9
+ # A zz structure consists of zz objects, which exist in multiple dimensions. Zz
10
+ # objects can be connected by directed edges -- connections. Connected objects
11
+ # are called neighbors. Each connection belongs to some dimension. A zz object
12
+ # is considered as having two <em>sides</em> in each dimension: posward side and
13
+ # negward side. A connection always points away from the posward side, and
14
+ # towards the neighbor's negward side. In each dimension, zz object can have at
15
+ # most one posward and one negward neighbor. A zz object can be connected to
16
+ # itself, forming a loop.
17
+ #
18
+ # To these basic properties, Ted Nelson adds a bunch of other terminology.
19
+ # A rank is a series of zz objects connected along one dimension. A rank viewed
20
+ # horizontally is referred to as row. A rank viewed vertically is referred to
21
+ # as column.
22
+
23
+ # Mixin defining Zz structure (aka. hyperorthogonal structure). As represented
24
+ # by YTed::Zz, a Zz structure is a collection of objects, whose connectivity is
25
+ # defined in a multidimensional space in such way, that each object, along each
26
+ # dimension, has at most one posward neighbor, and one negward neighbor. The
27
+ # relation is bijective: If B is a posward neighbor of A along some dimension,
28
+ # then A must be a negward neighbor of B along that dimension, and vice-versa.
29
+ #
30
+ module Yzz
31
+ # Adds initialization of the @zz_dimensions hash to #initialize.
32
+ #
33
+ def initialize *args
34
+ @zz_dimensions = Hash.new { |ꜧ, missing_dimension|
35
+ ꜧ[ missing_dimension ] = Yzz::SidePair
36
+ .new( zz: self, dimension: missing_dimension )
37
+ } # initialize the @zz_dimensions hash
38
+ super # and proceed as usual
39
+ end
40
+
41
+ # Returns a SidePair instance along the requested dimension.
42
+ #
43
+ def along dimension
44
+ @zz_dimensions[ dimension ]
45
+ end
46
+ alias call along
47
+ end
48
+
49
+ class Object
50
+ def is_a_zz?
51
+ is_a? ::Yzz
52
+ # class_complies? ::YTed::Zz
53
+ end
54
+ end
data/test/yzz_test.rb ADDED
@@ -0,0 +1,78 @@
1
+ #! /usr/bin/ruby
2
+ # -*- coding: utf-8 -*-
3
+
4
+ require 'minitest/spec'
5
+ require 'minitest/autorun'
6
+
7
+ # Until they alias it in the core.
8
+ require 'active_support/core_ext/string/starts_ends_with'
9
+
10
+ require 'yzz' # tested component itself
11
+
12
+ describe Yzz do
13
+ before do
14
+ @ç = Class.new do
15
+ include Yzz
16
+ attr_accessor :value
17
+ def to_s; "ZzEnabledObject<#{object_id}>" end
18
+ end
19
+ end
20
+
21
+ it "reacts to #is_a_zz?" do
22
+ assert @ç.new.is_a_zz?
23
+ # ordinary objets know the method, but return false
24
+ assert ! Object.new.is_a_zz?
25
+ end
26
+
27
+ it "provides #along instance method" do
28
+ zz = @ç.new
29
+ assert_kind_of Yzz::SidePair, zz.along( :row )
30
+ assert_equal zz.along( :row ), zz.(:row)
31
+ end
32
+
33
+ describe 'basic zz object behavior' do
34
+ before do
35
+ @a = @ç.new
36
+ end
37
+
38
+ it "provides two sides, #opposite_side also tested" do
39
+ assert_equal @a.(:row).posward, @a.(:row).n.opposite_side
40
+ assert_equal @a.(:row).negward, @a.(:row).p.opposite_side
41
+ end
42
+
43
+ it "has #to_s" do
44
+ assert @a.(:row).to_s.starts_with? "#<YTed::Zz::SidePair"
45
+ end
46
+
47
+ describe 'more advanced zz object behavior' do
48
+ it "has #same_side" do
49
+ zz = @ç.new
50
+ assert_equal zz.(:row).p, @a.(:row).p.same_side( of: zz )
51
+ assert_equal zz.(:row).n, @a.(:row).n.same_side( of: zz )
52
+ end
53
+
54
+ it "works" do
55
+ a, b, c = @ç.new, @ç.new, @ç.new
56
+ assert ( b.(:row).posward << c ).nil?
57
+ assert_equal c, b.(:row).P
58
+ assert_equal b, c.(:row).N
59
+ assert ( b.(:row).negward << a ).nil?
60
+ assert_equal a, b.(:row).N
61
+ assert_equal b, a.(:row).P
62
+ assert_equal b, c.(:row).negward.unlink
63
+ assert_equal b, a.(:row).posward.unlink
64
+ a.(:row) >> b >> c
65
+ assert_equal b, a.(:row).posward * c
66
+ assert_equal b, b.(:row) * c
67
+ assert_equal b, a.(:row).P
68
+ assert_equal c, b.(:row).P
69
+ # TODO:
70
+ # assert_equal [a, c], b.connections.map { |side| side.neighbor }
71
+ # assert_equal [a, c], b.neighbors
72
+ # assert_equal [ b.(:row).negward ], b.connections( to: a )
73
+ # assert_equal [ b.(:row).posward ], b.connections( to: c )
74
+ # TODO: Rename YTed to Zz
75
+ end
76
+ end
77
+ end
78
+ end
data/yzz.gemspec ADDED
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'yzz/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "yzz"
8
+ spec.version = Yzz::VERSION
9
+ spec.authors = ["boris"]
10
+ spec.email = ["\"boris@iis.sinica.edu.tw\""]
11
+ spec.description = %q{A zz structure domain model.}
12
+ spec.summary = %q{Yzz module is a mixin that imbues object with zz qualities.}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ end
metadata ADDED
@@ -0,0 +1,84 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: yzz
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - boris
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-04-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description: A zz structure domain model.
42
+ email:
43
+ - '"boris@iis.sinica.edu.tw"'
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - .gitignore
49
+ - Gemfile
50
+ - LICENSE.txt
51
+ - README.md
52
+ - Rakefile
53
+ - lib/yzz.rb
54
+ - lib/yzz/side.rb
55
+ - lib/yzz/side_pair.rb
56
+ - lib/yzz/version.rb
57
+ - test/yzz_test.rb
58
+ - yzz.gemspec
59
+ homepage: ''
60
+ licenses:
61
+ - MIT
62
+ metadata: {}
63
+ post_install_message:
64
+ rdoc_options: []
65
+ require_paths:
66
+ - lib
67
+ required_ruby_version: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - '>='
70
+ - !ruby/object:Gem::Version
71
+ version: '0'
72
+ required_rubygems_version: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - '>='
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ requirements: []
78
+ rubyforge_project:
79
+ rubygems_version: 2.0.0
80
+ signing_key:
81
+ specification_version: 4
82
+ summary: Yzz module is a mixin that imbues object with zz qualities.
83
+ test_files:
84
+ - test/yzz_test.rb