wabur 0.1.0d2 → 0.2.0d1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +19 -16
- data/lib/wab/controller.rb +155 -106
- data/lib/wab/data.rb +8 -0
- data/lib/wab/errors.rb +13 -0
- data/lib/wab/impl/data.rb +49 -18
- data/lib/wab/impl/shell.rb +1 -1
- data/lib/wab/io/call.rb +23 -0
- data/lib/wab/io/engine.rb +118 -0
- data/lib/wab/io/shell.rb +141 -0
- data/lib/wab/io.rb +13 -0
- data/lib/wab/shell.rb +93 -16
- data/lib/wab/version.rb +1 -1
- data/lib/wab.rb +2 -1
- data/test/data_test.rb +6 -7
- data/test/impl_test.rb +1 -4
- data/test/ioshell_test.rb +461 -0
- data/test/tests.rb +12 -0
- metadata +13 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 86b81a950a3090451b79d8d4841b4acaa340f208
|
4
|
+
data.tar.gz: 2ac14502f2dabdd7f294be9eeae7c16f068ffadb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 16411e53df75d17339ecdf1efe6b6afc84945c01a71ce2ff2f34a83a3a8e965ba73177286afaf273543711cc5a4646b9742c166699e2f01e80cb813cfa20a32f
|
7
|
+
data.tar.gz: 8f9ca7584c9395e39ccabaab4e0229b0f4e306651adaea727efdf4460dac1ed45e21df33073b54cdd09a3992c4ba35a0f6adf10fe496ce5a82b85cf9c9bf703e
|
data/README.md
CHANGED
@@ -1,31 +1,34 @@
|
|
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)
|
3
|
+
[![Build Status](https://img.shields.io/travis/ohler55/wabur/develop.svg)](http://travis-ci.org/ohler55/wabur?branch=develop)
|
4
|
+
[![Windows Build status](https://img.shields.io/appveyor/ci/ohler55/wabur/develop.svg?label=Windows%20build)](https://ci.appveyor.com/project/ohler55/wabur/branch/develop)
|
5
|
+
[![Gem Version](https://badge.fury.io/rb/wabur.svg)](https://rubygems.org/gems/wabur)
|
6
|
+
[![Gem](https://img.shields.io/gem/dt/wabur.svg)](https://rubygems.org/gems/wabur)
|
4
7
|
|
5
|
-
Ruby is a great language but for performance C is
|
8
|
+
Ruby is a great language but for performance C is the better alternative. It is
|
6
9
|
possible to get the best of both as evident with [Oj](http://www.ohler.com/oj)
|
7
10
|
and [Ox](http://www.ohler.com/ox). C by itself allowed
|
8
|
-
[Piper](http://piperpushcache.com), a fast push web server to be developed and
|
11
|
+
[Piper](http://piperpushcache.com), a fast push web server to be developed, and
|
9
12
|
is being used to develop [OpO](http://opo.technology) a high performance graph
|
10
|
-
and JSON database. This project takes from all of those projects for a
|
13
|
+
and JSON database. This project takes from all of those projects for a high
|
11
14
|
performance Ruby web framework.
|
12
15
|
|
13
|
-
Ruby on Rails has made Ruby
|
16
|
+
Ruby on Rails has made Ruby mainstream. While RoR is fine for some
|
14
17
|
applications there are others that might be better served with an alternative.
|
15
18
|
This project was started as an alternative to Ruby on Rails with a focus on
|
16
|
-
performance and
|
19
|
+
performance and ease of use.
|
17
20
|
|
18
|
-
Why develop an alternative to Rails?
|
19
|
-
still huge but not as popular as it used to be. RoR is not going away
|
20
|
-
soon but for some applications alternatives are needed.
|
21
|
+
Why develop an alternative to Rails? The popularity of Rails has been waning.
|
22
|
+
It is still huge but not as popular as it used to be. RoR is not going away
|
23
|
+
any time soon but for some applications, alternatives are needed.
|
21
24
|
|
22
25
|
## Goals
|
23
26
|
|
24
|
-
Lets start with the assumption that we want to continue
|
25
|
-
of this project is to provide a high performance, easy to use, and fully
|
27
|
+
Lets start with the assumption that we want to continue using Ruby. The goal
|
28
|
+
of this project is to provide a high performance, easy to use, and a fully
|
26
29
|
featured web framework with Ruby at the core. By keeping the core, the
|
27
|
-
business logic in Ruby but allowing options for other parts to be in different
|
28
|
-
languages the best
|
30
|
+
business logic, in Ruby but allowing options for other parts to be in different
|
31
|
+
languages, the best of each can be utilized.
|
29
32
|
|
30
33
|
Targets are a throughput of 100K page fetches per second at a latency of no
|
31
34
|
more than 1 millisecond on a desktop machine. That is more than an order of
|
@@ -47,13 +50,13 @@ C shell that handles HTTP and data storage.
|
|
47
50
|
|
48
51
|
## Participate and Contribute
|
49
52
|
|
50
|
-
If you like the idea and want to help out or become a core developer on the
|
51
|
-
project send me an [email](mailto:peter@ohler.com). Get in on the ground floor
|
53
|
+
If you like the idea and want to help out, or become a core developer on the
|
54
|
+
project, send me an [email](mailto:peter@ohler.com). Get in on the ground floor
|
52
55
|
and lets make something awesome together.
|
53
56
|
|
54
57
|
### Guidelines
|
55
58
|
|
56
|
-
These are the simple guidelines for
|
59
|
+
These are the simple guidelines for contributing.
|
57
60
|
|
58
61
|
1. Coordinate with me first before getting started to avoid duplication of
|
59
62
|
effort or implementing something in conflict with the plans.
|
data/lib/wab/controller.rb
CHANGED
@@ -7,29 +7,22 @@ module WAB
|
|
7
7
|
#
|
8
8
|
# A description of the available methods is included as private methods.
|
9
9
|
class Controller # :doc: all
|
10
|
-
|
11
|
-
attribute_accessor :view
|
12
|
-
attribute_accessor :model
|
10
|
+
attr_accessor :shell
|
13
11
|
|
14
12
|
# Create a instance.
|
15
|
-
def initialize()
|
16
|
-
@shell =
|
17
|
-
|
18
|
-
@model = shell.model
|
19
|
-
# TBD
|
13
|
+
def initialize(shell, async=false)
|
14
|
+
@shell = shell
|
15
|
+
# TBD handle async
|
20
16
|
end
|
21
17
|
|
22
|
-
# Handler for paths that do not match the REST pattern
|
23
|
-
# default controller.
|
18
|
+
# Handler for paths that do not match the REST pattern or for unregistered
|
19
|
+
# types. Only called on the default controller.
|
24
20
|
#
|
25
21
|
# Processing result are passed back to the view which forward the result
|
26
|
-
# on to the requester. The result,
|
22
|
+
# on to the requester. The result, if not nil, should be a Data instance.
|
27
23
|
#
|
28
|
-
# path:: identifies operation and additional data in a / delimited
|
29
|
-
# format. It is up to the controller to decide how to process the
|
30
|
-
# request.
|
31
24
|
# data:: data to be processed
|
32
|
-
def handle(
|
25
|
+
def handle(data)
|
33
26
|
nil
|
34
27
|
end
|
35
28
|
|
@@ -38,63 +31,89 @@ module WAB
|
|
38
31
|
# they will not be called.
|
39
32
|
private
|
40
33
|
|
41
|
-
#
|
34
|
+
# Create a new data object. If a query is provided it is treated as a
|
35
|
+
# check against an existing object with the same key/value pairs.
|
42
36
|
#
|
43
|
-
# The
|
44
|
-
# +with_data+ is true a Data object with an +id+ attribute and a +data+
|
45
|
-
# attribute that contains the full object details.
|
37
|
+
# The reference to the object created is returned on success.
|
46
38
|
#
|
47
39
|
# On error an Exception should be raised.
|
48
40
|
#
|
41
|
+
# path:: array of tokens in the path.
|
42
|
+
# query:: query parameters from a URL.
|
49
43
|
# data:: the data to use as a new object.
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
# of the +with_id+ argument.
|
62
|
-
#
|
63
|
-
# id:: identifier of the object
|
64
|
-
# with_id:: if true wrap the object data with an envelope that includes
|
65
|
-
# the id as well as the object data.
|
66
|
-
def read(id, with_id=false) # :doc:
|
67
|
-
# TBD implement the default behavior as an example or starting point
|
44
|
+
def create(path, query, data) # :doc:
|
45
|
+
tql = { }
|
46
|
+
kind = path[@shell.path_pos]
|
47
|
+
if query.is_a?(Hash) && 0 < query.size
|
48
|
+
where = ['AND']
|
49
|
+
where << form_where_eq(@shell.type_key, kind)
|
50
|
+
query.each_pair { |k,v| where << form_where_eq(k, v) }
|
51
|
+
tql[:where] = where
|
52
|
+
end
|
53
|
+
tql[:insert] = data.native
|
54
|
+
shell_query(tql, kind, 'create')
|
68
55
|
end
|
69
56
|
|
70
|
-
#
|
71
|
-
#
|
72
|
-
#
|
73
|
-
#
|
74
|
-
#
|
75
|
-
#
|
76
|
-
#
|
77
|
-
#
|
78
|
-
#
|
79
|
-
#
|
80
|
-
|
81
|
-
|
57
|
+
# Return the objects according to the path and query arguments.
|
58
|
+
#
|
59
|
+
# If the path includes an object reference then that object is returned as
|
60
|
+
# the only member of the results list of a WAB::Data returned.
|
61
|
+
#
|
62
|
+
# If there is no object reference in the path then the attributes are used
|
63
|
+
# to find matching objects. The objects that have the same key/value pairs
|
64
|
+
# are returned.
|
65
|
+
#
|
66
|
+
# path:: array of tokens in the path.
|
67
|
+
# query:: query parameters from a URL as a Hash with keys matching paths
|
68
|
+
# into the target objects and value equal to the target attribute
|
69
|
+
# values. A path can be an array of keys used to walk a path to
|
70
|
+
# the target or a +.+ delimited set of keys.
|
71
|
+
def read(path, query) # :doc:
|
72
|
+
if @shell.path_pos + 2 == path.length # has an object reference in the path
|
73
|
+
ref = path[@shell.path_pos + 1].to_i
|
74
|
+
obj = @shell.get(ref)
|
75
|
+
obj = obj.native if obj.is_a?(::WAB::Data)
|
76
|
+
return @shell.data({ code: 0, results: [ { id: ref, data: obj } ]})
|
77
|
+
end
|
78
|
+
tql = { }
|
79
|
+
kind = path[@shell.path_pos]
|
80
|
+
# No id so must be either a simple query by attribute or a list.
|
81
|
+
if query.is_a?(Hash) && 0 < query.size
|
82
|
+
where = ['AND']
|
83
|
+
where << form_where_eq(@shell.type_key, kind)
|
84
|
+
query.each_pair { |k,v| where << form_where_eq(k, v) }
|
85
|
+
else
|
86
|
+
where = form_where_eq(@shell.type_key, kind)
|
87
|
+
end
|
88
|
+
tql[:where] = where
|
89
|
+
tql[:select] = { id: '$ref', data: '$' }
|
90
|
+
shell_query(tql, kind, 'read')
|
82
91
|
end
|
83
92
|
|
84
93
|
# Replaces the object data for the identified object.
|
85
94
|
#
|
86
|
-
# The return should be the
|
87
|
-
# +with_data+ is true a Data object with an +id+ attribute and a +data+
|
88
|
-
# attribute that contains the full object details. Note that depending on
|
89
|
-
# the implemenation the identifier may change as a result of an update.
|
95
|
+
# The return should be the identifiers for the object updated.
|
90
96
|
#
|
91
97
|
# On error an Exception should be raised.
|
92
98
|
#
|
93
|
-
#
|
99
|
+
# path:: array of tokens in the path.
|
100
|
+
# query:: query parameters from a URL.
|
94
101
|
# data:: the data to use as a new object.
|
95
|
-
|
96
|
-
|
97
|
-
|
102
|
+
def update(path, query, data) # :doc:
|
103
|
+
tql = { }
|
104
|
+
kind = path[@shell.path_pos]
|
105
|
+
if @shell.path_pos + 2 == path.length # has an object reference in the path
|
106
|
+
tql[:where] = path[@shell.path_pos + 1].to_i
|
107
|
+
elsif query.is_a?(Hash) && 0 < query.size
|
108
|
+
where = ['AND']
|
109
|
+
where << form_where_eq(@shell.type_key, kind)
|
110
|
+
query.each_pair { |k,v| where << form_where_eq(k, v) }
|
111
|
+
tql[:where] = where
|
112
|
+
else
|
113
|
+
raise ::WAB::Error.new("update on all #{kind} not allowed.")
|
114
|
+
end
|
115
|
+
tql[:update] = data.native
|
116
|
+
shell_query(tql, kind, 'update')
|
98
117
|
end
|
99
118
|
|
100
119
|
# Delete the identified object.
|
@@ -102,54 +121,30 @@ module WAB
|
|
102
121
|
# On success the deleted object identifier is returned. If the object is
|
103
122
|
# not found then nil is returned. On error an Exception should be raised.
|
104
123
|
#
|
105
|
-
# id
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
#
|
111
|
-
#
|
112
|
-
#
|
113
|
-
#
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
# exists and were not provided.
|
130
|
-
#
|
131
|
-
# Note that this could return a very large set of data. If the number of
|
132
|
-
# instances in the type is large the +search()+ might be more appropriate
|
133
|
-
# as it allows for paging of results and sorting.
|
134
|
-
#
|
135
|
-
# with_data:: flag indicating the return should include object data
|
136
|
-
def list(with_data=false) # :doc:
|
137
|
-
# TBD implement the default behavior as an example or starting point
|
138
|
-
end
|
139
|
-
|
140
|
-
# Search using a TQL SELECT.
|
141
|
-
#
|
142
|
-
# The provided TQL[http://opo.technology/pages/doc/tql/index.html] can be
|
143
|
-
# either the JSON syntax or the friendly syntax. The call exists on the
|
144
|
-
# controller to allow filtering and permission checking before
|
145
|
-
# execution. Only the default controller is expected to provide a public
|
146
|
-
# version of this method.
|
147
|
-
#
|
148
|
-
# query:: query
|
149
|
-
# format:: can be one of :TQL or :TQL_JSON. The :GraphQL option is
|
150
|
-
# reserved for the future.
|
151
|
-
def search(query, format=:TQL) # :doc:
|
152
|
-
# TBD implement the default behavior as an example or starting point
|
124
|
+
# If no +id+ is present in the path then the return should be a Hash where
|
125
|
+
# the keys are the matching object identifiers and the value are the
|
126
|
+
# object data. An empty Hash or nil indicates there were no matches.
|
127
|
+
#
|
128
|
+
# path:: identifier of the object to be deleted
|
129
|
+
# query:: query parameters from a URL as a Hash with keys matching paths
|
130
|
+
# into the target objects and value equal to the target attribute
|
131
|
+
# values. A path can be an array of keys used to walk a path to
|
132
|
+
# the target or a +.+ delimited set of keys.
|
133
|
+
def delete(path, query) # :doc:
|
134
|
+
tql = { }
|
135
|
+
kind = path[@shell.path_pos]
|
136
|
+
if @shell.path_pos + 2 == path.length # has an object reference in the path
|
137
|
+
tql[:where] = path[@shell.path_pos + 1].to_i
|
138
|
+
elsif query.is_a?(Hash) && 0 < query.size
|
139
|
+
where = ['AND']
|
140
|
+
where << form_where_eq(@shell.type_key, kind)
|
141
|
+
query.each_pair { |k,v| where << form_where_eq(k, v) }
|
142
|
+
tql[:where] = where
|
143
|
+
else
|
144
|
+
tql[:where] = form_where_eq(@shell.type_key, kind)
|
145
|
+
end
|
146
|
+
tql[:delete] = nil
|
147
|
+
shell_query(tql, kind, 'delete')
|
153
148
|
end
|
154
149
|
|
155
150
|
# Subscribe to changes in data pushed from the model that will be passed
|
@@ -168,7 +163,61 @@ module WAB
|
|
168
163
|
#
|
169
164
|
# data:: the data that has changed
|
170
165
|
def changed(data) # :doc:
|
171
|
-
# TBD
|
166
|
+
# TBD filter accoding to subscriptions
|
167
|
+
@shell.changed(data)
|
168
|
+
end
|
169
|
+
|
170
|
+
# Form a EQ expression for a TQL where clause. Used as a helper to the
|
171
|
+
# primary API calls.
|
172
|
+
#
|
173
|
+
# key:: key in the expression
|
174
|
+
# value:: value portion converted to the correct format if necessary
|
175
|
+
def form_where_eq(key, value)
|
176
|
+
value_class = value.class
|
177
|
+
x = ['EQ', key.to_s]
|
178
|
+
if value.is_a?(String)
|
179
|
+
x << "'" + value
|
180
|
+
elsif Time == value_class
|
181
|
+
x << value.utc.iso8601(9)
|
182
|
+
elsif value.nil? ||
|
183
|
+
TrueClass == value_class ||
|
184
|
+
FalseClass == value_class ||
|
185
|
+
Integer == value_class ||
|
186
|
+
Float == value_class
|
187
|
+
x << value
|
188
|
+
elsif String == value_class
|
189
|
+
if 0 < value.length && '\'' == value[0]
|
190
|
+
x << value[1..-1]
|
191
|
+
elsif /^-?\d+$/.match?(value)
|
192
|
+
x << value.to_i
|
193
|
+
elsif /^-?\d*\.?\d+([eE][-+]?\d+)?$/.match?(value)
|
194
|
+
x << value.to_f
|
195
|
+
end
|
196
|
+
# TBD detect other types UUID, HTTP, Time
|
197
|
+
|
198
|
+
elsif '2' == RbConfig::CONFIG['MAJOR'] && '4' > RbConfig::CONFIG['MINOR'] && Fixnum == value_class
|
199
|
+
x << value
|
200
|
+
else
|
201
|
+
x << value.to_s
|
202
|
+
end
|
203
|
+
x
|
204
|
+
end
|
205
|
+
|
206
|
+
# Helper to send TQL requests to the shell either synchronously or
|
207
|
+
# asynchronously depending on the controller type.
|
208
|
+
def shell_query(tql, kind, op)
|
209
|
+
|
210
|
+
# TBD check for async or not
|
211
|
+
|
212
|
+
result = @shell.query(tql, nil) # synchronous call
|
213
|
+
if result.nil? || 0 != result[:code]
|
214
|
+
if result.nil?
|
215
|
+
raise ::WAB::Error.new("nil result on #{kind} #{op}.")
|
216
|
+
else
|
217
|
+
raise ::WAB::Error.new("error on #{kind} #{op}. #{result[:error]}")
|
218
|
+
end
|
219
|
+
end
|
220
|
+
result
|
172
221
|
end
|
173
222
|
|
174
223
|
end # Controller
|
data/lib/wab/data.rb
CHANGED
@@ -85,6 +85,14 @@ module WAB
|
|
85
85
|
raise NotImplementedError.new
|
86
86
|
end
|
87
87
|
|
88
|
+
# Detects and converts strings to Ruby objects following the rules:
|
89
|
+
# Time:: "2017-01-05T15:04:33.123456789Z", zulu only
|
90
|
+
# UUID:: "b0ca922d-372e-41f4-8fea-47d880188ba3"
|
91
|
+
# URI:: "http://opo.technology/sample", HTTP only
|
92
|
+
def detect()
|
93
|
+
raise NotImplementedError.new
|
94
|
+
end
|
95
|
+
|
88
96
|
private
|
89
97
|
|
90
98
|
# This method is included only to raise an error if an attempt is made to
|
data/lib/wab/errors.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
|
2
|
+
module WAB
|
3
|
+
|
4
|
+
# Base for WAB errors and exceptions.
|
5
|
+
class Error < StandardError
|
6
|
+
end # Error
|
7
|
+
|
8
|
+
# An Exception that is raised as a result of a parse error while parsing a
|
9
|
+
# JSON document.
|
10
|
+
class ParseError < Error
|
11
|
+
end # ParseError
|
12
|
+
|
13
|
+
end # Oj
|
data/lib/wab/impl/data.rb
CHANGED
@@ -21,10 +21,10 @@ module WAB
|
|
21
21
|
#
|
22
22
|
# value:: initial value
|
23
23
|
# repair:: flag indicating invalid value should be repaired if possible
|
24
|
-
def initialize(value, repair)
|
24
|
+
def initialize(value, repair, check=true)
|
25
25
|
if repair
|
26
26
|
value = fix(value)
|
27
|
-
|
27
|
+
elsif check
|
28
28
|
validate(value)
|
29
29
|
end
|
30
30
|
@root = value
|
@@ -35,23 +35,28 @@ module WAB
|
|
35
35
|
# array of path node identifiers. For example, child.grandchild is the
|
36
36
|
# same as ['child', 'grandchild'].
|
37
37
|
def get(path)
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
38
|
+
if path.is_a?(Symbol)
|
39
|
+
node = @root[path]
|
40
|
+
else
|
41
|
+
path = path.to_s.split('.') unless path.is_a?(Array)
|
42
|
+
node = @root
|
43
|
+
path.each { |key|
|
44
|
+
if node.is_a?(Hash)
|
45
|
+
node = node[key.to_sym]
|
46
|
+
elsif node.is_a?(Array)
|
47
|
+
i = key.to_i
|
48
|
+
if 0 == i && '0' != key && 0 != key
|
49
|
+
node = nil
|
50
|
+
break
|
51
|
+
end
|
52
|
+
node = node[i]
|
53
|
+
else
|
46
54
|
node = nil
|
47
55
|
break
|
48
56
|
end
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
break
|
53
|
-
end
|
54
|
-
}
|
57
|
+
}
|
58
|
+
end
|
59
|
+
return Data.new(node, false, false) if node.is_a?(Hash) || node.is_a?(Array)
|
55
60
|
node
|
56
61
|
end
|
57
62
|
|
@@ -70,7 +75,9 @@ module WAB
|
|
70
75
|
# repair:: flag indicating invalid value should be repaired if possible
|
71
76
|
def set(path, value, repair=false)
|
72
77
|
raise StandardError.new("path can not be empty.") if path.empty?
|
73
|
-
if
|
78
|
+
if value.is_a?(::WAB::Data)
|
79
|
+
value = value.native
|
80
|
+
elsif repair
|
74
81
|
value = fix_value(value)
|
75
82
|
else
|
76
83
|
validate_value(value)
|
@@ -177,6 +184,14 @@ module WAB
|
|
177
184
|
Oj.dump(@root, mode: :wab, indent: indent)
|
178
185
|
end
|
179
186
|
|
187
|
+
# Detects and converts strings to Ruby objects following the rules:
|
188
|
+
# Time:: "2017-01-05T15:04:33.123456789Z", zulu only
|
189
|
+
# UUID:: "b0ca922d-372e-41f4-8fea-47d880188ba3"
|
190
|
+
# URI:: "http://opo.technology/sample", HTTP only
|
191
|
+
def detect()
|
192
|
+
# TBD
|
193
|
+
end
|
194
|
+
|
180
195
|
private
|
181
196
|
|
182
197
|
# Raise an exception if the value is not a suitable data element. If the
|
@@ -223,6 +238,8 @@ module WAB
|
|
223
238
|
value.each { |v|
|
224
239
|
validate_value(v)
|
225
240
|
}
|
241
|
+
elsif '2' == RbConfig::CONFIG['MAJOR'] && '4' > RbConfig::CONFIG['MINOR'] && Fixnum == value_class
|
242
|
+
# valid value
|
226
243
|
else
|
227
244
|
raise StandardError.new("#{value_class.to_s} is not a valid Data value.")
|
228
245
|
end
|
@@ -281,6 +298,8 @@ module WAB
|
|
281
298
|
old.each { |v|
|
282
299
|
value << fix_value(v)
|
283
300
|
}
|
301
|
+
elsif '2' == RbConfig::CONFIG['MAJOR'] && '4' > RbConfig::CONFIG['MINOR'] && Fixnum == value_class
|
302
|
+
# valid value
|
284
303
|
elsif value.respond_to?(:to_h) && 0 == value.method(:to_h).arity
|
285
304
|
value = value.to_h
|
286
305
|
raise StandardError.new("Data values must be either a Hash or an Array") unless value.is_a?(Hash)
|
@@ -361,7 +380,19 @@ module WAB
|
|
361
380
|
c = []
|
362
381
|
value.each { |v| c << clone_value(v) }
|
363
382
|
else
|
364
|
-
|
383
|
+
value_class = value.class
|
384
|
+
if value.nil? ||
|
385
|
+
TrueClass == value_class ||
|
386
|
+
FalseClass == value_class ||
|
387
|
+
Integer == value_class ||
|
388
|
+
Float == value_class ||
|
389
|
+
String == value_class
|
390
|
+
c = value
|
391
|
+
elsif '2' == RbConfig::CONFIG['MAJOR'] && '4' > RbConfig::CONFIG['MINOR'] && Fixnum == value_class
|
392
|
+
c = value
|
393
|
+
else
|
394
|
+
c = value.clone
|
395
|
+
end
|
365
396
|
end
|
366
397
|
c
|
367
398
|
end
|
data/lib/wab/impl/shell.rb
CHANGED
data/lib/wab/io/call.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
|
2
|
+
require 'wab'
|
3
|
+
|
4
|
+
module WAB
|
5
|
+
|
6
|
+
module IO
|
7
|
+
|
8
|
+
class Call
|
9
|
+
|
10
|
+
attr_accessor :rid
|
11
|
+
attr_accessor :result
|
12
|
+
attr_accessor :thread
|
13
|
+
|
14
|
+
def initialize(timeout=2.0)
|
15
|
+
@rid = nil
|
16
|
+
@result = nil
|
17
|
+
@thread = Thread.current
|
18
|
+
@giveup = Time.now + timeout
|
19
|
+
end
|
20
|
+
|
21
|
+
end # Call
|
22
|
+
end # IO
|
23
|
+
end # WAB
|