presto-client 0.3.3 → 0.4.0
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 +8 -8
- data/README.md +3 -1
- data/Rakefile +13 -0
- data/lib/presto/client/models.rb +910 -207
- data/lib/presto/client/query.rb +27 -17
- data/lib/presto/client/statement_client.rb +18 -9
- data/lib/presto/client/version.rb +1 -1
- data/modelgen/modelgen.rb +69 -0
- data/modelgen/models.rb +132 -0
- data/modelgen/presto_models.rb +210 -0
- data/spec/statement_client_spec.rb +2 -2
- metadata +5 -3
data/lib/presto/client/query.rb
CHANGED
@@ -36,14 +36,20 @@ module Presto::Client
|
|
36
36
|
new StatementClient.new(faraday, query, options)
|
37
37
|
end
|
38
38
|
|
39
|
-
def initialize(
|
40
|
-
@
|
39
|
+
def initialize(api)
|
40
|
+
@api = api
|
41
41
|
end
|
42
42
|
|
43
|
-
|
43
|
+
def current_results
|
44
|
+
@api.current_results
|
45
|
+
end
|
46
|
+
|
47
|
+
def advance
|
48
|
+
@api.advance
|
49
|
+
end
|
44
50
|
|
45
51
|
def wait_for_data
|
46
|
-
while @
|
52
|
+
while @api.current_results.data == nil && @api.advance
|
47
53
|
end
|
48
54
|
end
|
49
55
|
|
@@ -52,9 +58,9 @@ module Presto::Client
|
|
52
58
|
def columns
|
53
59
|
wait_for_data
|
54
60
|
|
55
|
-
raise_error unless @
|
61
|
+
raise_error unless @api.query_succeeded?
|
56
62
|
|
57
|
-
return @
|
63
|
+
return @api.current_results.columns
|
58
64
|
end
|
59
65
|
|
60
66
|
def rows
|
@@ -74,36 +80,40 @@ module Presto::Client
|
|
74
80
|
def each_row_chunk(&block)
|
75
81
|
wait_for_data
|
76
82
|
|
77
|
-
raise_error unless @
|
83
|
+
raise_error unless @api.query_succeeded?
|
78
84
|
|
79
85
|
if self.columns == nil
|
80
|
-
raise PrestoError, "Query #{@
|
86
|
+
raise PrestoError, "Query #{@api.current_results.id} has no columns"
|
81
87
|
end
|
82
88
|
|
83
89
|
begin
|
84
|
-
if data = @
|
90
|
+
if data = @api.current_results.data
|
85
91
|
block.call(data)
|
86
92
|
end
|
87
|
-
end while @
|
93
|
+
end while @api.advance
|
94
|
+
end
|
95
|
+
|
96
|
+
def query_info
|
97
|
+
@api.query_info
|
88
98
|
end
|
89
99
|
|
90
100
|
def cancel
|
91
|
-
@
|
101
|
+
@api.cancel_leaf_stage
|
92
102
|
end
|
93
103
|
|
94
104
|
def close
|
95
|
-
@
|
105
|
+
@api.cancel_leaf_stage
|
96
106
|
nil
|
97
107
|
end
|
98
108
|
|
99
109
|
def raise_error
|
100
|
-
if @
|
110
|
+
if @api.closed?
|
101
111
|
raise PrestoClientError, "Query aborted by user"
|
102
|
-
elsif @
|
112
|
+
elsif @api.exception?
|
103
113
|
# query is gone
|
104
|
-
raise @
|
105
|
-
elsif @
|
106
|
-
results = @
|
114
|
+
raise @api.exception
|
115
|
+
elsif @api.query_failed?
|
116
|
+
results = @api.current_results
|
107
117
|
error = results.error
|
108
118
|
raise PrestoQueryError.new("Query #{results.id} failed: #{error.message}", results.id, error.error_code, error.failure_info)
|
109
119
|
end
|
@@ -89,7 +89,7 @@ module Presto::Client
|
|
89
89
|
|
90
90
|
body = response.body
|
91
91
|
hash = MultiJson.load(body)
|
92
|
-
@results = QueryResults.
|
92
|
+
@results = Models::QueryResults.decode(hash)
|
93
93
|
end
|
94
94
|
|
95
95
|
private :post_query_request!
|
@@ -132,27 +132,36 @@ module Presto::Client
|
|
132
132
|
end
|
133
133
|
uri = @results.next_uri
|
134
134
|
|
135
|
+
body = faraday_get_with_retry(uri)
|
136
|
+
@results = Models::QueryResults.decode(MultiJson.load(body))
|
137
|
+
|
138
|
+
return true
|
139
|
+
end
|
140
|
+
|
141
|
+
def query_info
|
142
|
+
body = faraday_get_with_retry("/v1/query/#{@results.id}")
|
143
|
+
Models::QueryInfo.decode(MultiJson.load(body))
|
144
|
+
end
|
145
|
+
|
146
|
+
def faraday_get_with_retry(uri, &block)
|
135
147
|
start = Time.now
|
136
148
|
attempts = 0
|
137
149
|
|
138
150
|
begin
|
139
151
|
begin
|
140
|
-
response = @faraday.get
|
141
|
-
req.url uri
|
142
|
-
end
|
152
|
+
response = @faraday.get(uri)
|
143
153
|
rescue => e
|
144
154
|
@exception = e
|
145
155
|
raise @exception
|
146
156
|
end
|
147
157
|
|
148
158
|
if response.status == 200 && !response.body.to_s.empty?
|
149
|
-
|
150
|
-
return true
|
159
|
+
return response.body
|
151
160
|
end
|
152
161
|
|
153
|
-
if response.status != 503 # retry
|
162
|
+
if response.status != 503 # retry only if 503 Service Unavailable
|
154
163
|
# deterministic error
|
155
|
-
@exception = PrestoHttpError.new(response.status, "
|
164
|
+
@exception = PrestoHttpError.new(response.status, "Presto API error at #{uri} returned #{response.status}: #{response.body}")
|
156
165
|
raise @exception
|
157
166
|
end
|
158
167
|
|
@@ -160,7 +169,7 @@ module Presto::Client
|
|
160
169
|
sleep attempts * 0.1
|
161
170
|
end while (Time.now - start) < 2*60*60 && !@closed
|
162
171
|
|
163
|
-
@exception = PrestoHttpError.new(408, "
|
172
|
+
@exception = PrestoHttpError.new(408, "Presto API error due to timeout")
|
164
173
|
raise @exception
|
165
174
|
end
|
166
175
|
|
@@ -0,0 +1,69 @@
|
|
1
|
+
|
2
|
+
if ARGV.length != 3
|
3
|
+
puts "usage: <presto-source-dir> <template.erb> <output.rb>"
|
4
|
+
end
|
5
|
+
|
6
|
+
source_dir, template_path, output_path = *ARGV
|
7
|
+
|
8
|
+
require_relative 'presto_models'
|
9
|
+
|
10
|
+
require 'erb'
|
11
|
+
erb = ERB.new(File.read(template_path))
|
12
|
+
|
13
|
+
source_path = "/Users/frsyuki/project/presto-client-ruby/presto"
|
14
|
+
|
15
|
+
predefined_simple_classes = %w[QueryId StageId TaskId PlanNodeId PlanFragmentId ConnectorSession]
|
16
|
+
predefined_models = %w[DistributionSnapshot PlanNode]
|
17
|
+
|
18
|
+
assume_primitive = %w[Object Type Symbol URI Duration DataSize DateTime ConnectorTableHandle ConnectorOutputTableHandle ConnectorIndexHandle ConnectorColumnHandle Expression FunctionCall]
|
19
|
+
enum_types = %w[QueryState StageState TaskState QueueState PlanDistribution OutputPartitioning Step SortOrder]
|
20
|
+
|
21
|
+
root_models = %w[QueryResults QueryInfo] + %w[
|
22
|
+
OutputNode
|
23
|
+
ProjectNode
|
24
|
+
TableScanNode
|
25
|
+
ValuesNode
|
26
|
+
AggregationNode
|
27
|
+
MarkDistinctNode
|
28
|
+
MaterializeSampleNode
|
29
|
+
FilterNode
|
30
|
+
WindowNode
|
31
|
+
LimitNode
|
32
|
+
DistinctLimitNode
|
33
|
+
TopNNode
|
34
|
+
SampleNode
|
35
|
+
SortNode
|
36
|
+
ExchangeNode
|
37
|
+
SinkNode
|
38
|
+
JoinNode
|
39
|
+
SemiJoinNode
|
40
|
+
IndexJoinNode
|
41
|
+
IndexSourceNode
|
42
|
+
TableWriterNode
|
43
|
+
TableCommitNode
|
44
|
+
]
|
45
|
+
|
46
|
+
analyzer = PrestoModels::ModelAnalyzer.new(
|
47
|
+
source_path,
|
48
|
+
skip_models: predefined_models + predefined_simple_classes + assume_primitive + enum_types
|
49
|
+
)
|
50
|
+
analyzer.analyze(root_models)
|
51
|
+
models = analyzer.models
|
52
|
+
skipped_models = analyzer.skipped_models
|
53
|
+
|
54
|
+
formatter = PrestoModels::ModelFormatter.new(
|
55
|
+
base_indent_count: 2,
|
56
|
+
struct_class: "Base",
|
57
|
+
special_struct_initialize_method: "initialize_struct",
|
58
|
+
primitive_types: assume_primitive,
|
59
|
+
skip_types: skipped_models,
|
60
|
+
simple_classes: predefined_simple_classes,
|
61
|
+
enum_types: enum_types,
|
62
|
+
)
|
63
|
+
formatter.format(models)
|
64
|
+
|
65
|
+
@contents = formatter.contents
|
66
|
+
|
67
|
+
data = erb.result
|
68
|
+
File.open(output_path, 'w') {|f| f.write data }
|
69
|
+
|
data/modelgen/models.rb
ADDED
@@ -0,0 +1,132 @@
|
|
1
|
+
#
|
2
|
+
# Presto client for Ruby
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
#
|
16
|
+
module Presto::Client
|
17
|
+
|
18
|
+
####
|
19
|
+
## lib/presto/client/models.rb is automatically generated using "rake modelgen" command.
|
20
|
+
## You should not edit this file directly. To modify the class definitions, edit
|
21
|
+
## modelgen/models.rb file and run "rake modelgen".
|
22
|
+
##
|
23
|
+
|
24
|
+
module Models
|
25
|
+
class Base < Struct
|
26
|
+
class << self
|
27
|
+
alias_method :new_struct, :new
|
28
|
+
|
29
|
+
def new(*args)
|
30
|
+
new_struct(*args) do
|
31
|
+
# make it immutable
|
32
|
+
undef_method :"[]="
|
33
|
+
members.each do |m|
|
34
|
+
undef_method :"#{m}="
|
35
|
+
end
|
36
|
+
|
37
|
+
# replace constructor to receive hash instead of array
|
38
|
+
alias_method :initialize_struct, :initialize
|
39
|
+
|
40
|
+
def initialize(params={})
|
41
|
+
initialize_struct(*members.map {|m| params[m] })
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
class QueryId < String
|
49
|
+
end
|
50
|
+
|
51
|
+
class StageId < String
|
52
|
+
end
|
53
|
+
|
54
|
+
class TaskId < String
|
55
|
+
end
|
56
|
+
|
57
|
+
class PlanNodeId < String
|
58
|
+
end
|
59
|
+
|
60
|
+
class PlanFragmentId < String
|
61
|
+
end
|
62
|
+
|
63
|
+
class ConnectorSession < Hash
|
64
|
+
def initialize(hash)
|
65
|
+
super()
|
66
|
+
merge!(hash)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
module PlanNode
|
71
|
+
def self.decode(hash)
|
72
|
+
model_class = case hash["type"]
|
73
|
+
when "output" then OutputNode
|
74
|
+
when "project" then ProjectNode
|
75
|
+
when "tablescan" then TableScanNode
|
76
|
+
when "values" then ValuesNode
|
77
|
+
when "aggregation" then AggregationNode
|
78
|
+
when "markDistinct" then MarkDistinctNode
|
79
|
+
when "materializeSample" then MaterializeSampleNode
|
80
|
+
when "filter" then FilterNode
|
81
|
+
when "window" then WindowNode
|
82
|
+
when "limit" then LimitNode
|
83
|
+
when "distinctlimit" then DistinctLimitNode
|
84
|
+
when "topn" then TopNNode
|
85
|
+
when "sample" then SampleNode
|
86
|
+
when "sort" then SortNode
|
87
|
+
when "exchange" then ExchangeNode
|
88
|
+
when "sink" then SinkNode
|
89
|
+
when "join" then JoinNode
|
90
|
+
when "semijoin" then SemiJoinNode
|
91
|
+
when "indexjoin" then IndexJoinNode
|
92
|
+
when "indexsource" then IndexSourceNode
|
93
|
+
when "tablewriter" then TableWriterNode
|
94
|
+
when "tablecommit" then TableCommitNode
|
95
|
+
end
|
96
|
+
model_class.decode(hash) if model_class
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
# io.airlift.stats.Distribution.DistributionSnapshot
|
101
|
+
class << DistributionSnapshot =
|
102
|
+
Base.new(:max_error, :count, :total, :p01, :p05, :p10, :p25, :p50, :p75, :p90, :p95, :p99, :min, :max)
|
103
|
+
def decode(hash)
|
104
|
+
obj = allocate
|
105
|
+
obj.send(:initialize_struct,
|
106
|
+
hash["maxError"],
|
107
|
+
hash["count"],
|
108
|
+
hash["total"],
|
109
|
+
hash["p01"],
|
110
|
+
hash["p05"],
|
111
|
+
hash["p10"],
|
112
|
+
hash["p25"],
|
113
|
+
hash["p50"],
|
114
|
+
hash["p75"],
|
115
|
+
hash["p90"],
|
116
|
+
hash["p95"],
|
117
|
+
hash["p99"],
|
118
|
+
hash["min"],
|
119
|
+
hash["max"],
|
120
|
+
)
|
121
|
+
obj
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
|
126
|
+
##
|
127
|
+
# Those model classes are automatically generated
|
128
|
+
#
|
129
|
+
|
130
|
+
<%= @contents %>
|
131
|
+
end
|
132
|
+
end
|
@@ -0,0 +1,210 @@
|
|
1
|
+
|
2
|
+
module PrestoModels
|
3
|
+
require 'find'
|
4
|
+
require 'stringio'
|
5
|
+
|
6
|
+
PRIMITIVE_TYPES = %w[String boolean long int short byte double float]
|
7
|
+
|
8
|
+
class Model < Struct.new(:name, :fields)
|
9
|
+
end
|
10
|
+
|
11
|
+
class Field < Struct.new(:key, :nullable, :array, :map, :type, :base_type, :map_value_base_type)
|
12
|
+
alias_method :nullable?, :nullable
|
13
|
+
alias_method :array?, :array
|
14
|
+
alias_method :map?, :map
|
15
|
+
|
16
|
+
def name
|
17
|
+
@name ||= key.gsub(/[A-Z]/) {|f| "_#{f.downcase}" }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class ModelAnalysisError < StandardError
|
22
|
+
end
|
23
|
+
|
24
|
+
class ModelAnalyzer
|
25
|
+
def initialize(source_path, options={})
|
26
|
+
@source_path = source_path
|
27
|
+
@ignore_types = PRIMITIVE_TYPES + (options[:skip_models] || [])
|
28
|
+
@models = {}
|
29
|
+
@skipped_models = []
|
30
|
+
end
|
31
|
+
|
32
|
+
attr_reader :skipped_models
|
33
|
+
|
34
|
+
def models
|
35
|
+
@models.values.sort_by {|model| model.name }
|
36
|
+
end
|
37
|
+
|
38
|
+
def analyze(root_models)
|
39
|
+
root_models.each {|model_name|
|
40
|
+
analyze_model(model_name)
|
41
|
+
}
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
PROPERTY_PATTERN = /@JsonProperty\(\"(\w+)\"\)\s+(@Nullable\s+)?([\w\<\>\,\s]+)\s+(\w+)/
|
47
|
+
CREATOR_PATTERN = /@JsonCreator[\w\s]+\((?:\s*#{PROPERTY_PATTERN}\s*,?)+\)/
|
48
|
+
|
49
|
+
def analyze_model(model_name)
|
50
|
+
return if @models[model_name] || @ignore_types.include?(model_name)
|
51
|
+
|
52
|
+
path = find_class_file(model_name)
|
53
|
+
java = File.read(path)
|
54
|
+
|
55
|
+
m = CREATOR_PATTERN.match(java)
|
56
|
+
unless m
|
57
|
+
raise ModelAnalysisError, "Can't find JsonCreator of a model class #{model_name}"
|
58
|
+
end
|
59
|
+
|
60
|
+
fields = m[0].scan(PROPERTY_PATTERN).map do |key,nullable,type,field|
|
61
|
+
map = false
|
62
|
+
array = false
|
63
|
+
nullable = !!nullable
|
64
|
+
if m = /(?:List|Set)<(\w+)>/.match(type)
|
65
|
+
base_type = m[1]
|
66
|
+
array = true
|
67
|
+
elsif m = /(?:Map)<(\w+),\s*(\w+)>/.match(type)
|
68
|
+
base_type = m[1]
|
69
|
+
map_value_base_type = m[2]
|
70
|
+
map = true
|
71
|
+
elsif m = /Optional<(\w+)>/.match(type)
|
72
|
+
base_type = m[1]
|
73
|
+
nullable = true
|
74
|
+
elsif type =~ /\w+/
|
75
|
+
base_type = type
|
76
|
+
else
|
77
|
+
raise ModelAnalysisError, "Unsupported type #{type} in model #{model_name}"
|
78
|
+
end
|
79
|
+
Field.new(key, !!nullable, array, map, type, base_type, map_value_base_type)
|
80
|
+
end
|
81
|
+
|
82
|
+
@models[model_name] = Model.new(model_name, fields)
|
83
|
+
|
84
|
+
# recursive call
|
85
|
+
fields.each do |field|
|
86
|
+
analyze_model(field.base_type)
|
87
|
+
analyze_model(field.map_value_base_type) if field.map_value_base_type
|
88
|
+
end
|
89
|
+
|
90
|
+
rescue => e
|
91
|
+
puts "Skipping model #{model_name}: #{e}"
|
92
|
+
@skipped_models << model_name
|
93
|
+
end
|
94
|
+
|
95
|
+
def find_class_file(model_name)
|
96
|
+
@source_files ||= Find.find(@source_path).to_a
|
97
|
+
pattern = /\/#{model_name}.java$/
|
98
|
+
matched = @source_files.find {|path| path =~ pattern }
|
99
|
+
unless matched
|
100
|
+
raise ModelAnalysisError, "Model class #{model_name} is not found"
|
101
|
+
end
|
102
|
+
return matched
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
class ModelFormatter
|
107
|
+
def initialize(options={})
|
108
|
+
@indent = options[:indent] || ' '
|
109
|
+
@base_indent_count = options[:base_indent_count] || 0
|
110
|
+
@struct_class = options[:struct_class] || 'Struct'
|
111
|
+
@special_struct_initialize_method = options[:special_struct_initialize_method]
|
112
|
+
@primitive_types = PRIMITIVE_TYPES + (options[:primitive_types] || [])
|
113
|
+
@skip_types = options[:skip_types] || []
|
114
|
+
@simple_classes = options[:simple_classes]
|
115
|
+
@enum_types = options[:enum_types]
|
116
|
+
@special_types = options[:special_types] || {}
|
117
|
+
@data = StringIO.new
|
118
|
+
end
|
119
|
+
|
120
|
+
def contents
|
121
|
+
@data.string
|
122
|
+
end
|
123
|
+
|
124
|
+
def format(models)
|
125
|
+
@models = models
|
126
|
+
models.each do |model|
|
127
|
+
@model = model
|
128
|
+
|
129
|
+
puts_with_indent 0, "class << #{model.name} ="
|
130
|
+
puts_with_indent 2, "#{@struct_class}.new(#{model.fields.map {|f| ":#{f.name}" }.join(', ')})"
|
131
|
+
format_decode
|
132
|
+
puts_with_indent 0, "end"
|
133
|
+
line
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
private
|
138
|
+
|
139
|
+
def line
|
140
|
+
@data.puts ""
|
141
|
+
end
|
142
|
+
|
143
|
+
def puts_with_indent(n, str)
|
144
|
+
@data.puts "#{@indent * (@base_indent_count + n)}#{str}"
|
145
|
+
end
|
146
|
+
|
147
|
+
def format_decode
|
148
|
+
puts_with_indent 1, "def decode(hash)"
|
149
|
+
if @special_struct_initialize_method
|
150
|
+
puts_with_indent 2, "obj = allocate"
|
151
|
+
puts_with_indent 2, "obj.send(:#{@special_struct_initialize_method},"
|
152
|
+
else
|
153
|
+
puts_with_indent 2, "new("
|
154
|
+
end
|
155
|
+
|
156
|
+
@model.fields.each do |field|
|
157
|
+
next if @skip_types.include?(field.base_type) || @skip_types.include?(field.map_value_base_type)
|
158
|
+
|
159
|
+
if @primitive_types.include?(field.base_type) && !field.map?
|
160
|
+
expr = "hash[\"#{field.key}\"]"
|
161
|
+
else
|
162
|
+
expr = ""
|
163
|
+
expr << "hash[\"#{field.key}\"] && " #if field.nullable?
|
164
|
+
|
165
|
+
if field.map?
|
166
|
+
key_expr = convert_expression(field.base_type, field.base_type, "k")
|
167
|
+
value_expr = convert_expression(field.map_value_base_type, field.map_value_base_type, "v")
|
168
|
+
if key_expr == 'k' && value_expr == 'v'
|
169
|
+
expr = "hash[\"#{field.key}\"]"
|
170
|
+
else
|
171
|
+
expr << "Hash[hash[\"#{field.key}\"].to_a.map! {|k,v| [#{key_expr}, #{value_expr}] }]"
|
172
|
+
end
|
173
|
+
elsif field.array?
|
174
|
+
elem_expr = convert_expression(field.base_type, field.base_type, "h")
|
175
|
+
expr << "hash[\"#{field.key}\"].map {|h| #{elem_expr} }"
|
176
|
+
else
|
177
|
+
expr << convert_expression(field.type, field.base_type, "hash[\"#{field.key}\"]")
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
#comment = "# #{field.base_type}#{field.array? ? '[]' : ''} #{field.key}"
|
182
|
+
#puts_with_indent 3, "#{expr}, #{comment}"
|
183
|
+
puts_with_indent 3, "#{expr},"
|
184
|
+
end
|
185
|
+
|
186
|
+
puts_with_indent 2, ")"
|
187
|
+
|
188
|
+
if @special_struct_initialize_method
|
189
|
+
puts_with_indent 2, "obj"
|
190
|
+
end
|
191
|
+
|
192
|
+
puts_with_indent 1, "end"
|
193
|
+
end
|
194
|
+
|
195
|
+
def convert_expression(type, base_type, key)
|
196
|
+
if @special_types[type]
|
197
|
+
special.call(key)
|
198
|
+
elsif @enum_types.include?(type)
|
199
|
+
"#{key}.downcase.to_sym"
|
200
|
+
elsif @primitive_types.include?(base_type)
|
201
|
+
key
|
202
|
+
elsif @simple_classes.include?(base_type)
|
203
|
+
"#{base_type}.new(#{key})"
|
204
|
+
else # model class
|
205
|
+
"#{base_type}.decode(#{key})"
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|