wabur 0.1.0d1 → 0.1.0d2

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: f881d414a328e743c262513bde6977d3a28a3ec3
4
- data.tar.gz: f3d99084f2ad9ccb619ceb3572a4c0bc66863c5f
3
+ metadata.gz: f4ae2c00f65bf680b3432bd10ec6135aeed7e6bc
4
+ data.tar.gz: 01f5f822d40665b8f314d3744ccdcf7bf35c071c
5
5
  SHA512:
6
- metadata.gz: 0b4ca9f500c474908d6a4b740a86ec360f0386332e7d55bd724743124d041983c1854834cbac66a874c73b2a7f1b3c674224c830a4d0b3dee3f50204efe89b3f
7
- data.tar.gz: b71155371f81f098034483486ff036e36ccaee5c460072dc61a7fe3012df97de643c2eea5514c9561f50ace56c5b1685cbde4cd04e3a322d5dd490ce15f71f1a
6
+ metadata.gz: 48320600e036669b67ba89b0b241440891af68ca7f59c287e632a96e3df1512cc1b917c0bb8425d9d4e7040d4c145ccb5ed28d0d3f62b228aceecc40e58f3ad2
7
+ data.tar.gz: 7f83da64ecc5ed24f2bc2e225204f757873fda7f2843634bae74afb7be3dcf497098aa3d2754aa2be7f8ade8535b3096e25b3a7c9429723ead367cc1ed42bcd9
data/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # WABuR (Web Application Builder using Ruby)
2
2
 
3
+ [![Build Status](https://img.shields.io/travis/ohler55/wabur/develop.svg)](http://travis-ci.org/ohler55/wabur?branch=develop) ![Gem](https://img.shields.io/gem/v/wabur.svg) ![Gem](https://img.shields.io/gem/dt/wabur.svg)
4
+
3
5
  Ruby is a great language but for performance C is a better alternative. It is
4
6
  possible to get the best of both as evident with [Oj](http://www.ohler.com/oj)
5
7
  and [Ox](http://www.ohler.com/ox). C by itself allowed
@@ -43,16 +45,28 @@ C shell that handles HTTP and data storage.
43
45
 
44
46
  [Continue reading ...](pages/Architecture.md)
45
47
 
46
- ## Participate
48
+ ## Participate and Contribute
47
49
 
48
50
  If you like the idea and want to help out or become a core developer on the
49
51
  project send me an [email](mailto:peter@ohler.com). Get in on the ground floor
50
52
  and lets make something awesome together.
51
53
 
54
+ ### Guidelines
55
+
56
+ These are the simple guidelines for contrinuting.
57
+
58
+ 1. Coordinate with me first before getting started to avoid duplication of
59
+ effort or implementing something in conflict with the plans.
60
+
61
+ 2. Branch off the develop branch and submit a PR.
62
+
63
+ 3. Write unit tests.
64
+
65
+ 4. Write straight forward, clean, and simple code. No magic stuff, no monkey
66
+ patching Ruby core classes, and no inheriting from core classes.
67
+
52
68
  ## Planning
53
69
 
54
70
  The plan is informal and high level until more details are defined.
55
71
 
56
72
  [Details ...](pages/Plan.md)
57
-
58
-
File without changes
data/lib/wab/data.rb CHANGED
@@ -1,4 +1,6 @@
1
1
 
2
+ require 'uri'
3
+
2
4
  module WAB
3
5
 
4
6
  # The class representing the cananical data structure in WAB. Typically the
@@ -7,21 +9,12 @@ module WAB
7
9
  # class (has the same methods and behavior).
8
10
  class Data
9
11
 
10
- # This method is included only for testing purposes of the Ruby base
11
- # Shell. It should only be called by the Shell. Create a new Data instance
12
- # with the initial value provided. The value must be a Hash or Array. The
13
- # members of the Hash or Array must be nil, boolean, String, Integer,
14
- # Float, BigDecimal, Array, Hash, Time, WAB::UUID, or WAB::IRI.
15
- def initialize(value=nil)
16
- # TBD
17
- end
18
-
19
12
  # Gets the Data element or value identified by the path where the path
20
13
  # elements are separated by the '.' character. The path can also be a
21
14
  # array of path node identifiers. For example, child.grandchild is the
22
15
  # same as ['child', 'grandchild'].
23
16
  def get(path)
24
- # TBD
17
+ raise NotImplementedError.new
25
18
  end
26
19
 
27
20
  # Sets the node value identified by the path where the path elements are
@@ -29,44 +22,80 @@ module WAB
29
22
  # node identifiers. For example, child.grandchild is the same as ['child',
30
23
  # 'grandchild']. The value must be one of the allowed data values
31
24
  # described in the initialize method.
25
+ #
26
+ # For arrays, the behavior is similar to an Array#[] with the exception
27
+ # of a negative index less than the negative length in which case the
28
+ # value is prepended (Array#unshift).
29
+ #
30
+ # path:: path to location to be set
31
+ # value:: value to set
32
+ # repair:: flag indicating invalid value should be repaired if possible
32
33
  def set(path, value)
33
- # TBD
34
+ raise NotImplementedError.new
34
35
  end
35
36
 
36
37
  # Each child of the Data instance is provided as an argument to a block
37
38
  # when the each method is called.
38
39
  def each()
39
- # TBD
40
+ raise NotImplementedError.new
40
41
  end
41
42
 
42
43
  # Each leaf of the Data instance is provided as an argument to a block
43
44
  # when the each method is called. A leaf is a primitive that has no
44
45
  # children and will be nil, a Boolean, String, Numberic, Time, WAB::UUID,
45
- # or WAB::IRI.
46
+ # or URI.
46
47
  def each_leaf()
47
- # TBD
48
+ raise NotImplementedError.new
48
49
  end
49
50
 
50
51
  # Make a deep copy of the Data instance.
51
52
  def clone()
52
- # TBD
53
+ raise NotImplementedError.new
53
54
  end
54
55
 
55
56
  # Returns the instance converted to native Ruby values such as a Hash,
56
57
  # Array, etc.
57
58
  def native()
58
- # TBD
59
+ raise NotImplementedError.new
59
60
  end
60
61
 
61
62
  # Returns true if self and other are either the same or have the same
62
63
  # contents. This is a deep comparison.
63
64
  def eql?(other)
64
- # TBD
65
+ raise NotImplementedError.new
66
+ end
67
+
68
+ # Returns the length of the root element.
69
+ def length()
70
+ raise NotImplementedError.new
71
+ end
72
+
73
+ # Returns the number of leaves in the data tree.
74
+ def leaf_count()
75
+ raise NotImplementedError.new
76
+ end
77
+
78
+ # Returns the number of nodes in the data tree.
79
+ def size()
80
+ raise NotImplementedError.new
65
81
  end
66
82
 
67
83
  # Encode the data as a JSON string.
68
84
  def json(indent=0)
69
- # TBD
85
+ raise NotImplementedError.new
86
+ end
87
+
88
+ private
89
+
90
+ # This method is included only to raise an error if an attempt is made to
91
+ # create an instance directly. The Shell implementation should provide a
92
+ # similar initializer which should create a new Data instance with the
93
+ # initial value provided. The value must be a Hash or Array. The members
94
+ # of the Hash or Array must be nil, boolean, String, Integer, Float,
95
+ # BigDecimal, Array, Hash, Time, URI::HTTP, or WAB::UUID. Keys to Hashes
96
+ # must be Symbols.
97
+ def initialize(value=nil, repair=false)
98
+ raise NotImplementedError.new
70
99
  end
71
100
 
72
101
  end # Data
@@ -0,0 +1,371 @@
1
+
2
+ require 'uri'
3
+ require 'oj'
4
+
5
+ module WAB
6
+ module Impl
7
+
8
+ # The class representing the cananical data structure in WAB. Typically
9
+ # the Data instances are factory created by the Shell and will most likely
10
+ # not be instance of this class but rather a class that is a duck-type of
11
+ # this class (has the same methods and behavior).
12
+ class Data < ::WAB::Data
13
+
14
+ # This method should not be called directly. New instances should be
15
+ # created by using a Shell#data method.
16
+ #
17
+ # Creates a new Data instance with the initial value provided. The value
18
+ # must be a Hash or Array. The members of the Hash or Array must be nil,
19
+ # boolean, String, Integer, Float, BigDecimal, Array, Hash, Time,
20
+ # WAB::UUID, or the Ruby URI::HTTP.
21
+ #
22
+ # value:: initial value
23
+ # repair:: flag indicating invalid value should be repaired if possible
24
+ def initialize(value, repair)
25
+ if repair
26
+ value = fix(value)
27
+ else
28
+ validate(value)
29
+ end
30
+ @root = value
31
+ end
32
+
33
+ # Gets the Data element or value identified by the path where the path
34
+ # elements are separated by the '.' character. The path can also be a
35
+ # array of path node identifiers. For example, child.grandchild is the
36
+ # same as ['child', 'grandchild'].
37
+ def get(path)
38
+ path = path.to_s.split('.') unless path.is_a?(Array)
39
+ node = @root
40
+ path.each { |key|
41
+ if node.is_a?(Hash)
42
+ node = node[key.to_sym]
43
+ elsif node.is_a?(Array)
44
+ i = key.to_i
45
+ if 0 == i && '0' != key && 0 != key
46
+ node = nil
47
+ break
48
+ end
49
+ node = node[i]
50
+ else
51
+ node = nil
52
+ break
53
+ end
54
+ }
55
+ node
56
+ end
57
+
58
+ # Sets the node value identified by the path where the path elements are
59
+ # separated by the '.' character. The path can also be a array of path
60
+ # node identifiers. For example, child.grandchild is the same as ['child',
61
+ # 'grandchild']. The value must be one of the allowed data values
62
+ # described in the initialize method.
63
+ #
64
+ # For arrays, the behavior is similar to an Array#[] with the exception
65
+ # of a negative index less than the negative length in which case the
66
+ # value is prepended (Array#unshift).
67
+ #
68
+ # path:: path to location to be set
69
+ # value:: value to set
70
+ # repair:: flag indicating invalid value should be repaired if possible
71
+ def set(path, value, repair=false)
72
+ raise StandardError.new("path can not be empty.") if path.empty?
73
+ if repair
74
+ value = fix_value(value)
75
+ else
76
+ validate_value(value)
77
+ end
78
+ node = @root
79
+ path = path.to_s.split('.') unless path.is_a?(Array)
80
+ path[0..-2].each { |key|
81
+ if node.is_a?(Hash)
82
+ key = key.to_sym
83
+ node[key] = {} unless node.has_key?(key)
84
+ node = node[key]
85
+ elsif node.is_a?(Array)
86
+ i = key.to_i
87
+ raise StandardError.new("path key must be an integer for an Array.") if (0 == i && '0' != key && 0 != key)
88
+ if i < node.length && -node.length < i
89
+ node = node[i]
90
+ else
91
+ # TBD if next key is a number then make an array instead
92
+ nn = {}
93
+ if i < -node.length
94
+ node.unshift(nn)
95
+ else
96
+ node[i] = nn
97
+ end
98
+ node = nn
99
+ end
100
+ else
101
+ raise StandardError.new("Can not set a member of an #{node.class}.")
102
+ end
103
+ }
104
+ key = path[-1]
105
+ if node.is_a?(Hash)
106
+ key = key.to_sym
107
+ node[key] = value
108
+ elsif node.is_a?(Array)
109
+ i = key.to_i
110
+ raise StandardError.new("path key must be an integer for an Array.") if (0 == i && '0' != key && 0 != key)
111
+ if i < -node.length
112
+ node.unshift(value)
113
+ else
114
+ node[i] = value
115
+ end
116
+ else
117
+ raise StandardError.new("Can not set a member of an #{node.class}.")
118
+ end
119
+ value
120
+ end
121
+
122
+ # Each child of the Data instance is provided as an argument to a block
123
+ # when the each method is called.
124
+ def each(&block)
125
+ each_node([], @root, block)
126
+ end
127
+
128
+ # Each leaf of the Data instance is provided as an argument to a block
129
+ # when the each method is called. A leaf is a primitive that has no
130
+ # children and will be nil, a Boolean, String, Numberic, Time, WAB::UUID,
131
+ # or URI.
132
+ def each_leaf(&block)
133
+ each_leaf_node([], @root, block)
134
+ end
135
+
136
+ # Make a deep copy of the Data instance.
137
+ def clone()
138
+ # avoid validation by using a empty Hash for the intial value.
139
+ c = self.class.new({}, false)
140
+ c.instance_variable_set(:@root, clone_value(@root))
141
+ c
142
+ end
143
+
144
+ # Returns the instance converted to native Ruby values such as a Hash,
145
+ # Array, etc.
146
+ def native()
147
+ @root
148
+ end
149
+
150
+ # Returns true if self and other are either the same or have the same
151
+ # contents. This is a deep comparison.
152
+ def eql?(other)
153
+ # Any object that is of a class derived from the API class is a
154
+ # candidate for being ==.
155
+ return false unless other.is_a?(::WAB::Data)
156
+ values_eql?(@root, other.native)
157
+ end
158
+ alias == eql?
159
+
160
+ # Returns the length of the root element.
161
+ def length()
162
+ @root.length
163
+ end
164
+
165
+ # Returns the number of leaves in the data tree.
166
+ def leaf_count()
167
+ branch_count(@root)
168
+ end
169
+
170
+ # Returns the number of nodes in the data tree.
171
+ def size()
172
+ branch_size(@root)
173
+ end
174
+
175
+ # Encode the data as a JSON string.
176
+ def json(indent=0)
177
+ Oj.dump(@root, mode: :wab, indent: indent)
178
+ end
179
+
180
+ private
181
+
182
+ # Raise an exception if the value is not a suitable data element. If the
183
+ # repair flag is true an attempt is made to fix the value if possible by
184
+ # replacing non-symbol keys with Symbols and converting unsupported
185
+ # objects with a to_h or a to_s.
186
+ #
187
+ # value:: value to validate
188
+ def validate(value)
189
+ if value.is_a?(Hash)
190
+ value.each_pair { |k, v|
191
+ raise StandardError.new("Hash keys must be Symbols.") unless k.is_a?(Symbol)
192
+ validate_value(v)
193
+ }
194
+ elsif value.is_a?(Array)
195
+ value.each { |v|
196
+ validate_value(v)
197
+ }
198
+ else
199
+ raise StandardError.new("Data values must be either a Hash or an Array")
200
+ end
201
+ value
202
+ end
203
+
204
+ def validate_value(value)
205
+ value_class = value.class
206
+ if value.nil? ||
207
+ TrueClass == value_class ||
208
+ FalseClass == value_class ||
209
+ Integer == value_class ||
210
+ Float == value_class ||
211
+ String == value_class ||
212
+ Time == value_class ||
213
+ BigDecimal == value_class ||
214
+ URI::HTTP == value_class ||
215
+ ::WAB::UUID == value_class
216
+ # valid values
217
+ elsif Hash == value_class
218
+ value.each_pair { |k, v|
219
+ raise StandardError.new("Hash keys must be Symbols.") unless k.is_a?(Symbol)
220
+ validate_value(v)
221
+ }
222
+ elsif Array == value_class
223
+ value.each { |v|
224
+ validate_value(v)
225
+ }
226
+ else
227
+ raise StandardError.new("#{value_class.to_s} is not a valid Data value.")
228
+ end
229
+ value
230
+ end
231
+
232
+ # Fix values by returing either the value or the fixed alternative. In
233
+ # the cases of Hash and Array a copy is always made. (its just easier)
234
+ def fix(value)
235
+ if value.is_a?(Hash)
236
+ old = value
237
+ value = {}
238
+ old.each_pair { |k, v|
239
+ k = k.to_sym unless k.is_a?(Symbol)
240
+ value[k] = fix_value(v)
241
+ }
242
+ elsif value.is_a?(Array)
243
+ old = value
244
+ value = []
245
+ old.each { |v|
246
+ value << fix_value(v)
247
+ }
248
+ elsif value.respond_to?(:to_h) && 0 == value.method(:to_h).arity
249
+ value = value.to_h
250
+ raise StandardError.new("Data values must be either a Hash or an Array") unless value.is_a?(Hash)
251
+ value = fix(value)
252
+ else
253
+ raise StandardError.new("Data values must be either a Hash or an Array")
254
+ end
255
+ value
256
+ end
257
+
258
+ def fix_value(value)
259
+ value_class = value.class
260
+ if value.nil? ||
261
+ TrueClass == value_class ||
262
+ FalseClass == value_class ||
263
+ Integer == value_class ||
264
+ Float == value_class ||
265
+ String == value_class ||
266
+ Time == value_class ||
267
+ BigDecimal == value_class ||
268
+ URI::HTTP == value_class ||
269
+ ::WAB::UUID == value_class
270
+ # valid values
271
+ elsif Hash == value_class
272
+ old = value
273
+ value = {}
274
+ old.each_pair { |k, v|
275
+ k = k.to_sym unless k.is_a?(Symbol)
276
+ value[k] = fix_value(v)
277
+ }
278
+ elsif Array == value_class
279
+ old = value
280
+ value = []
281
+ old.each { |v|
282
+ value << fix_value(v)
283
+ }
284
+ elsif value.respond_to?(:to_h) && 0 == value.method(:to_h).arity
285
+ value = value.to_h
286
+ raise StandardError.new("Data values must be either a Hash or an Array") unless value.is_a?(Hash)
287
+ value = fix(value)
288
+ elsif value.respond_to?(:to_s)
289
+ value = value.to_s
290
+ raise StandardError.new("Data values must be either a Hash or an Array") unless value.is_a?(String)
291
+ else
292
+ raise StandardError.new("#{value_class.to_s} is not a valid Data value.")
293
+ end
294
+ value
295
+ end
296
+
297
+ def each_node(path, value, block)
298
+ block.call(path, value)
299
+ if value.is_a?(Hash)
300
+ value.each_pair { |k, v| each_node(path + [k], v, block) }
301
+ elsif value.is_a?(Array)
302
+ value.each_index { |i| each_node(path + [i], value[i], block) }
303
+ end
304
+ end
305
+
306
+ def each_leaf_node(path, value, block)
307
+ if value.is_a?(Hash)
308
+ value.each_pair { |k, v| each_leaf_node(path + [k], v, block) }
309
+ elsif value.is_a?(Array)
310
+ value.each_index { |i| each_leaf_node(path + [i], value[i], block) }
311
+ else
312
+ block.call(path, value)
313
+ end
314
+ end
315
+
316
+ def branch_count(value)
317
+ cnt = 0
318
+ if value.is_a?(Hash)
319
+ value.each_value { |v| cnt += branch_count(v) }
320
+ elsif value.is_a?(Array)
321
+ value.each { |v| cnt += branch_count(v) }
322
+ else
323
+ cnt = 1
324
+ end
325
+ cnt
326
+ end
327
+
328
+ def branch_size(value)
329
+ cnt = 1
330
+ if value.is_a?(Hash)
331
+ value.each_value { |v| cnt += branch_size(v) }
332
+ elsif value.is_a?(Array)
333
+ value.each { |v| cnt += branch_size(v) }
334
+ end
335
+ cnt
336
+ end
337
+
338
+ def values_eql?(v0, v1)
339
+ return false unless v0.class == v1.class
340
+ if v0.is_a?(Hash)
341
+ return false unless v0.length == v1.length
342
+ v0.each_key { |k|
343
+ return false unless values_eql?(v0[k], v1[k])
344
+ return false unless v1.has_key?(k)
345
+ }
346
+ elsif v0.is_a?(Array)
347
+ return false unless v0.length == v1.length
348
+ v0.each_index { |i|
349
+ return false unless values_eql?(v0[i], v1[i])
350
+ }
351
+ else
352
+ v0 == v1
353
+ end
354
+ end
355
+
356
+ def clone_value(value)
357
+ if value.is_a?(Hash)
358
+ c = {}
359
+ value.each_pair { |k, v| c[k] = clone_value(v) }
360
+ elsif value.is_a?(Array)
361
+ c = []
362
+ value.each { |v| c << clone_value(v) }
363
+ else
364
+ c = value.clone
365
+ end
366
+ c
367
+ end
368
+
369
+ end # Data
370
+ end # Impl
371
+ end # WAB
@@ -0,0 +1,33 @@
1
+
2
+ require 'wab'
3
+
4
+ module WAB
5
+
6
+ module Impl
7
+
8
+ # The shell for reference Ruby implementation.
9
+ class Shell < ::WAB::Shell
10
+
11
+ # Sets up the shell with a view, model, and type_key.
12
+ def initialize(view, model, type_key='kind')
13
+ super
14
+ end
15
+
16
+ # Create and return a new data instance with the provided initial value.
17
+ # The value must be a Hash or Array. The members of the Hash or Array
18
+ # must be nil, boolean, String, Integer, Float, BigDecimal, Array, Hash,
19
+ # Time, URI::HTTP, or WAB::UUID. Keys to Hashes must be Symbols.
20
+ #
21
+ # If the repair flag is true then an attempt will be made to fix the
22
+ # value by replacing String keys with Symbols and calling to_h or to_s
23
+ # on unsupported Objects.
24
+ #
25
+ # value:: initial value
26
+ # repair:: flag indicating invalid value should be repaired if possible
27
+ def data(value={}, repair=false)
28
+ Data.new(value, repair)
29
+ end
30
+
31
+ end # Shell
32
+ end # Impl
33
+ end # WAB
data/lib/wab/impl.rb ADDED
@@ -0,0 +1,11 @@
1
+
2
+ require 'wab'
3
+
4
+ module WAB
5
+ # Web Application Builder implementation of the WAB APIs.
6
+ module Impl
7
+ end
8
+ end
9
+
10
+ require 'wab/impl/data'
11
+ require 'wab/impl/shell'
data/lib/wab/model.rb CHANGED
File without changes
data/lib/wab/shell.rb CHANGED
@@ -42,5 +42,20 @@ module WAB
42
42
  @controllers[type] = controller
43
43
  end
44
44
 
45
+ # Create and return a new data instance with the provided initial value.
46
+ # The value must be a Hash or Array. The members of the Hash or Array must
47
+ # be nil, boolean, String, Integer, Float, BigDecimal, Array, Hash, Time,
48
+ # URI::HTTP, or WAB::UUID. Keys to Hashes must be Symbols.
49
+ #
50
+ # If the repair flag is true then an attempt will be made to fix the value
51
+ # by replacing String keys with Symbols and calling to_h or to_s on
52
+ # unsupported Objects.
53
+ #
54
+ # value:: initial value
55
+ # repair:: flag indicating invalid value should be repaired if possible
56
+ def data(value=nil, repair=false)
57
+ raise NotImplementedError.new
58
+ end
59
+
45
60
  end # Shell
46
61
  end # WAB
data/lib/wab/uuid.rb ADDED
@@ -0,0 +1,28 @@
1
+
2
+ module WAB
3
+
4
+ # The UUID class representing a 128 bit UUID although values are not
5
+ # validated for conformane to the ISO/IEC specifications.
6
+ class UUID
7
+
8
+ attr_reader :id
9
+
10
+ # Initializes a UUID from string representation of the UUID
11
+ # following the pattern "123e4567-e89b-12d3-a456-426655440000".
12
+ def initialize(id)
13
+ @id = id.downcase
14
+ # TBD change to WAB exception
15
+ raise Exception.new("Invalid UUID format.") if /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.match(@id).nil?
16
+ end
17
+
18
+ # Returns the string representation of the UUID.
19
+ def to_s
20
+ @id
21
+ end
22
+
23
+ def ==(other)
24
+ other.is_a?(self.class) && @id == other.id
25
+ end
26
+
27
+ end # UUID
28
+ end # WAB
data/lib/wab/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
 
2
2
  module WAB
3
3
  # Current version of the module.
4
- VERSION = '0.1.0d1'
4
+ VERSION = '0.1.0d2'
5
5
  end
data/lib/wab/view.rb CHANGED
File without changes
data/lib/wab.rb CHANGED
@@ -3,3 +3,8 @@
3
3
  module WAB
4
4
  end
5
5
 
6
+ require 'wab/data'
7
+ require 'wab/model'
8
+ require 'wab/shell'
9
+ require 'wab/uuid'
10
+ require 'wab/version'
data/pages/Plan.md CHANGED
@@ -1,11 +1,21 @@
1
1
  # Plan
2
2
 
3
- More details will be added here as progress continues. For now the hight level plan is:
3
+ More details will be added here as progress continues. For now the high level plan is:
4
+
5
+ - _Define APIs - initial complete_
6
+
7
+ - Start the WAB::Dev module
8
+ - WAB::Dev::Data class
9
+ - WAB::Dev::UUID class
10
+ - WAB::Dev::Model class
11
+ - WAB::Dev::Shell class
12
+ - WAB::Dev::View class
13
+ - WAB::Dev::Controller class
14
+
15
+ - Extern Glue implemenation and test
16
+ - this is in the WAB module
4
17
 
5
- - Define APIs
6
- - View API
7
- - Model/Store API
8
18
  - Structure Ruby Framework
9
- - Embedded Glue layer
10
- - External Glue layer
19
+ - Embedded Glue layer
20
+ - External Glue layer
11
21
 
data/test/data_test.rb ADDED
@@ -0,0 +1,254 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ $: << File.dirname(__FILE__)
5
+ $: << File.join(File.dirname(File.dirname(File.expand_path(__FILE__))), 'lib')
6
+ $: << File.join(File.dirname(File.dirname(File.dirname(File.expand_path(__FILE__)))), 'oj', 'ext')
7
+ $: << File.join(File.dirname(File.dirname(File.dirname(File.expand_path(__FILE__)))), 'oj', 'lib')
8
+
9
+ require 'minitest'
10
+ require 'minitest/autorun'
11
+
12
+ require 'wab'
13
+ require 'wab/impl'
14
+
15
+ $shell = ::WAB::Impl::Shell.new(nil, nil) if $shell.nil?
16
+
17
+ class DataTest < Minitest::Test
18
+
19
+ class ToHash
20
+ def initialize(x, y)
21
+ @h = {x: x, y: y }
22
+ end
23
+ def to_h
24
+ @h
25
+ end
26
+ end
27
+
28
+ def test_json
29
+ d = $shell.data({
30
+ boo: true,
31
+ n: nil,
32
+ num: 7,
33
+ float: 7.654,
34
+ str: 'a string',
35
+ t: Time.gm(2017, 1, 5, 15, 4, 33.123456789),
36
+ big: BigDecimal('63.21'),
37
+ uri: URI('http://opo.technology/sample'),
38
+ uuid: ::WAB::UUID.new('b0ca922d-372e-41f4-8fea-47d880188ba3'),
39
+ a: [],
40
+ h: {},
41
+ })
42
+ assert_equal(%|{
43
+ "boo":true,
44
+ "n":null,
45
+ "num":7,
46
+ "float":7.654,
47
+ "str":"a string",
48
+ "t":"2017-01-05T15:04:33.123456789Z",
49
+ "big":0.6321e2,
50
+ "uri":"http://opo.technology/sample",
51
+ "uuid":"b0ca922d-372e-41f4-8fea-47d880188ba3",
52
+ "a":[],
53
+ "h":{}
54
+ }
55
+ |, d.json(2))
56
+ end
57
+
58
+ def test_validate_keys
59
+ assert_raises() { d = $shell.data({ 'a' => 1}) }
60
+ end
61
+
62
+ def test_repair_keys
63
+ d = $shell.data({ 'a' => 1}, true)
64
+ assert_equal({a:1}, d.native, "data not repaired")
65
+ end
66
+
67
+ def test_validate_non_hash_array
68
+ assert_raises() { d = $shell.data(123) }
69
+ end
70
+
71
+ def test_fix_non_hash_array
72
+ # can not fix this one
73
+ assert_raises() { d = $shell.data(123, true) }
74
+ end
75
+
76
+ def test_validate_object
77
+ assert_raises() { d = $shell.data({a: 1..3}) }
78
+ end
79
+
80
+ def test_repair_to_s_object
81
+ d = $shell.data({a: 1..3}, true)
82
+ assert_equal({a:'1..3'}, d.native, "data not repaired")
83
+ end
84
+
85
+ def test_repair_to_h_object
86
+ d = $shell.data(ToHash.new(1, 2), true)
87
+ assert_equal({x:1,y:2}, d.native, "data not repaired")
88
+ end
89
+
90
+ def test_hash_get
91
+ d = $shell.data({a: 1, b: 2})
92
+ assert_equal(2, d.get('b'), "failed to get 'b'")
93
+ assert_equal(2, d.get([:b]), "failed to get [:b]")
94
+ assert_equal(1, d.get(['a']), "failed to get ['a']")
95
+ assert_nil(d.get(['d']), "failed to get ['d']")
96
+ end
97
+
98
+ def test_array_get
99
+ d = $shell.data(['a', 'b', 'c'])
100
+ assert_equal('b', d.get('1'), "failed to get '1'")
101
+ assert_equal('c', d.get(['2']), "failed to get ['2']")
102
+ assert_equal('b', d.get([1]), "failed to get [1]")
103
+ assert_equal('a', d.get([0]), "failed to get [0]")
104
+ assert_equal('c', d.get([-1]), "failed to get [-1]")
105
+ assert_nil(d.get([4]), "failed to get [4]")
106
+ end
107
+
108
+ def test_get_mixed
109
+ d = $shell.data({a: 1, b: ['a', 'b', { c: 3}]})
110
+ assert_equal(3, d.get('b.2.c'), "failed to get 'b.2.c'")
111
+ assert_equal(3, d.get([:b, 2, 'c']), "failed to get [b, 2, c]")
112
+ end
113
+
114
+ def test_hash_set
115
+ d = $shell.data({a: 1})
116
+ d.set('b', 2)
117
+ d.set([:c], 3)
118
+ d.set([:d], 1..4, true)
119
+ assert_raises() { d.set([:e], 1..5) }
120
+ assert_equal(%|{
121
+ "a":1,
122
+ "b":2,
123
+ "c":3,
124
+ "d":"1..4"
125
+ }
126
+ |, d.json(2))
127
+ # replace existing
128
+ d.set([:c], -3)
129
+ assert_equal(%|{
130
+ "a":1,
131
+ "b":2,
132
+ "c":-3,
133
+ "d":"1..4"
134
+ }
135
+ |, d.json(2))
136
+ end
137
+
138
+ def test_array_set
139
+ d = $shell.data(['x'])
140
+ d.set([2], 'd')
141
+ assert_equal(%|["x",null,"d"]|, d.json(), "after d")
142
+ d.set([1], 'c')
143
+ assert_equal(%|["x","c","d"]|, d.json(), "after c")
144
+ d.set([0], 'y')
145
+ assert_equal(%|["y","c","d"]|, d.json(), "after y")
146
+ d.set([-3], 'b')
147
+ assert_equal(%|["b","c","d"]|, d.json(), "after b")
148
+ d.set([-4], 'a')
149
+ assert_equal(%|["a","b","c","d"]|, d.json(), "after a")
150
+ end
151
+
152
+ def test_set_mixed
153
+ d = $shell.data({a: 1, b: ['a', 'b', { c: 3}]})
154
+ d.set('b.2', 'c')
155
+ assert_equal(%|{"a":1,"b":["a","b","c"]}|, d.json())
156
+ end
157
+
158
+ def test_hash_length
159
+ d = $shell.data({a: 1, b: 2})
160
+ assert_equal(2, d.length())
161
+ end
162
+
163
+ def test_array_length
164
+ d = $shell.data(['a', 'b', 'c'])
165
+ assert_equal(3, d.length())
166
+ end
167
+
168
+ def test_mixed_length
169
+ d = $shell.data({a: 1, b: ['a', 'b', { c: 3}]})
170
+ assert_equal(2, d.length())
171
+ end
172
+
173
+ def test_empty_leaf_count
174
+ d = $shell.data()
175
+ assert_equal(0, d.leaf_count())
176
+ end
177
+
178
+ def test_hash_leaf_count
179
+ d = $shell.data({a: 1, b: 2, c:{}})
180
+ assert_equal(2, d.leaf_count())
181
+ end
182
+
183
+ def test_array_leaf_count
184
+ d = $shell.data(['a', 'b', 'c', []])
185
+ assert_equal(3, d.leaf_count())
186
+ end
187
+
188
+ def test_mixed_leaf_count
189
+ d = $shell.data({a: 1, b: ['a', 'b', { c: 3}]})
190
+ assert_equal(4, d.leaf_count())
191
+ end
192
+
193
+ def test_empty_size
194
+ d = $shell.data()
195
+ assert_equal(1, d.size())
196
+ end
197
+
198
+ def test_hash_size
199
+ d = $shell.data({a: 1, b: 2, c:{}})
200
+ assert_equal(4, d.size())
201
+ end
202
+
203
+ def test_array_size
204
+ d = $shell.data(['a', 'b', 'c', []])
205
+ assert_equal(5, d.size())
206
+ end
207
+
208
+ def test_mixed_size
209
+ d = $shell.data({a: 1, b: ['a', 'b', { c: 3}]})
210
+ assert_equal(7, d.size())
211
+ end
212
+
213
+ def test_each
214
+ d = $shell.data({a: 1, b: ['a', 'b', { c: 3}]})
215
+ paths = []
216
+ values = []
217
+ d.each { |p, v|
218
+ paths << p
219
+ values << v
220
+ }
221
+ assert_equal([[], [:a], [:b], [:b, 0], [:b, 1], [:b, 2], [:b, 2, :c]], paths, "paths mismatch")
222
+ assert_equal([{:a=>1, :b=>["a", "b", {:c=>3}]}, 1, ["a", "b", {:c=>3}], "a", "b", {:c=>3}, 3], values, "values mismatch")
223
+ end
224
+
225
+ def test_each_leaf
226
+ d = $shell.data({a: 1, b: ['a', 'b', { c: 3}]})
227
+ paths = []
228
+ values = []
229
+ d.each_leaf { |p, v|
230
+ paths << p
231
+ values << v
232
+ }
233
+ assert_equal([[:a], [:b, 0], [:b, 1], [:b, 2, :c]], paths, "paths mismatch")
234
+ assert_equal([1, "a", "b", 3], values, "values mismatch")
235
+ end
236
+
237
+ def test_eql
238
+ d = $shell.data({a: 1, b: ['a', 'b', { c: 3}]})
239
+ d2 = $shell.data({a: 1, b: ['a', 'b', { c: 3}]})
240
+ d3 = $shell.data({a: 1, b: ['a', 'b', { d: 3}]})
241
+ d4 = $shell.data({a: 1, b: ['a', 'b', { c: 4}]})
242
+
243
+ assert(d == d2, "same keys and values should be eql")
244
+ assert(!(d == d3), "same values different keys should not be eql")
245
+ assert(!(d == d4), "same keys different values should not be eql")
246
+ end
247
+
248
+ def test_clone
249
+ d = $shell.data({a: 1, b: ['a', 'b', { c: 3}]})
250
+ c = d.clone
251
+ assert(d == c)
252
+ end
253
+
254
+ end # DataTest
data/test/impl_test.rb ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ $: << File.dirname(__FILE__)
5
+ $: << File.join(File.dirname(File.dirname(File.expand_path(__FILE__))), 'lib')
6
+
7
+ require 'minitest'
8
+ require 'minitest/autorun'
9
+
10
+ require 'wab/impl'
11
+
12
+ $shell = ::WAB::Impl::Shell.new(nil, nil)
13
+
14
+ require 'data_test'
metadata CHANGED
@@ -1,15 +1,71 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: wabur
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0d1
4
+ version: 0.1.0d2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter Ohler
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-06-03 00:00:00.000000000 Z
12
- dependencies: []
11
+ date: 2017-07-07 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: oj
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 3.3.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 3.3.0
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
+ - !ruby/object:Gem::Dependency
42
+ name: rake-compiler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '0.9'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '0.9'
55
+ - !ruby/object:Gem::Dependency
56
+ name: minitest
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '5'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '5'
13
69
  description: 'Web Application Builder '
14
70
  email: peter@ohler.com
15
71
  executables: []
@@ -17,21 +73,27 @@ extensions: []
17
73
  extra_rdoc_files:
18
74
  - README.md
19
75
  - pages/Architecture.md
20
- - pages/Goals.md
21
76
  - pages/Plan.md
77
+ - pages/Goals.md
22
78
  files:
23
79
  - LICENSE
24
80
  - README.md
25
81
  - lib/wab.rb
26
82
  - lib/wab/controller.rb
27
83
  - lib/wab/data.rb
84
+ - lib/wab/impl.rb
85
+ - lib/wab/impl/data.rb
86
+ - lib/wab/impl/shell.rb
28
87
  - lib/wab/model.rb
29
88
  - lib/wab/shell.rb
89
+ - lib/wab/uuid.rb
30
90
  - lib/wab/version.rb
31
91
  - lib/wab/view.rb
32
92
  - pages/Architecture.md
33
93
  - pages/Goals.md
34
94
  - pages/Plan.md
95
+ - test/data_test.rb
96
+ - test/impl_test.rb
35
97
  homepage: http://github.com/ohler55/wabur
36
98
  licenses:
37
99
  - MIT
@@ -61,4 +123,6 @@ rubygems_version: 2.6.11
61
123
  signing_key:
62
124
  specification_version: 4
63
125
  summary: Web Application Builder
64
- test_files: []
126
+ test_files:
127
+ - test/impl_test.rb
128
+ - test/data_test.rb