y_support 2.0.11 → 2.0.13

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: bc98d20ebfbbb5861b8ec817347e5d73a71e4518
4
- data.tar.gz: 1226960b1bc6b9e15a0c1626816266378fa489de
3
+ metadata.gz: d466ad84213f9677a44efdb9d426ae1a24c11688
4
+ data.tar.gz: 398a4cbaf4e656dcabd8d87055c5069bcef90275
5
5
  SHA512:
6
- metadata.gz: 070a617610e329aa6a5ca5ac868ab3f875864485626e4db658da11bade01e37fb64f56a9323d41dc2a98acbac23e440bf3abb15a2d25fd4a0803d9c639713cda
7
- data.tar.gz: 68f0b3f5b32caa61bc0fe44391d1946ca25dc6e394dbdc23fdcfe3e63d5b7d8489f9906a691d3b71107b4993a35192f65f452ee33a1a7628d34d4414718b65c7
6
+ metadata.gz: ace20c52c5676019cdf9b1633911a134903db65a3bca738e478172ea3d826e4485ab957b4f541fd21e2e0ab924b3ae9336ce5c6922be5ac7252edad5ed893bdf
7
+ data.tar.gz: cf26fc0b943d41e39af89f4dbb8025e258733fea47497a6f5be8b030125ccd0efbeb24212dd852833949111fb312f0acb1ea11e7f1e8f19570d9c7a361e6ca0b
data/lib/y_support/all.rb CHANGED
@@ -8,6 +8,7 @@ require 'y_support/respond_to'
8
8
  require 'y_support/null_object'
9
9
  require 'y_support/inert_recorder'
10
10
  require 'y_support/local_object'
11
+ require 'y_support/try'
11
12
  require 'y_support/abstract_algebra'
12
13
  require 'y_support/kde'
13
14
  require 'y_support/x'
@@ -0,0 +1,132 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ require 'y_support'
4
+ require 'y_support/core_ext/array/misc'
5
+
6
+ # Provides +Try+ class, and +Object#try+ method that constructs and calls a
7
+ # +Consciously::Try+ instance. This +#try+ method has nothing to do with the
8
+ # error-swallowing +#try+ method frequently seen elsewhere. On the contrary,
9
+ # our +#try+ method _facilitates_ raising and ultimately, correcting errors
10
+ # by providing well-formed error messages.
11
+ #
12
+ # Constructing error messages is labor-intensive. +Consciously::Try+ allows one
13
+ # to construct verbose error messages with +#note+ statements inside the block,
14
+ # that act as comments at the same time.
15
+ #
16
+ # "FooBar".try "to do something" do
17
+ # note has: "#{size} letters", is: "a #{self.class} instance"
18
+ # unless include? "Quux"
19
+ # note "Quux", is: "not a part of it"
20
+ # try "to append Quux to it" do
21
+ # self << "Quux"
22
+ # fail "EPIC FAIL"
23
+ # end
24
+ # end
25
+ # end
26
+ #
27
+ # Should produce an automatic error message like this: "When trying to do
28
+ # something, FooBar having 6 letters, being a String instance, Quux being
29
+ # not a part of it, RuntimeError occurred: When trying to append Quux to it,
30
+ # RuntimeError occurred: EPIC FAIL"
31
+ #
32
+ module Consciously
33
+ class Try < BasicObject
34
+ DECORATE = -> str, prefix: '', postfix: '' {
35
+ str.to_s.tap { |ς| return ς.empty? ? '' : prefix + ς + postfix }
36
+ }
37
+ TRANSITIVE = ::Hash.new do |ꜧ, key| "#{key}ing %s" end
38
+ .update( is: "being %s",
39
+ has: "having %s" )
40
+ STATE = ::Hash.new do |ꜧ, key| "#{key} %s" end
41
+ .update( is: "%s",
42
+ has: "has %s" )
43
+
44
+ attr_reader :__obj__, :__txt__, :__bl__, :__facts__
45
+
46
+ # This
47
+ def initialize( object: nil, text: nil, &block )
48
+ @__obj__, @__txt__, @__bl__ = object, text, block
49
+ @__facts__ = ::Hash.new do |hsh, key| hsh[key] = [ {} ] end
50
+ end
51
+
52
+ # The syntax of this method, available inside the #try block, is:
53
+ #
54
+ # note "Concatenation of Foo and Bar", is: "FooBar", has: "6 letters"
55
+ #
56
+ def note *subjects, **statements, &block
57
+ return Array( subjects ).each { |s| __facts__[s].push_ordered s } if
58
+ statements.empty?
59
+ subjects << __obj__ if subjects.empty?
60
+ Array( subjects ).each { |subj|
61
+ statements.each { |verb, obj| __facts__[subj].push_named verb => obj }
62
+ }
63
+ return statements.first[1]
64
+ end
65
+
66
+ # Invokes the Try object's block.
67
+ #
68
+ def __invoke__ *args
69
+ begin
70
+ instance_exec *args, &__bl__
71
+ rescue ::StandardError => err
72
+ txt1 = "When trying #{__txt__}"
73
+ thing, statements = __describe__
74
+ txt2 = DECORATE.( thing, prefix: ' ' )
75
+ txt3 = DECORATE.( statements.map { |verb, object|
76
+ STATE[verb] % object
77
+ }.join( ', ' ),
78
+ prefix: ' (', postfix: ')' )
79
+ txt4 = DECORATE.( __circumstances__, prefix: ', ' )
80
+ txt5 = DECORATE.( "#{err.class} occurred: #{err}", prefix: ', ' )
81
+ raise err, txt1 + txt2 + txt3 + txt4 + txt5
82
+ end
83
+ end
84
+
85
+ def try *args, &block
86
+ __obj__.try *args, &block
87
+ end
88
+
89
+ def method_missing sym, *args
90
+ __obj__.send sym, *args
91
+ end
92
+
93
+ def __describe__ obj=__obj__
94
+ facts = __facts__[obj].dup
95
+ statements = if facts.last.is_a? ::Hash then facts.pop else {} end
96
+ fs = facts.join ', '
97
+ if statements.empty? then
98
+ return fs, statements
99
+ else
100
+ return facts.empty? ? obj.to_s : fs, statements
101
+ end
102
+ end
103
+
104
+ def __circumstances__
105
+ __facts__.reject { |subj, _| subj == __obj__ }.map { |subj, _|
106
+ thing, statements = __describe__( subj )
107
+ thing + DECORATE.( statements.map { |v, o|
108
+ TRANSITIVE[v] % o
109
+ }.join( ', ' ),
110
+ prefix: ' ' )
111
+ }.join( ', ' )
112
+ end
113
+ end
114
+ end
115
+
116
+
117
+ class Object
118
+ # Try method takes two textual arguments and one block. The first (optional)
119
+ # argument is a natural language description of the method's receiver (with
120
+ # #to_s of the receiver used by default). The second argument is a natural
121
+ # language description of the supplied block's _contract_ -- in other words,
122
+ # what the supplied block tries to do. Finally, the block contains the code
123
+ # to perform the described risky action. Inside the block, +#note+ method is
124
+ # available, which builds up the context information for a good error message,
125
+ # should the risky action raise one.
126
+ #
127
+ def try receiver_NL_description=self, attempt_NL_description, &block
128
+ Consciously::Try.new( object: receiver_NL_description,
129
+ text: attempt_NL_description,
130
+ &block ).__invoke__
131
+ end
132
+ end
@@ -1,3 +1,3 @@
1
1
  module YSupport
2
- VERSION = "2.0.11"
2
+ VERSION = "2.0.13"
3
3
  end
data/test/try_test.rb ADDED
@@ -0,0 +1,103 @@
1
+ #! /usr/bin/ruby
2
+
3
+ require 'minitest/spec'
4
+ require 'minitest/autorun'
5
+ # require 'y_support/try' # tested component itself
6
+ require './../lib/y_support/try'
7
+
8
+ describe Consciously do
9
+ before do
10
+ @try = Consciously::Try.new object: "Dummy", text: "to fire" do
11
+ note is: "dummy"
12
+ note has: "no care in the world"
13
+ n = note "its number", is: 42
14
+ raise TypeError, 'foo'
15
+ end
16
+ end
17
+
18
+ it "should have basic functionality" do
19
+ assert_equal "to fire", @try.__txt__
20
+ assert_equal "Dummy", @try.__obj__
21
+ assert_equal 0, @try.__facts__.size # haven't tried anything yet
22
+ @try.__facts__["something"]
23
+ assert_equal 1, @try.__facts__.size
24
+ @try.note is: 'dummy'
25
+ @try.note has: 'no care in the world'
26
+ assert_equal 2, @try.__facts__.size
27
+ assert_equal ["something", "Dummy"], @try.__facts__.keys
28
+ assert_equal( [ { is: "dummy", has: "no care in the world" } ],
29
+ @try.__facts__["Dummy"] )
30
+ assert_equal " hello!", Consciously::Try::DECORATE.( :hello, prefix: ' ', postfix: '!' )
31
+ assert_equal( ['Dummy', {is: 'dummy', has: 'no care in the world'}],
32
+ @try.__describe__( "Dummy" ) )
33
+ end
34
+
35
+ describe 'case 1' do
36
+ it "should work" do
37
+ begin
38
+ @try.__invoke__
39
+ rescue TypeError => err
40
+ expected_msg = "When trying to fire Dummy (dummy, has no care in " +
41
+ "the world), its number being 42, TypeError occurred: foo"
42
+ assert_equal expected_msg, err.message
43
+ else
44
+ flunk "Expected TypeError error not raised!"
45
+ end
46
+ end
47
+ end
48
+
49
+ describe 'case 2' do
50
+ it "should work" do
51
+ begin
52
+ try "to call constant Nonexistant" do Nonexistant end
53
+ rescue NameError => err
54
+ expected_msg = 'When trying to call constant Nonexistant, ' +
55
+ 'NameError occurred: uninitialized constant Nonexistant'
56
+ assert_equal( expected_msg, err.message )
57
+ else
58
+ flunk "Expected NameError error not raised!"
59
+ end
60
+ end
61
+ end
62
+
63
+ describe 'case 3' do
64
+ it "should work" do
65
+ o = Object.new
66
+ class << o
67
+ def to_s; "THIS OBJECT" end
68
+ def hello!; "hello hello" end
69
+ end
70
+ # Object's methods must be callable
71
+ o.try "to say hello" do hello! end.must_equal "hello hello"
72
+ begin
73
+ o.try "to call a weird method" do goodbye! end
74
+ rescue NoMethodError => err
75
+ err.message.must_include "When trying to call a weird method, " +
76
+ "NoMethodError occurred: undefined method"
77
+ err.message.must_include "goodbye!"
78
+ end
79
+ end
80
+ end
81
+
82
+ describe 'case 4' do
83
+ it "should work" do
84
+ begin
85
+ "FooBar".try "to do something" do
86
+ note has: "#{size} letters", is: "a #{self.class} instance"
87
+ unless include? "Quux"
88
+ note "Quux", is: "not a part of it"
89
+ try "to append Quux to it" do
90
+ self << "Quux"
91
+ fail "EPIC FAIL"
92
+ end
93
+ end
94
+ end
95
+ rescue => err
96
+ err.message.must_equal 'When trying to do something, FooBar having ' +
97
+ '6 letters, being a String instance, Quux being not a part of it, ' +
98
+ 'RuntimeError occurred: When trying to append Quux to it, ' +
99
+ 'RuntimeError occurred: EPIC FAIL'
100
+ end
101
+ end
102
+ end
103
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: y_support
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.11
4
+ version: 2.0.13
5
5
  platform: ruby
6
6
  authors:
7
7
  - boris
@@ -53,7 +53,6 @@ files:
53
53
  - lib/y_support.rb
54
54
  - lib/y_support/abstract_algebra.rb
55
55
  - lib/y_support/all.rb
56
- - lib/y_support/conscience.rb
57
56
  - lib/y_support/core_ext.rb
58
57
  - lib/y_support/core_ext/array.rb
59
58
  - lib/y_support/core_ext/array/misc.rb
@@ -81,6 +80,7 @@ files:
81
80
  - lib/y_support/stdlib_ext.rb
82
81
  - lib/y_support/stdlib_ext/matrix.rb
83
82
  - lib/y_support/stdlib_ext/matrix/misc.rb
83
+ - lib/y_support/try.rb
84
84
  - lib/y_support/typing.rb
85
85
  - lib/y_support/typing/array.rb
86
86
  - lib/y_support/typing/array/typing.rb
@@ -96,7 +96,6 @@ files:
96
96
  - lib/y_support/version.rb
97
97
  - lib/y_support/x.rb
98
98
  - test/abstract_algebra_test.rb
99
- - test/conscience_test.rb
100
99
  - test/inert_recorder_test.rb
101
100
  - test/kde_test.rb
102
101
  - test/local_object_test.rb
@@ -105,6 +104,7 @@ files:
105
104
  - test/name_magic_test.rb
106
105
  - test/null_object_test.rb
107
106
  - test/respond_to_test.rb
107
+ - test/try_test.rb
108
108
  - test/typing_test.rb
109
109
  - test/unicode_test.rb
110
110
  - test/x_test.rb
@@ -135,7 +135,6 @@ summary: LocalObject, RespondTo, InertRecorder, NullObject, NameMagic, core exte
135
135
  typing etc.
136
136
  test_files:
137
137
  - test/abstract_algebra_test.rb
138
- - test/conscience_test.rb
139
138
  - test/inert_recorder_test.rb
140
139
  - test/kde_test.rb
141
140
  - test/local_object_test.rb
@@ -144,6 +143,7 @@ test_files:
144
143
  - test/name_magic_test.rb
145
144
  - test/null_object_test.rb
146
145
  - test/respond_to_test.rb
146
+ - test/try_test.rb
147
147
  - test/typing_test.rb
148
148
  - test/unicode_test.rb
149
149
  - test/x_test.rb
@@ -1,132 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- require 'y_support'
3
- require 'active_support/core_ext/array/extract_options'
4
-
5
- # FIXME: Now, here, in order for the token game to work properly with
6
- # guarding system, several things have to be done. Firstly,
7
- # Place#set_marking( marking, blame ) will have to be implemented,
8
- # and in Place#add, blame argument will have to be added
9
- # Now with that blame, the transition will have to explain what it is
10
- # trying to do. So actually it should be a block
11
- #
12
- # It's gonna be a mixin, Conscience, or something like that, and it will
13
- # imbue the receiving class with the ability to try something.
14
- #
15
- # Transition.try "to fire!" do
16
- # if assignment_action? then
17
- # note has: "assignment action"
18
- # note "Δt", is: Δt
19
- # note "domain marking" is: domain_marking
20
- # act = note "Action" do Array action( Δt ) end
21
- # codomain.each_with_index do |place, i|
22
- # try "set the marking of place #{place} to the #{i}-th element of the action vector (#{act[i]})" do
23
- # place.marking = act[i]
24
- # end
25
- # end
26
- # else
27
- # etc. etc. etc.
28
- # end
29
- #
30
- # Should produce error messages like this:
31
- # When trying to fire! the transition #{self}, having assignment action, with Δt being #{Δt}, action has
32
- # been computed to #{Array action(Δt)}, when trying to set the marking of plac #{place} to the #{i}-th
33
- # element of the action vector (#{act[i]}), GuardError was encountered, with the message: ..."
34
- #
35
- # Yes, this is a managed code miniframework that I need here.
36
- #
37
- module Conscience
38
- class Try
39
- PUSH_ORDERED = -> ary, e {
40
- named = ary.extract_options!
41
- ary << e << named
42
- }
43
- PUSH_NAMED = -> ary, k, v {
44
- named = ary.extract_options!
45
- ary << named.update( k => v )
46
- }
47
- DECORATE = -> str, prefix: '', postfix: '' {
48
- str.to_s.tap { |ς| return ς.empty? ? '' : prefix + ς + postfix }
49
- }
50
- TRANSITIVE = Hash.new do |ꜧ, key| "#{key}ing %s" end
51
- .update( is: "being %s",
52
- has: "having %s" )
53
- STATE = Hash.new do |ꜧ, key| "#{key} %s" end
54
- .update( is: "%s",
55
- has: "has %s" )
56
-
57
- attr_reader :_object_, :_text_, :_block_, :_facts_
58
-
59
- def initialize( object: nil, text: nil, &block )
60
- @_object_, @_text_, @_block_ = object, text, block
61
- @_facts_ = Hash.new do |ꜧ, key| ꜧ[key] = [ {} ] end
62
- end
63
-
64
- def note *subjects, **statements, &block
65
- return *Array( subjects ).each do |s|
66
- PUSH_ORDERED.( _facts_[s], s )
67
- end if statements.empty?
68
- subjects << _object_ if subjects.empty?
69
- Array( subjects ).each do |subj|
70
- statements.each do |verb, object|
71
- PUSH_NAMED.( _facts_[subj], verb, object )
72
- end
73
- end
74
- return statements.first[1]
75
- end
76
-
77
- def call *args
78
- begin
79
- instance_exec *args, &_block_
80
- rescue StandardError => err
81
- txt1 = "When trying #{_text_}"
82
- thing, statements = _describe_
83
- txt2 = DECORATE.( thing, prefix: ' ' )
84
- txt3 = DECORATE.( statements.map { |verb, object|
85
- STATE[verb] % object
86
- }.join( ', ' ),
87
- prefix: ' (', postfix: ')' )
88
- txt4 = DECORATE.( _circumstances_, prefix: ', ' )
89
- txt5 = DECORATE.( "#{err.class} occurred: #{err}", prefix: ', ' )
90
- raise err, txt1 + txt2 + txt3 + txt4 + txt5
91
- end
92
- end
93
-
94
- def method_missing sym, *args
95
- _object_.send sym, *args
96
- end
97
-
98
- private
99
-
100
- def _describe_ obj=_object_
101
- facts = _facts_[obj].dup
102
- statements = facts.extract_options!
103
- fs = facts.join ', '
104
- if statements.empty? then
105
- return fs, statements
106
- else
107
- return facts.empty? ? obj.to_s : fs, statements
108
- end
109
- end
110
-
111
- def _circumstances_
112
- _facts_.reject { |subj, _| subj == _object_ }.map { |subj, _|
113
- thing, statements = _describe_( subj )
114
- thing + DECORATE.( statements.map { |v, o|
115
- TRANSITIVE[v] % o
116
- }.join( ', ' ),
117
- prefix: ' ' )
118
- }.join( ', ' )
119
- end
120
- end
121
-
122
- # Try method taxes two textual arguments and one block. The first (optional)
123
- # textual argument describes what is the receiver of #try. The second argument
124
- # describes in plain speech what activity is #try attempting. The block that
125
- # follows then contains the code, which performs that activity. In the block,
126
- # #note method is available, that builds up the context for the error message,
127
- # if any.
128
- #
129
- def try object=self, to_do_something, &block
130
- Try.new( object: object, text: to_do_something, &block ).call
131
- end
132
- end
@@ -1,75 +0,0 @@
1
- #! /usr/bin/ruby
2
-
3
- require 'minitest/spec'
4
- require 'minitest/autorun'
5
- # require 'y_support/conscience' # tested component itself
6
- require './../lib/y_support/conscience'
7
-
8
- include Conscience
9
-
10
- describe Try do
11
- before do
12
- @try = Try.new object: "Dummy", text: "to fire" do
13
- note is: "dummy"
14
- note has: "no care in the world"
15
- n = note "the number", is: 42
16
- raise TypeError, 'foo'
17
- end
18
- end
19
-
20
- it "should have basic functionality" do
21
- assert_equal "to fire", @try._text_
22
- assert_equal "Dummy", @try._object_
23
- assert_equal 0, @try._facts_.size # haven't tried anything yet
24
- @try._facts_["something"]
25
- assert_equal 1, @try._facts_.size
26
- @try.note is: 'dummy'
27
- @try.note has: 'no care in the world'
28
- assert_equal 2, @try._facts_.size
29
- assert_equal ["something", "Dummy"], @try._facts_.keys
30
- assert_equal( [ { is: "dummy", has: "no care in the world" } ],
31
- @try._facts_["Dummy"] )
32
- assert_equal " hello!", Try::DECORATE.( :hello, prefix: ' ', postfix: '!' )
33
- assert_equal( ['Dummy', {is: 'dummy', has: 'no care in the world'}],
34
- @try.send( :_describe_, "Dummy" ) )
35
- end
36
-
37
- describe 'case 1' do
38
- it "should work" do
39
- begin
40
- @try.call
41
- rescue TypeError => err
42
- expected_msg = "When trying to fire Dummy (dummy, has no care in " +
43
- "the world), the number being 42, TypeError occurred: foo"
44
- assert_equal expected_msg, err.message
45
- else
46
- flunk "Expected TypeError error not raised!"
47
- end
48
- end
49
- end
50
-
51
- describe 'case 2' do
52
- it "should work" do
53
- begin
54
- try "to call constant Nonexistant" do Nonexistant end
55
- rescue NameError => err
56
- expected_msg = 'When trying to call constant Nonexistant, ' +
57
- 'NameError occurred: uninitialized constant Nonexistant'
58
- assert_equal( expected_msg, err.message )
59
- else
60
- flunk "Expected NameError error not raised!"
61
- end
62
- end
63
- end
64
-
65
- describe 'case 3' do
66
- it "should work" do
67
- o = Object.new
68
- class << o
69
- def to_s; "THIS OBJECT" end
70
- def hello!; "hello hello" end
71
- end
72
- o.try "to call a missing method" do hello! end
73
- end
74
- end
75
- end