ydl 0.2.06

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +23 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +5 -0
  5. data/Gemfile +4 -0
  6. data/Gemfile.lock +83 -0
  7. data/README.org +208 -0
  8. data/Rakefile +6 -0
  9. data/bin/console +11 -0
  10. data/bin/setup +8 -0
  11. data/examples/brief.tex +43 -0
  12. data/examples/cases.ydl +79 -0
  13. data/examples/header.rb +12 -0
  14. data/examples/lawyers.ydl +65 -0
  15. data/examples/mwe.tex +67 -0
  16. data/examples/persons.ydl +10 -0
  17. data/examples/simple.tex +23 -0
  18. data/examples/test_page.tex +76 -0
  19. data/lib/ydl/core_ext/array.rb +6 -0
  20. data/lib/ydl/core_ext/array_refine.rb +16 -0
  21. data/lib/ydl/core_ext/boolean.rb +13 -0
  22. data/lib/ydl/core_ext/date.rb +6 -0
  23. data/lib/ydl/core_ext/hash.rb +16 -0
  24. data/lib/ydl/core_ext/numeric.rb +6 -0
  25. data/lib/ydl/core_ext/string.rb +16 -0
  26. data/lib/ydl/core_ext.rb +7 -0
  27. data/lib/ydl/errors.rb +5 -0
  28. data/lib/ydl/node.rb +190 -0
  29. data/lib/ydl/top_queue.rb +40 -0
  30. data/lib/ydl/tree.rb +95 -0
  31. data/lib/ydl/version.rb +3 -0
  32. data/lib/ydl/ydl.rb +244 -0
  33. data/lib/ydl.rb +15 -0
  34. data/spec/array_spec.rb +14 -0
  35. data/spec/core_ext/array_spec.rb +23 -0
  36. data/spec/core_ext/boolean_spec.rb +10 -0
  37. data/spec/core_ext/date_spec.rb +9 -0
  38. data/spec/core_ext/hash_spec.rb +19 -0
  39. data/spec/core_ext/numeric_spec.rb +13 -0
  40. data/spec/core_ext/string_spec.rb +27 -0
  41. data/spec/example_files/err/person_err.ydl +6 -0
  42. data/spec/example_files/home/user/.ydl/config_template.yaml +40 -0
  43. data/spec/example_files/home/user/.ydl/courts/courts.ydl +28 -0
  44. data/spec/example_files/home/user/.ydl/lawyers.ydl +40 -0
  45. data/spec/example_files/home/user/.ydl/persons.ydl +9 -0
  46. data/spec/example_files/home/user/project/cases.ydl +59 -0
  47. data/spec/example_files/home/user/project/courts.ydl +7 -0
  48. data/spec/example_files/home/user/project/judges.ydl +27 -0
  49. data/spec/example_files/home/user/project/lawyers.ydl +52 -0
  50. data/spec/example_files/home/user/project/persons.ydl +5 -0
  51. data/spec/example_files/home/user/project/subproject/lawyers.ydl +85 -0
  52. data/spec/example_files/home/user/project/subproject/persons.ydl +55 -0
  53. data/spec/example_files/sys/ydl/constants.ydl +1 -0
  54. data/spec/example_files/sys/ydl/junk.ydl +1 -0
  55. data/spec/spec_helper.rb +55 -0
  56. data/spec/ydl_error_spec.rb +15 -0
  57. data/spec/ydl_spec.rb +184 -0
  58. data/yaml_v_psych.rb +15 -0
  59. data/ydl.gemspec +40 -0
  60. metadata +230 -0
@@ -0,0 +1,76 @@
1
+ \documentclass{article}
2
+
3
+ \usepackage{setspace}
4
+ \usepackage{array}
5
+ \usepackage{longtable}
6
+ \usepackage{yfonts}
7
+ \usepackage{calc}
8
+ \usepackage[showframe]{geometry}
9
+
10
+ \begin{document}
11
+
12
+ \setlength\extrarowheight{12pt}%
13
+ \newlength{\ldhalfwd}
14
+ \setlength{\ldhalfwd}{((\textwidth) * \real{0.95} - \columnsep)/2}%
15
+
16
+ \def\ol{%
17
+ %\begin{small}
18
+ \begin{longtable}{p{\ldhalfwd}p{\ldhalfwd}}%
19
+ \parbox[t]{\ldhalfwd}{
20
+ \textsc{Daniel E. Doherty}\\
21
+ Commerce Plaza I\\
22
+ 7300 W. 110th Street, Suite 930\\
23
+ Overland Park, KS 66210\\
24
+ \textbf{Phone:} 913-338-7182\\
25
+ \textbf{Fax:} 913-338-7164\\
26
+ \textbf{Email:} \texttt{ded-law@ddoherty.net}\\
27
+ Attorney for Lisa A. Gibbons and Revive Investing LLC
28
+ }&
29
+ \parbox[t]{\ldhalfwd}{
30
+ \textsc{Charles J. Hyland}\\
31
+ Commerce Plaza I\\
32
+ 7300 W. 110th Street, Suite 930\\
33
+ Overland Park, KS 66210\\
34
+ \textsc{Hyland Law Firm LLC}\\
35
+ \textbf{Phone:} 913-498-1911\\
36
+ \textbf{Fax:} 913-498-1950\\
37
+ \textbf{Email:} \texttt{charlie@hylandkc.com}\\
38
+ Attorney for Lisa A. Gibbons and Revive Investing LLC
39
+ }\\
40
+ \end{longtable}
41
+ %\end{small}
42
+ }
43
+
44
+ \thispagestyle{empty}
45
+ \setcounter{page}{-1}
46
+ {PreCaption}
47
+ \begin{center}
48
+ \hrule
49
+ \vspace{10pt}
50
+ \vfil
51
+ {\Large\gothfamily In th{e}\\
52
+ {\LARGE ForumName:}}
53
+ \vfil
54
+ \hrule
55
+ \vfil
56
+ LeftCaptionBlock
57
+ \vfil
58
+ \hrule
59
+ \vfil
60
+ \textbf{OriginatingCourt}
61
+ \vfil
62
+ \hrule
63
+ \vfil
64
+ \textsc{\bfseries DocTitle}
65
+ \vfil
66
+ \hrule
67
+ \vfil
68
+ \begin{singlespace}
69
+ \mbox{\ol}
70
+ \end{singlespace}
71
+ \vfil
72
+ \vspace{10pt}
73
+ \hrule
74
+ \end{center}
75
+ \clearpage
76
+ \end{document}
@@ -0,0 +1,6 @@
1
+ # Extension of Array class.
2
+ class Array
3
+ def xref?
4
+ select { |v| v.is_a?(String) }.any?(&:xref?)
5
+ end
6
+ end
@@ -0,0 +1,16 @@
1
+ module Ydl
2
+ # Extensions for Array class
3
+ module ArrayRefinements
4
+ refine Array do
5
+ # Return true of this array has other as a prefix. If self is [:a, :b,
6
+ # :c, :d], then other is a prefix if it consists of elements equal to
7
+ # the corresponding element of self through other's whole length.
8
+ def prefixed_by(other)
9
+ return false if other.length > length
10
+
11
+ residuals = zip(other).drop_while { |(a, b)| a == b }
12
+ residuals.empty? || residuals.all? { |(a, b)| !a.nil? && b.nil? }
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,13 @@
1
+ # Add xref? to booleans.
2
+ class TrueClass
3
+ def xref?
4
+ false
5
+ end
6
+ end
7
+
8
+ # Add xref? to booleans.
9
+ class FalseClass
10
+ def xref?
11
+ false
12
+ end
13
+ end
@@ -0,0 +1,6 @@
1
+ # Add xref? to Date.
2
+ class Date
3
+ def xref?
4
+ false
5
+ end
6
+ end
@@ -0,0 +1,16 @@
1
+ require 'tsort'
2
+
3
+ # Extend Hash for use with TSort and add xref?.
4
+ class Hash
5
+ include TSort
6
+
7
+ alias tsort_each_node each_key
8
+
9
+ def tsort_each_child(node, &block)
10
+ fetch(node).each(&block)
11
+ end
12
+
13
+ def xref?
14
+ values.select { |v| v.is_a?(String)}.any?(&:xref?)
15
+ end
16
+ end
@@ -0,0 +1,6 @@
1
+ # Add xref? to Numerics.
2
+ class Numeric
3
+ def xref?
4
+ false
5
+ end
6
+ end
@@ -0,0 +1,16 @@
1
+ require 'active_support/core_ext/string'
2
+
3
+ # Some useful monkey patching for Ydl
4
+ class String
5
+ def singular?
6
+ singularize == self
7
+ end
8
+
9
+ def plural?
10
+ !singular?
11
+ end
12
+
13
+ def xref?
14
+ clean.match?(%r{\Aydl:/?})
15
+ end
16
+ end
@@ -0,0 +1,7 @@
1
+ require 'ydl/core_ext/array'
2
+ require 'ydl/core_ext/array_refine'
3
+ require 'ydl/core_ext/boolean'
4
+ require 'ydl/core_ext/date'
5
+ require 'ydl/core_ext/hash'
6
+ require 'ydl/core_ext/numeric'
7
+ require 'ydl/core_ext/string'
data/lib/ydl/errors.rb ADDED
@@ -0,0 +1,5 @@
1
+ module Ydl
2
+ class UserError < RuntimeError; end
3
+ class CircularReference < RuntimeError; end
4
+ class BadXRef < RuntimeError; end
5
+ end
data/lib/ydl/node.rb ADDED
@@ -0,0 +1,190 @@
1
+ module Ydl
2
+ # A Node in a Ydl::Tree
3
+ class Node
4
+ attr_reader :path, :tree_id
5
+ attr_accessor :val, :klass, :children
6
+
7
+ def initialize(path, val, klass = nil, tree_id:)
8
+ # The path is an array of symbols representing the series of references
9
+ # taken from the root of the tree to this Node.
10
+ @path = path
11
+ # The Object id for the root Node of the tree of which this Node is a
12
+ # part.
13
+ @tree_id = tree_id
14
+ # The uninterpreted value for this Node, which when instantiated, will
15
+ # be an instance of klass. Either a Hash or a primitive type.
16
+ @val = val
17
+
18
+ # The class into which this Node should be instantiated.
19
+ @klass = klass
20
+ # child Nodes built from val; always a Hash, but keys may be numeric
21
+ # symbols, such a :'1', :'88', etc, where an sequential array-like
22
+ # structure is wanted.
23
+ @children = {}
24
+ end
25
+
26
+ # Return a reference to the Ydl::Tree to which this Node belongs, in case we
27
+ # instantiate more than one tree.
28
+ def our_tree
29
+ ObjectSpace._id2ref(tree_id)
30
+ end
31
+
32
+ # Return an Array of the
33
+ def prerequisites
34
+ result = []
35
+ if children.size.positive?
36
+ children.each_value do |child|
37
+ result += child.prerequisites
38
+ end
39
+ elsif val.instance_of?(String)
40
+ result << val if val.xref?
41
+ end
42
+ result.flatten
43
+ end
44
+
45
+ # Return the /val/ of child at key +key+ or nil if there is none
46
+ def [](key)
47
+ return nil if children.empty?
48
+
49
+ key = key.to_s.to_sym if key.is_a?(Numeric)
50
+ children[key]
51
+ end
52
+
53
+ # Return the xref for this Node.
54
+ def xref
55
+ Tree.path_to_xref(path)
56
+ end
57
+
58
+ # Record a dependency of this Node on a foreign Node by virtue of a
59
+ # cross-reference to the foreign Node. The argument foreign can be a
60
+ # string in the form of a Node xref or an array of xrefs.
61
+ def depends_on(foreign)
62
+ our_tree.workq.add_dependency(xref, foreign) unless foreign.empty?
63
+ end
64
+
65
+ # Convert this Node's children to a Hash or Array.
66
+ def to_params
67
+ return {} if children.empty?
68
+
69
+ make_arr = children.keys.map(&:to_s).all? { |k| k =~ /\A[0-9]+\z/ }
70
+ result = make_arr ? [] : {}
71
+
72
+ children.each_pair do |k, child|
73
+ k = make_arr ? k.to_s.to_i : k
74
+ result[k] =
75
+ if child.children.empty? || child.instantiated?
76
+ child.val
77
+ else
78
+ child.to_params
79
+ end
80
+ rescue TypeError
81
+ warn "ydl: cannot convert #{path} with value '#{child.val}' to params"
82
+ end
83
+ result
84
+ end
85
+
86
+ # Recursively build the subtree of Nodes starting at this Node and set
87
+ # this node's instance variables.
88
+ #
89
+ # This Node's val is either (1) a String (not a cross-reference), (2) a
90
+ # String that is a cross reference, in which case we need to record its
91
+ # dependence on the referenced node in the Tree.workq, (3) a Hash in which
92
+ # case its elements become the children of this Node and should have their
93
+ # klass set to the class corresponding to this Node's last path key if
94
+ # it's a registered class, or (4) an Array, in which case its elements
95
+ # become the children of this Node converted into a Hash (with their
96
+ # numeric indices as keys) and its children have their klass set as with a
97
+ # Hash.
98
+ def build_subtree
99
+ child_klass = Ydl.class_for(path.last) unless path.empty?
100
+ case val
101
+ when Hash
102
+ warn "Build from Hash for class '#{child_klass}': #{val.keys.join('|')}" if child_klass
103
+ # Build child subtrees first
104
+ val.each_pair do |k, v|
105
+ # If this node names a registered class, its /children/ should be
106
+ # instantiated into that class, but this node itself should not be.
107
+ # E.g., if this node's path ends in :persons, then it is a container
108
+ # for the class Person, and its children should be instantiated into
109
+ # that class, but not the container itself. This node may also
110
+ # simply represent a parameter for a class above it, e.g., :name,
111
+ # for a Person class. We set the klass of the child to nil if it is
112
+ # either a container node or a parameter node, but we set it to the
113
+ # class if it is to be instantiated.
114
+ child = Node.new(path + [k], v, child_klass, tree_id: tree_id)
115
+ # Depth-first recursion on building the Tree.
116
+ children[k] = child.build_subtree
117
+ end
118
+ # Record the cross-reference dependencies for this Node
119
+ depends_on(prerequisites)
120
+ self.val = nil
121
+ when Array
122
+ warn "Build from Array for class '#{child_klass}'" if child_klass
123
+ val.each_with_index do |v, k|
124
+ child = Node.new(path + [k.to_s.to_sym], v, child_klass, tree_id: tree_id)
125
+ children[k.to_s.to_sym] = child.build_subtree
126
+ end
127
+ depends_on(prerequisites)
128
+ self.klass = nil
129
+ self.val = nil
130
+ when String
131
+ if val.xref?
132
+ warn "Noted cross-reference to '#{xref}'"
133
+ depends_on(val)
134
+ else
135
+ self.klass = String
136
+ end
137
+ self.children = {}
138
+ else
139
+ # E.g., Numeric, Date, DateTime
140
+ self.children = {}
141
+ self.klass = val.class
142
+ end
143
+ self
144
+ end
145
+
146
+ # Return an object of class @klass if one can be initialized with the Hash
147
+ # val or the current Node converted to a params hash.
148
+ def instantiate
149
+ return nil if klass.blank?
150
+ return val if instantiated?
151
+
152
+ warn "Instantiating #{path} to #{klass} ..."
153
+ result =
154
+ if val.instance_of?(Hash)
155
+ klass.send(konstructor, **val)
156
+ else
157
+ klass.send(konstructor, **to_params)
158
+ end
159
+ warn "Instantiated #{path} to #{klass}" if result
160
+ self.val = result
161
+ end
162
+
163
+ def instantiated?
164
+ klass && val.instance_of?(klass)
165
+ end
166
+
167
+ # Do a depth-first instantiation of this node's children, then this node.
168
+ def instantiate_subtree
169
+ children.each_value do |child|
170
+ next if child.instantiated?
171
+
172
+ child.val =
173
+ if child.children.empty?
174
+ child.instantiate
175
+ else
176
+ child.instantiate_subtree
177
+ end
178
+ end
179
+ self.val = instantiate
180
+ end
181
+
182
+ # Return a symbol for the constructor method for klass: either :new or the
183
+ # user-defined constructor from the config.
184
+ def konstructor
185
+ return nil if klass.blank?
186
+
187
+ Ydl.class_init(klass.to_s)
188
+ end
189
+ end
190
+ end
@@ -0,0 +1,40 @@
1
+ module Ydl
2
+ # A class for keeping track of dependencies among nodes in a Ydl::Tree caused
3
+ # by the use of cross-references. This class collects those dependencies with
4
+ # #add_dependency and can return a "total ordering" consistent with the
5
+ # dependencies with its #tsort method.
6
+ class TopQueue
7
+ def initialize
8
+ @dependencies = {}
9
+ end
10
+
11
+ def add_dependency(dependent, depends_on)
12
+ depends_on =
13
+ case depends_on
14
+ when Array
15
+ depends_on
16
+ else
17
+ [depends_on]
18
+ end
19
+ @dependencies[dependent] ||= []
20
+ @dependencies[dependent] += depends_on
21
+ # Add an empty dependency for all the depends_on members; the TSort
22
+ # module expects this to indicate that the ref depends on nothing else.
23
+ depends_on.each do |ref|
24
+ @dependencies[ref] = [] unless @dependencies.key?(ref)
25
+ end
26
+ self
27
+ end
28
+
29
+ def print_out
30
+ puts 'Dependencies:'
31
+ pp @dependencies.tsort
32
+ end
33
+
34
+ def topological_xrefs
35
+ @dependencies.tsort
36
+ rescue TSort::Cyclic => e
37
+ raise Ydl::CircularReference, e.to_s
38
+ end
39
+ end
40
+ end
data/lib/ydl/tree.rb ADDED
@@ -0,0 +1,95 @@
1
+ module Ydl
2
+ # The Tree class holds the data read from the .ydl files while any
3
+ # cross-references are being resolved and objects are being instantiated.
4
+ # After all that work is done, its nodes are merged into the main Ydl.data
5
+ # hash to be referenced by the application.
6
+ class Tree
7
+ attr_reader :tree_id, :root, :workq
8
+
9
+ # A new Tree is initialized with a Hash, which is itself a tree structure,
10
+ # but not of instantiated classes of the type we want. The nodes of the
11
+ # input hash will be basic ruby objects such as Strings, Dates, Numerics,
12
+ # and so forth, just as they are read from the ydl files by Psych. Some
13
+ # of the String objects will be in the form of cross-references to other
14
+ # parts of this tree, or of other trees that my not even exist at the time
15
+ # this one is being built.
16
+ def initialize(hsh)
17
+ @tree_id = object_id
18
+ @root = Node.new([], hsh, tree_id: @tree_id)
19
+ # A Queue of unresolved Nodes as a Hash keyed by the path to the dependent
20
+ # nodes with a value of the nodes on which that node depends.
21
+ @workq = Ydl::TopQueue.new
22
+ # Depth-first recursive build and instantiation of root node
23
+ # cross reference paths.
24
+ @root.build_subtree
25
+ instantiate
26
+ end
27
+
28
+ def inspect
29
+ "Tree<#{object_id}> with top-level keys: #{@root.children.keys.join(', ')}"
30
+ end
31
+
32
+ def to_hash
33
+ @root.to_params
34
+ end
35
+
36
+ # Resolution. Note: a 'xref' means a string of the form
37
+ # 'ydl:/path/to/other/object' referencing an object in another part of the
38
+ # root tree. A 'path' is an Array of Symbols such as [:path, :to, :other,
39
+ # :object], which can correspond to an xref and vice-versa. A 'node'
40
+ # means a ruby reference to the object instatiated at some path.
41
+
42
+ # Instantiate nodes in the tree in the order of any cross-references,
43
+ # topologically sorted. That is, instantiate those on which others depend
44
+ # first, and those dependent on earlier nodes last.
45
+ def instantiate
46
+ workq.topological_xrefs.each do |ref|
47
+ node = node_at_xref(ref)
48
+ if node.val.instance_of?(String) && node.val.xref?
49
+ node.val = node_at_xref(node.val).val
50
+ else
51
+ node.instantiate
52
+ end
53
+ end
54
+ @root.instantiate_subtree
55
+ self
56
+ end
57
+
58
+ # Return the Ydl::Node at path in Ydl.data or nil if there is no node at
59
+ # the given path.
60
+ def node_at_path(path)
61
+ node = @root
62
+ partial_path = []
63
+ path.each do |key|
64
+ if node[key].nil?
65
+ xref = Tree.path_to_xref(path)
66
+ pxref = Tree.path_to_xref(partial_path)
67
+ klass = node.klass
68
+ msg = "can\'t resolve cross-ref '#{xref}' beyond #{klass} object '#{pxref}'"
69
+ raise Ydl::BadXRef, msg
70
+ end
71
+ partial_path << key
72
+ node = node[key]
73
+ end
74
+ node
75
+ end
76
+
77
+ # Return the Node referenced by the given xref string
78
+ def node_at_xref(xref)
79
+ node_at_path(Tree.xref_to_path(xref))
80
+ end
81
+
82
+ # Return an Array of symbols representing a the path described by a ydl xref
83
+ # string. Return nil if str is not an xref string.
84
+ def self.xref_to_path(xref)
85
+ match = xref.to_s.clean.match(%r{\Aydl:/(?<path_str>.*)\z})
86
+ return nil unless match
87
+
88
+ match[:path_str].split('/').map(&:to_sym)
89
+ end
90
+
91
+ def self.path_to_xref(path)
92
+ "ydl:/#{path.map(&:to_s).join('/')}"
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,3 @@
1
+ module Ydl
2
+ VERSION = '0.2.06'.freeze
3
+ end