wabur 0.1.0d2 → 0.2.0d1
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.
- 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
|
-
[](http://travis-ci.org/ohler55/wabur?branch=develop)
|
3
|
+
[](http://travis-ci.org/ohler55/wabur?branch=develop)
|
4
|
+
[](https://ci.appveyor.com/project/ohler55/wabur/branch/develop)
|
5
|
+
[](https://rubygems.org/gems/wabur)
|
6
|
+
[](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
|