ruby_ex 0.1.1

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.
Files changed (108) hide show
  1. data/AUTHORS +51 -0
  2. data/ChangeLog +1763 -0
  3. data/NEWS +3 -0
  4. data/README +1 -0
  5. data/Rakefile +8 -0
  6. data/SPEC.dyn.yml +10 -0
  7. data/SPEC.gem.yml +269 -0
  8. data/SPEC.yml +36 -0
  9. data/src/abstract.rb +253 -0
  10. data/src/abstract_node.rb +85 -0
  11. data/src/algorithms.rb +12 -0
  12. data/src/algorithms/simulated_annealing.rb +142 -0
  13. data/src/ask.rb +100 -0
  14. data/src/attributed_class.rb +303 -0
  15. data/src/cache.rb +350 -0
  16. data/src/checkout.rb +12 -0
  17. data/src/choose.rb +271 -0
  18. data/src/commands.rb +20 -0
  19. data/src/commands/command.rb +492 -0
  20. data/src/commands/datas.rb +16 -0
  21. data/src/commands/datas/composite.rb +31 -0
  22. data/src/commands/datas/data.rb +65 -0
  23. data/src/commands/datas/factory.rb +69 -0
  24. data/src/commands/datas/temp.rb +26 -0
  25. data/src/commands/factory.rb +67 -0
  26. data/src/commands/helpers.rb +81 -0
  27. data/src/commands/pipe.rb +66 -0
  28. data/src/commands/runners.rb +16 -0
  29. data/src/commands/runners/exec.rb +50 -0
  30. data/src/commands/runners/fork.rb +130 -0
  31. data/src/commands/runners/runner.rb +140 -0
  32. data/src/commands/runners/system.rb +57 -0
  33. data/src/commands/seq.rb +32 -0
  34. data/src/config_file.rb +95 -0
  35. data/src/const_regexp.rb +57 -0
  36. data/src/daemon.rb +135 -0
  37. data/src/diff.rb +665 -0
  38. data/src/dlogger.rb +62 -0
  39. data/src/drb/drb_observable.rb +95 -0
  40. data/src/drb/drb_observable_pool.rb +27 -0
  41. data/src/drb/drb_service.rb +44 -0
  42. data/src/drb/drb_undumped_attributes.rb +56 -0
  43. data/src/drb/drb_undumped_indexed_object.rb +55 -0
  44. data/src/drb/insecure_protected_methods.rb +101 -0
  45. data/src/drb_ex.rb +12 -0
  46. data/src/dumpable_proc.rb +57 -0
  47. data/src/filetype.rb +229 -0
  48. data/src/generate_id.rb +44 -0
  49. data/src/histogram.rb +222 -0
  50. data/src/hookable.rb +283 -0
  51. data/src/hooker.rb +54 -0
  52. data/src/indexed_node.rb +65 -0
  53. data/src/io_marshal.rb +99 -0
  54. data/src/ioo.rb +193 -0
  55. data/src/labeled_node.rb +62 -0
  56. data/src/logger_observer.rb +24 -0
  57. data/src/md5sum.rb +70 -0
  58. data/src/module/autoload_tree.rb +65 -0
  59. data/src/module/hierarchy.rb +334 -0
  60. data/src/module/instance_method_visibility.rb +71 -0
  61. data/src/node.rb +81 -0
  62. data/src/object_monitor.rb +143 -0
  63. data/src/object_monitor_activity.rb +34 -0
  64. data/src/observable.rb +138 -0
  65. data/src/observable_pool.rb +291 -0
  66. data/src/orderedhash.rb +252 -0
  67. data/src/pp_hierarchy.rb +30 -0
  68. data/src/random_generators.rb +29 -0
  69. data/src/random_generators/random_generator.rb +33 -0
  70. data/src/random_generators/ruby.rb +25 -0
  71. data/src/ruby_ex.rb +124 -0
  72. data/src/safe_eval.rb +346 -0
  73. data/src/sendmail.rb +214 -0
  74. data/src/service_manager.rb +122 -0
  75. data/src/shuffle.rb +30 -0
  76. data/src/spring.rb +134 -0
  77. data/src/spring_set.rb +134 -0
  78. data/src/symtbl.rb +108 -0
  79. data/src/synflow.rb +474 -0
  80. data/src/thread_mutex.rb +11 -0
  81. data/src/timeout_ex.rb +79 -0
  82. data/src/trace.rb +26 -0
  83. data/src/uri/druby.rb +78 -0
  84. data/src/uri/file.rb +63 -0
  85. data/src/uri/ftp_ex.rb +36 -0
  86. data/src/uri/http_ex.rb +41 -0
  87. data/src/uri/pgsql.rb +136 -0
  88. data/src/uri/ssh.rb +87 -0
  89. data/src/uri/svn.rb +113 -0
  90. data/src/uri_ex.rb +71 -0
  91. data/src/verbose_object.rb +70 -0
  92. data/src/yaml/basenode_ext.rb +63 -0
  93. data/src/yaml/chop_header.rb +24 -0
  94. data/src/yaml/transform.rb +450 -0
  95. data/src/yaml/yregexpath.rb +76 -0
  96. data/test/algorithms/simulated_annealing_test.rb +102 -0
  97. data/test/check-pkg-ruby_ex.yml +15 -0
  98. data/test/check-ruby_ex.yml +12 -0
  99. data/test/resources/autoload_tree/A.rb +11 -0
  100. data/test/resources/autoload_tree/B.rb +10 -0
  101. data/test/resources/autoload_tree/foo/C.rb +18 -0
  102. data/test/resources/foo.txt +6 -0
  103. data/test/sanity-suite.yml +12 -0
  104. data/test/sanity/multiple-requires.yml +20 -0
  105. data/test/sanity/single-requires.yml +24 -0
  106. data/test/test-unit-setup.rb +6 -0
  107. data/test/unit-suite.yml +14 -0
  108. metadata +269 -0
data/src/uri/ssh.rb ADDED
@@ -0,0 +1,87 @@
1
+ # Copyright:: Copyright (c) 2005 Nicolas Pouillard. All rights reserved.
2
+ # Author:: Nicolas Pouillard <ertai@lrde.epita.fr>.
3
+ # License:: Gnu General Public License.
4
+ # Revision:: $Id$
5
+
6
+ require 'uri_ex'
7
+
8
+ module URI
9
+
10
+ class Ssh < Generic
11
+
12
+ SCHEME = 'ssh'.freeze
13
+ DEFAULT_HOST = 'localhost'.freeze
14
+ DEFAULT_PORT = 22
15
+ DEFAULT_QUERY = ''.freeze
16
+ SCP = 'scp'.to_cmd.freeze
17
+
18
+ COMPONENT = [
19
+ :scheme,
20
+ :userinfo,
21
+ :host,
22
+ :path,
23
+ :query
24
+ ].freeze
25
+
26
+ def self.build ( args )
27
+ tmp = Util::make_components_hash(self, args)
28
+ return super(tmp)
29
+ end
30
+
31
+ def mk_opts
32
+ opts = []
33
+ opts << '-q'
34
+ opts << '-P' << @port if @port != DEFAULT_PORT
35
+ return opts if @query.nil?
36
+ @query.split(/,/).map do |x|
37
+ k, v = x.split(/=/)
38
+ if k.size == 1
39
+ opts << "-#{k}"
40
+ else
41
+ opts << "--#{k}"
42
+ end
43
+ opts << v unless v.nil?
44
+ end
45
+ opts
46
+ end
47
+
48
+ def mk_arg
49
+ "#@user@#@host:#{@path.gsub(/^\//, '')}"
50
+ end
51
+
52
+ def checkout
53
+ out = TempPath.new('checkout', pathname.basename.to_s)
54
+ SCP[mk_opts, mk_arg, out].run(self.runner)
55
+ end
56
+
57
+ def save
58
+ checkout.save
59
+ end
60
+
61
+ def commit ( aPath )
62
+ SCP[mk_opts, aPath, mk_arg].run(self.runner)
63
+ end
64
+
65
+ end # class Ssh
66
+
67
+ @@schemes[Ssh::SCHEME.upcase] = Ssh
68
+
69
+ test_section __FILE__ do
70
+ class SshTest < Test::Unit::TestCase
71
+ def test_basic
72
+ assert_nothing_raised { @uri = URI.parse('ssh://foo@bar') }
73
+ assert_equal(['-q'], @uri.mk_opts)
74
+ assert_nothing_raised { @uri = URI.parse('ssh://foo@bar:42') }
75
+ assert_equal(['-q', '-P', 42], @uri.mk_opts)
76
+ end
77
+ def test_with_query
78
+ assert_nothing_raised do
79
+ @uri = URI.parse('ssh://foo@bar/qux?a=b,c,d=e,f,ghi,jkl=mno')
80
+ end
81
+ ref = ['-q', '-a', 'b', '-c', '-d', 'e', '-f', '--ghi', '--jkl', 'mno']
82
+ assert_equal(ref, @uri.mk_opts)
83
+ end
84
+ end # class SshTest
85
+ end
86
+
87
+ end # module URI
data/src/uri/svn.rb ADDED
@@ -0,0 +1,113 @@
1
+ # Copyright: Copyright (c) 2005 Nicolas Pouillard. All rights reserved.
2
+ # Author: Nicolas Pouillard <ertai@lrde.epita.fr>.
3
+ # License: Gnu General Public License.
4
+
5
+ # $LastChangedBy: ertai $
6
+ # $Id: svn.rb 237 2005-05-17 19:01:52Z ertai $
7
+
8
+ require 'uri_ex'
9
+ require 'abstract'
10
+
11
+ module URI
12
+
13
+ class Svn < Generic
14
+
15
+ COMPONENT = [
16
+ :scheme,
17
+ :host,
18
+ :path
19
+ ].freeze
20
+ SVN = 'svn'.to_cmd.freeze
21
+ SVNADMIN = 'svnadmin'.to_cmd.freeze
22
+
23
+
24
+ def self.build ( args )
25
+ tmp = Util::make_components_hash(self, args)
26
+ return super(tmp)
27
+ end
28
+
29
+ def checkout
30
+ # klass = SvnSchemes.guess(scheme)
31
+ # klass.new
32
+ tmp = TempPath.new('svn-checkout')
33
+ tmp.mkpath
34
+ target = to_s.sub(/^svn\./, '')
35
+ tmp += pathname.basename
36
+ SVN['checkout', target, tmp].run(self.runner)
37
+ end
38
+
39
+ def save
40
+ raise SaveError unless scheme.downcase == 'svn.file'
41
+ repos = pathname
42
+ raise SaveError unless (repos + 'format').exist?
43
+ out = TempPath.new('save', "#{repos.basename}.svndump")
44
+ cmd = SVNADMIN['dump', repos, '--incremental', '--quiet'] > out
45
+ cmd.run(self.runner)
46
+ end
47
+
48
+ end # class Svn
49
+
50
+ module SvnSchemes
51
+
52
+ def self.guess ( aScheme )
53
+ init
54
+ @@svn_schemes[aScheme]
55
+ end
56
+
57
+ def self.init
58
+ return if defined? @@svn_schemes
59
+ @@svn_schemes = {}
60
+ constants.each do |const|
61
+ klass = const_get(const)
62
+ next if klass.abstract?
63
+ @@svn_schemes[klass.scheme] = klass
64
+ end
65
+ @@svn_schemes.freeze
66
+ end
67
+
68
+ def self.svn_schemes
69
+ init
70
+ @@svn_schemes
71
+ end
72
+
73
+ class AbstractSvnScheme
74
+ include Abstract
75
+
76
+ def self.scheme
77
+ const_get(:SCHEME)
78
+ end
79
+
80
+ end
81
+
82
+ class SvnScheme < AbstractSvnScheme
83
+ include Concrete
84
+ SCHEME = 'SVN'
85
+ end
86
+
87
+ class SvnSshScheme < AbstractSvnScheme
88
+ include Concrete
89
+ SCHEME = 'SVN+SSH'
90
+ end
91
+
92
+ class SvnHttpScheme < AbstractSvnScheme
93
+ include Concrete
94
+ SCHEME = 'SVN.HTTP'
95
+ end
96
+
97
+ class SvnHttpsScheme < AbstractSvnScheme
98
+ include Concrete
99
+ SCHEME = 'SVN.HTTPS'
100
+ end
101
+
102
+ class SvnFileScheme < AbstractSvnScheme
103
+ include Concrete
104
+ SCHEME = 'SVN.FILE'
105
+ end
106
+
107
+ end
108
+
109
+ SvnSchemes.svn_schemes.each_key do |name|
110
+ @@schemes[name] = Svn
111
+ end
112
+
113
+ end # module URI
data/src/uri_ex.rb ADDED
@@ -0,0 +1,71 @@
1
+ # Copyright:: Copyright (c) 2005 Nicolas Pouillard. All rights reserved.
2
+ # Author:: Nicolas Pouillard <ertai@lrde.epita.fr>.
3
+ # License:: Gnu General Public License.
4
+ # Revision:: $Id: uri_ex.rb 266 2005-06-01 14:27:18Z ertai $
5
+
6
+ require 'uri'
7
+ require 'ruby_ex'
8
+ require 'commands'
9
+
10
+
11
+ module URI
12
+
13
+ class CheckoutError < Exception
14
+ end
15
+
16
+ class CommitError < Exception
17
+ end
18
+
19
+ class SaveError < Exception
20
+ end
21
+
22
+ class Generic
23
+
24
+ #
25
+ # To make all uris command verbose you can change or modify the global
26
+ # command runner: URI::Generic.runner.make_verbose!
27
+ #
28
+ class_inheritable_accessor :runner
29
+
30
+ self.runner = Commands::Runners::System.new.raise_on_failures
31
+ self.runner.command_data_factory.command_data_class = Commands::Datas::Temp
32
+
33
+ def pathname
34
+ Pathname.new(path)
35
+ end
36
+
37
+ def pathname= ( path )
38
+ self.path = path.to_s
39
+ end
40
+
41
+ def to_yaml_type
42
+ '!uri'
43
+ end
44
+
45
+ def to_yaml ( opts={} )
46
+ "!uri #{to_s}"
47
+ end
48
+
49
+ def checkout
50
+ raise CheckoutError, "Can't checkout a #{self.class}"
51
+ end
52
+
53
+ def commit
54
+ raise CommitError, "Can't commit a #{self.class}"
55
+ end
56
+
57
+ def save
58
+ raise SaveError, "Can't save a #{self.class}"
59
+ end
60
+
61
+ def add_query ( arg )
62
+ if query.nil?
63
+ self.query = arg
64
+ else
65
+ self.query += ',' + arg
66
+ end
67
+ end
68
+
69
+ end # module Generic
70
+
71
+ end # module URI
@@ -0,0 +1,70 @@
1
+ # Author:: Nicolas Pouillard <ertai@lrde.epita.fr>.
2
+ # Copyright:: Copyright (c) 2005 Nicolas Pouillard. All rights reserved.
3
+ # License:: GNU General Public License (GPL).
4
+ # Revision:: $Id: verbose_object.rb 266 2005-06-01 14:27:18Z ertai $
5
+
6
+ require 'ruby_ex'
7
+
8
+ module Verbosify
9
+
10
+ SKIP_METHODS = %w[ instance_eval ]
11
+
12
+ def self.extend_object ( anObject )
13
+ anObject.methods.each do |meth|
14
+ next if meth =~ /__.*__/
15
+ next if SKIP_METHODS.include? meth
16
+ anObject.instance_eval %Q{
17
+ def #{meth} ( *a, &b )
18
+ __log__(:#{meth}, *a, &b)
19
+ super
20
+ end
21
+ }
22
+ end
23
+ anObject.instance_eval do
24
+ def verbose_object?
25
+ true
26
+ end
27
+ def __log__ ( *a )
28
+ STDERR.puts "LOG: #{a.inspect}"
29
+ end
30
+ end
31
+ end
32
+
33
+ end # module Verbosify
34
+
35
+
36
+ class VerboseObject
37
+
38
+ def initialize ( anObject )
39
+ @obj = anObject
40
+ end
41
+
42
+ instance_methods.each do |meth|
43
+ next if meth =~ /__.*__/
44
+ module_eval %Q{
45
+ def #{meth} ( *a, &b )
46
+ method_missing(:#{meth}, *a, &b)
47
+ end
48
+ }
49
+ end
50
+
51
+ def verbose_object?
52
+ true
53
+ end
54
+
55
+ def __log__ ( *a )
56
+ STDERR.puts "LOG: #{a.inspect}"
57
+ end
58
+
59
+ def method_missing ( *a, &b )
60
+ __log__(*a)
61
+ @obj.__send__(*a, &b)
62
+ end
63
+
64
+ end # class VerboseObject
65
+
66
+ class Object
67
+ def verbose_object?
68
+ false
69
+ end
70
+ end # class Object
@@ -0,0 +1,63 @@
1
+ # Copyright: Copyright (c) 2004 Nicolas Pouillard. All rights reserved.
2
+ # Author: Nicolas Pouillard <ertai@lrde.epita.fr>.
3
+ # License: Gnu General Public License.
4
+
5
+ # $LastChangedBy: ertai $
6
+ # $Id: basenode_ext.rb 266 2005-06-01 14:27:18Z ertai $
7
+
8
+ require 'ruby_ex'
9
+ require 'yaml'
10
+ require 'yaml/basenode'
11
+
12
+ module YAML
13
+
14
+ module BaseNode
15
+
16
+ def ordered_children_with_index
17
+ arr = children_with_index
18
+ arr.sort! { |a,b| b[0].object_id <=> a[0].object_id } unless arr.nil?
19
+ arr
20
+ end
21
+
22
+ def ordered_children_indexes
23
+ arr = ordered_children_with_index
24
+ arr.map! { |x| x[1] } unless arr.nil?
25
+ arr
26
+ end
27
+
28
+ def ordered_children
29
+ arr = children
30
+ arr.map! { |x| x[0] } unless arr.nil?
31
+ arr
32
+ end
33
+
34
+ end # module BaseNode
35
+
36
+ end # module YAML
37
+
38
+
39
+ test_section __FILE__ do
40
+
41
+ class BaseNodeExtTest < Test::Unit::TestCase
42
+
43
+ def basenode_checker ( my, ref )
44
+ node = YAML::parse(my)
45
+ assert_equal(node.ordered_children_indexes, ref)
46
+ end
47
+
48
+ def test_basenode1
49
+ basenode_checker('--- { d: 4, a: 1, b: 2, c: 3 }', ["d", "a", "b", "c"])
50
+ end
51
+
52
+ def test_basenode2
53
+ basenode_checker('--- { a: 1, d: 42, b: 2, c: 3 }', ["a", "d", "b", "c"])
54
+ end
55
+
56
+ def test_basenode3
57
+ basenode_checker(
58
+ '--- { a: :"1", d: "foo", b: [2,3], c: {} }', ["a", "d", "b", "c"])
59
+ end
60
+
61
+ end # class BaseNodeExtTest
62
+
63
+ end
@@ -0,0 +1,24 @@
1
+ # Author:: Nicolas Pouillard <ertai@lrde.epita.fr>.
2
+ # Copyright:: Copyright (c) 2004 LRDE. All rights reserved.
3
+ # License:: GNU General Public License (GPL).
4
+ # Revision:: $Id: header 98 2004-09-29 12:07:43Z ertai $
5
+
6
+ require 'ruby_ex'
7
+ require 'yaml'
8
+
9
+ module YAML
10
+
11
+ def self.chop_header ( io )
12
+ aStr = io.gets
13
+ unless aStr =~ /^---/
14
+ io.rewind
15
+ raise Exception, "First line is not valid: `#{aLine}'"
16
+ end
17
+ io.each do |aLine|
18
+ break if aLine =~ /^---/
19
+ aStr += aLine
20
+ end
21
+ YAML::load(aStr)
22
+ end
23
+
24
+ end # module YAML
@@ -0,0 +1,450 @@
1
+ # Author:: Marco Tessari <marco.tessari@epita.fr>.
2
+ # Copyright:: Copyright (c) 2004 TTK Team. All rights reserved.
3
+ # License:: Gnu General Public License.
4
+
5
+ # $LastChangedBy: ertai $
6
+ # $Id: transform.rb 266 2005-06-01 14:27:18Z ertai $
7
+
8
+ # TODO: Raised exception must be completed with a descritpion
9
+ # TODO: Fix bug on Array see test_higher_level_on_array
10
+ # TODO: Comment code.
11
+ # TODO: Write more tests.
12
+ # TODO: Make it stream.
13
+ # TODO: Add the possibility to write '//' to match all node with deep n.
14
+ # TODO: Add the && and ||.
15
+ # TODO: Optimize, skip if no more node to match.
16
+ # TODO: separate dot implemantation.
17
+
18
+ require 'ruby_ex'
19
+ require 'yaml/yregexpath'
20
+
21
+ module YAML
22
+
23
+ class Transformer
24
+ private
25
+
26
+ # FIXME: Implement all ruby types.
27
+ class ::Object # :nodoc:
28
+ def yaml_doc_traverse ( activated )
29
+ raise ArgumentError, "can't traverse class #{self.class}"
30
+ end
31
+ end
32
+
33
+ class ::Hash # :nodoc:
34
+ # FIXME: Are we sure we want to traverse like that? (by stage)
35
+ def yaml_doc_traverse ( activated )
36
+ new_activated = []
37
+ sons = []
38
+ self.each do |key, value|
39
+ activated.each do |n|
40
+ new_activated += n.match(key, value)
41
+ sons << value unless value.is_a?(String)
42
+ end
43
+ end
44
+ activated += new_activated
45
+ sons.yaml_doc_traverse(activated)
46
+ end
47
+ end
48
+
49
+ class ::Array # :nodoc:
50
+ def yaml_doc_traverse ( activated )
51
+ self.each { |e| e.yaml_doc_traverse(activated) }
52
+ end
53
+ end
54
+
55
+ class Node
56
+ attr_reader :name, :values
57
+
58
+ Struct.new( 'Value', :regexp, :remember, :associated )
59
+
60
+ def initialize ( name )
61
+ raise ArgumentError unless name.is_a?(Regexp)
62
+ @name = name
63
+ @values = []
64
+ @uid = @@cpt
65
+ @@cpt += 1
66
+ # Save a node to handle the '#' feature.
67
+ @saved_node_key = nil
68
+ @saved_node_value = nil
69
+ end
70
+ @@cpt = 0
71
+
72
+ # Return the associated nodes of a value
73
+ # Add the value if not present.
74
+ def get_associated ( value, remember=false )
75
+ val = @values.find { |e| e.regexp == value and e.remember == remember }
76
+ if val.nil?
77
+ val = Struct::Value.new(value, remember, [])
78
+ @values << val
79
+ end
80
+ return val.associated
81
+ end
82
+
83
+ # Dottify a node.
84
+ def dottify ( stream )
85
+ # Declare node
86
+ stream << "#{unique_name} [shape=box, color=blue, label=#{self}]\n"
87
+ # Print node links with values
88
+ stream << "#{unique_name} -> { rank=same; "
89
+ @values.each_index { |i| stream << "value#{@uid}_#{i}; " }
90
+ stream << "}\n"
91
+ # Declare each values and links
92
+ @values.each_with_index do |val, i|
93
+ stream << "value#{@uid}_#{i} [shape=box, color=yellow, label=\"/"
94
+ stream << val.regexp.source << '/'
95
+ stream << ' #' if val.remember
96
+ stream << "\"]\n"
97
+ stream << "value#{@uid}_#{i} -> { rank=same; "
98
+ val.associated.each { |match| stream << "#{match.unique_name}; " }
99
+ stream << "} \n"
100
+ val.associated.each { |match| match.dottify(stream) }
101
+ end
102
+ end
103
+
104
+ def match ( key, value )
105
+ return [] unless key =~ @name
106
+ activated = []
107
+ @values.each do |val|
108
+ if ! value.is_a?(String) or value =~ val.regexp
109
+ save_node_set(key, value) if val.remember
110
+ val.associated.each do |n|
111
+ if n.is_a?(Match)
112
+ # There must be a remembered node in the path.
113
+ p self unless has_saved_node?
114
+ n[@saved_node_key, @saved_node_value]
115
+ else
116
+ n.save_node_set(@saved_node_key,
117
+ @saved_node_value) if has_saved_node?
118
+ activated << n
119
+ end
120
+ end
121
+ save_node_reset if val.remember
122
+ end
123
+ end
124
+ save_node_reset
125
+ return activated
126
+ end
127
+
128
+ def save_node_set ( key, value )
129
+ raise RuntimeError unless @saved_node_key.nil?
130
+ @saved_node_key = key
131
+ raise RuntimeError unless @saved_node_value.nil?
132
+ @saved_node_value = value
133
+ end
134
+
135
+ def save_node_reset
136
+ @saved_node_key = nil
137
+ @saved_node_value = nil
138
+ end
139
+
140
+ def has_saved_node?
141
+ return ! (@saved_node_key.nil? and @saved_node_value.nil?)
142
+ end
143
+
144
+ def unique_name
145
+ "node#{@uid}"
146
+ end
147
+
148
+ # Human readable name.
149
+ def to_s
150
+ "\"/#{@name.source}/\""
151
+ end
152
+ end
153
+
154
+ class Root
155
+ attr_reader :children
156
+
157
+ def initialize
158
+ @name = 'root'
159
+ @children = []
160
+ end
161
+
162
+ def dottify ( stream )
163
+ stream << "#{self} [shape=circle, color=red, label=#{self}]\n"
164
+ stream << "#{self} -> { rank=same; "
165
+ @children.each { |e| stream << "#{e.unique_name} " }
166
+ stream << "}\n"
167
+ @children.each { |e| e.dottify(stream) }
168
+ end
169
+
170
+ def to_s
171
+ 'root'
172
+ end
173
+
174
+ alias unique_name to_s
175
+ end
176
+
177
+ class Match
178
+
179
+ def initialize ( name=nil, &block )
180
+ @block = block
181
+ @name = name
182
+ @uid = @@cpt
183
+ @@cpt += 1
184
+ end
185
+ @@cpt = 0
186
+
187
+ def dottify ( stream )
188
+ stream << "match#{@uid} [shape=circle, color=green, label=#{self}]\n"
189
+ end
190
+
191
+ def call ( key, value )
192
+ @block[key, value]
193
+ end
194
+
195
+ alias [] call
196
+
197
+ def unique_name
198
+ "match#{@uid}"
199
+ end
200
+
201
+ def to_s
202
+ @name.nil? ? unique_name : "\"#{@name}\""
203
+ end
204
+ end
205
+
206
+ public
207
+ # Constructor.
208
+ def initialize
209
+ @rootgraph = Root.new
210
+ @graph = []
211
+ @activated = []
212
+ end
213
+
214
+ # Register the given block and compile the path.
215
+ def add ( path, name=nil, &block)
216
+ raise ArgumentError unless (path.kind_of?(YRegexPath))
217
+ raise ArgumentError if (block.nil?)
218
+ current_nodes = path.root ? @rootgraph.children : @graph
219
+ add_segment(path.segments, path.wanted_node_index,
220
+ current_nodes, name, &block)
221
+ end
222
+ alias register add
223
+
224
+ def add_segment ( seg, remember_index, current_nodes, name, &block )
225
+ if (seg.empty?)
226
+ current_nodes << Match.new(name, &block)
227
+ return
228
+ end
229
+ updated = false
230
+ searched_name, searched_value = seg.shift
231
+ searched_value = /.*/ if searched_value.nil?
232
+ current_nodes.collect! do |n|
233
+ if (n.is_a?(Node) and n.name == searched_name)
234
+ updated = true
235
+ update_node(n, searched_value, seg, remember_index, name, &block)
236
+ else
237
+ n
238
+ end
239
+ end
240
+ unless (updated)
241
+ current_nodes << update_node(Node.new(searched_name), searched_value,
242
+ seg, remember_index, name, &block)
243
+ end
244
+ end
245
+ private :add_segment
246
+
247
+ def update_node ( node, value, segment, remember_index, name, &block )
248
+ sons = node.get_associated(value, remember_index == 1)
249
+ add_segment(segment, remember_index - 1, sons, name, &block)
250
+ return node
251
+ end
252
+
253
+ # Print graph in a file in doty format
254
+ def dottify ( filename )
255
+ File.open(filename, 'w') do |f|
256
+ f << <<EOF
257
+ /* Compiled rules graph visualisation by YSLT */
258
+ digraph "Compiled rules" {
259
+ node [style=filled]
260
+ edge [style=solid, arrowtail=none]
261
+ rankdir=LR
262
+ EOF
263
+ @rootgraph.dottify(f)
264
+ @graph.each do |n|
265
+ n.dottify(f)
266
+ end
267
+ f << "}\n"
268
+ end
269
+ end
270
+
271
+ # Reset position to root.
272
+ def reset
273
+ @activated = @rootgraph.children + @graph
274
+ end
275
+
276
+ # Clear compiled rules.
277
+ def clear
278
+ initialize
279
+ end
280
+
281
+ # Traverse an entire YAML tree.
282
+ def traverse ( tree )
283
+ reset
284
+ tree.yaml_doc_traverse(@activated)
285
+ end
286
+
287
+ # Work like XLST.
288
+ # Traverse all tree and return a list of element that match the path.
289
+ def self.traverse ( path, tree, &block )
290
+ p = self.new
291
+ p.add(path, &block)
292
+ p.traverse(tree)
293
+ end
294
+
295
+ end # class Transformer
296
+
297
+ end # module YAML
298
+
299
+ test_section __FILE__ do
300
+
301
+ class Transformer < Test::Unit::TestCase
302
+
303
+ def test_compiled
304
+ # Initialization.
305
+ engine = nil
306
+ str = ''
307
+ assert_nothing_raised { engine = YAML::Transformer.new }
308
+
309
+ # Build.
310
+ assert_nothing_raised do
311
+ engine.add(YAML::YRegexPath.new('/name')) do |key, value|
312
+ assert_instance_of(String, key)
313
+ str += "#{value} is"
314
+ end
315
+ end
316
+ assert_nothing_raised do
317
+ engine.add(YAML::YRegexPath.new('/name=Akli/boss')) do |key, value|
318
+ assert_instance_of(String, key)
319
+ str += ' a boss'
320
+ end
321
+ end
322
+ assert_nothing_raised do
323
+ engine.add(YAML::YRegexPath.new('/jobs/professor=EPITA/class')) do |k,v|
324
+ assert_instance_of(String, k)
325
+ str += e
326
+ end
327
+ end
328
+ assert_nothing_raised do
329
+ engine.add(YAML::YRegexPath.new('toto#titi'), 'unused') do
330
+ str += 'Why am I HERE???'
331
+ end
332
+ end
333
+
334
+ # Display.
335
+ TempPath.new do |dot_file|
336
+ assert_nothing_raised { engine.dottify(dot_file) }
337
+ `dot #{dot_file} 2> /dev/null`
338
+ # assert_equal(0, $?, 'Cannot display the graph')
339
+ end
340
+
341
+ # Traverse.
342
+ tree = {
343
+ 'name' => 'Akli A.',
344
+ 'tel' => '06932359',
345
+ 'address' => '23 boulevard voltaire',
346
+ 'jobs' => [
347
+ {
348
+ 'professor' => 'EPITA',
349
+ 'boss' => 'MindSuite',
350
+ 'god' => 'world',
351
+ }
352
+ ]
353
+ }
354
+ assert_nothing_raised { engine.traverse(tree) }
355
+ assert_equal(str, 'Akli A. is a boss a boss a boss')
356
+ end
357
+
358
+ def test_unique
359
+ names = []
360
+ tree = {
361
+ 'users' => [
362
+ {
363
+ 'name' => 'Sea',
364
+ 'tel' => '111'
365
+ },
366
+ {
367
+ 'name' => 'Sex',
368
+ 'tel' => '222'
369
+ },
370
+ {
371
+ 'name' => 'Sun',
372
+ 'tel' => '333'
373
+ }
374
+ ]
375
+ }
376
+ YAML::Transformer.traverse(YAML::YRegexPath.new('name=Se'), tree) do
377
+ |k,v| names << v
378
+ end
379
+ assert_equal(['Sea', 'Sex'], names)
380
+ end
381
+
382
+ def test_higher_level_on_hash
383
+ email = ''
384
+ tree = {
385
+ 'Name' => 'Foo',
386
+ 'FirstName' => 'Bar',
387
+ 'Emails' => {
388
+ 'home' => 'foo312@coldmail.com',
389
+ 'work' => 'bfoo@staff.com',
390
+ 'edu' => 'bar.foo@school.edu'
391
+ }
392
+ }
393
+ path = YAML::YRegexPath.new('Emails#.*=.*\.edu')
394
+ YAML::Transformer.traverse(path, tree) do |key, value|
395
+ assert_instance_of(Hash, value)
396
+ email += "#{value['home']}"
397
+ end
398
+ assert_equal('foo312@coldmail.com', email)
399
+ end
400
+
401
+ # def test_higher_level_on_array
402
+ # users = []
403
+ # tree = {
404
+ # 'users' => [
405
+ # {
406
+ # 'name' => 'Sea',
407
+ # 'tel' => '111'
408
+ # },
409
+ # {
410
+ # 'name' => 'Sex',
411
+ # 'tel' => '222'
412
+ # },
413
+ # {
414
+ # 'name' => 'Sun',
415
+ # 'tel' => '333'
416
+ # }
417
+ # ]
418
+ # }
419
+ # YAML::Transformer.traverse(YAML::YRegexPath.new('users#name=Se'), tree) do
420
+ # |key, value|
421
+ # assert_instance_of(Array, value)
422
+ # value.each do |e|
423
+ # users << "#{e['name']} (#{e['tel']})"
424
+ # end
425
+ # end
426
+ # p users
427
+ # #assert_equal(['Sea (111)', 'Sex (222)'], users)
428
+ # end
429
+
430
+ def test_errors
431
+ engine = nil
432
+ assert_nothing_raised { engine = YAML::Transformer.new }
433
+
434
+ # Not an YRegexPath gived.
435
+ assert_raise(ArgumentError) do
436
+ engine.add('/name') { |key, value| str += "error" }
437
+ end
438
+
439
+ # # Block with only one parameter.
440
+ # assert_raise(ArgumentError) do
441
+ # engine.add(YAML::YRegexPath.new('/name')) { |key| str += "error" }
442
+ # end
443
+
444
+ # No block ginven.
445
+ assert_raise(ArgumentError) do
446
+ engine.add(YAML::YRegexPath.new('/name'))
447
+ end
448
+ end
449
+ end
450
+ end