ydl 0.2.06

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 (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