linkage 0.0.1 → 0.0.2
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.
- data/Gemfile +3 -2
- data/Gemfile.lock +25 -17
- data/Guardfile +2 -3
- data/Rakefile +1 -1
- data/VERSION +1 -1
- data/lib/linkage/configuration.rb +55 -15
- data/lib/linkage/data.rb +171 -0
- data/lib/linkage/dataset.rb +16 -16
- data/lib/linkage/expectation.rb +6 -6
- data/lib/linkage/field.rb +5 -144
- data/lib/linkage/function.rb +108 -0
- data/lib/linkage/functions/trim.rb +22 -0
- data/lib/linkage/warnings.rb +5 -0
- data/lib/linkage.rb +3 -0
- data/linkage.gemspec +24 -4
- data/test/config.yml +5 -0
- data/test/helper.rb +27 -2
- data/test/integration/test_dual_linkage.rb +33 -0
- data/test/integration/test_self_linkage.rb +21 -0
- data/test/unit/functions/test_trim.rb +36 -0
- data/test/unit/test_configuration.rb +42 -6
- data/test/unit/test_data.rb +456 -0
- data/test/unit/test_dataset.rb +85 -22
- data/test/unit/test_expectation.rb +97 -1
- data/test/unit/test_field.rb +15 -411
- data/test/unit/test_function.rb +190 -0
- metadata +58 -39
- /data/test/unit/{test_single_threaded_runner.rb → runner/test_single_threaded.rb} +0 -0
data/Gemfile
CHANGED
@@ -6,12 +6,13 @@ group :development do
|
|
6
6
|
gem "bundler", "~> 1.0.0"
|
7
7
|
gem "jeweler", "~> 1.6.4"
|
8
8
|
gem "rcov", ">= 0"
|
9
|
-
gem "guard-test"
|
10
9
|
gem "test-unit", "2.3.2"
|
11
10
|
gem "mocha"
|
12
11
|
gem "sqlite3"
|
13
12
|
gem "yard"
|
14
13
|
gem "rake"
|
15
14
|
gem "versionomy"
|
16
|
-
gem "
|
15
|
+
gem "mysql2"
|
16
|
+
gem 'pry'
|
17
|
+
gem 'rdiscount'
|
17
18
|
end
|
data/Gemfile.lock
CHANGED
@@ -2,41 +2,49 @@ GEM
|
|
2
2
|
remote: http://rubygems.org/
|
3
3
|
specs:
|
4
4
|
blockenspiel (0.4.3)
|
5
|
+
coderay (0.9.8)
|
5
6
|
git (1.2.5)
|
6
|
-
guard (0.6.2)
|
7
|
-
thor (~> 0.14.6)
|
8
|
-
guard-test (0.3.0)
|
9
|
-
guard (>= 0.2.2)
|
10
|
-
test-unit (~> 2.2)
|
11
|
-
guard-yard (1.0.1)
|
12
|
-
guard (>= 0.2.2)
|
13
|
-
yard (>= 0.7.0)
|
14
7
|
jeweler (1.6.4)
|
15
8
|
bundler (~> 1.0)
|
16
9
|
git (>= 1.2.5)
|
17
10
|
rake
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
11
|
+
metaclass (0.0.1)
|
12
|
+
method_source (0.6.7)
|
13
|
+
ruby_parser (>= 2.3.1)
|
14
|
+
mocha (0.10.0)
|
15
|
+
metaclass (~> 0.0.1)
|
16
|
+
mysql2 (0.3.10)
|
17
|
+
pry (0.9.7.4)
|
18
|
+
coderay (~> 0.9.8)
|
19
|
+
method_source (~> 0.6.7)
|
20
|
+
ruby_parser (>= 2.3.1)
|
21
|
+
slop (~> 2.1.0)
|
22
|
+
rake (0.9.2.2)
|
23
|
+
rcov (0.9.11)
|
24
|
+
rdiscount (1.6.8)
|
25
|
+
ruby_parser (2.3.1)
|
26
|
+
sexp_processor (~> 3.0)
|
27
|
+
sequel (3.29.0)
|
28
|
+
sexp_processor (3.0.8)
|
29
|
+
slop (2.1.0)
|
30
|
+
sqlite3 (1.3.4)
|
23
31
|
test-unit (2.3.2)
|
24
|
-
thor (0.14.6)
|
25
32
|
versionomy (0.4.1)
|
26
33
|
blockenspiel (>= 0.4.1)
|
27
|
-
yard (0.7.
|
34
|
+
yard (0.7.3)
|
28
35
|
|
29
36
|
PLATFORMS
|
30
37
|
ruby
|
31
38
|
|
32
39
|
DEPENDENCIES
|
33
40
|
bundler (~> 1.0.0)
|
34
|
-
guard-test
|
35
|
-
guard-yard
|
36
41
|
jeweler (~> 1.6.4)
|
37
42
|
mocha
|
43
|
+
mysql2
|
44
|
+
pry
|
38
45
|
rake
|
39
46
|
rcov
|
47
|
+
rdiscount
|
40
48
|
sequel
|
41
49
|
sqlite3
|
42
50
|
test-unit (= 2.3.2)
|
data/Guardfile
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
guard 'test' do
|
2
|
-
watch(%r{^lib/linkage/
|
3
|
-
watch(%r{^
|
4
|
-
watch(%r{^test/unit/test_.+\.rb$})
|
2
|
+
watch(%r{^lib/linkage/([^/]+/)*([^/]+)\.rb$}) { |m| "test/unit/#{m[1]}test_#{m[2]}.rb" }
|
3
|
+
watch(%r{^test/unit/([^/]+/)*test_.+\.rb$})
|
5
4
|
watch(%r{^test/integration/test_.+\.rb$})
|
6
5
|
watch('lib/linkage/configuration.rb') { "test/unit/test_dataset.rb" }
|
7
6
|
watch('test/helper.rb') { "test" }
|
data/Rakefile
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.2
|
@@ -14,9 +14,9 @@ module Linkage
|
|
14
14
|
class Configuration
|
15
15
|
# @private
|
16
16
|
class ExpectationWrapper
|
17
|
-
def initialize(type,
|
17
|
+
def initialize(type, data, config)
|
18
18
|
@type = type
|
19
|
-
@
|
19
|
+
@data = data
|
20
20
|
@config = config
|
21
21
|
@side = nil
|
22
22
|
@forced_kind = nil
|
@@ -25,15 +25,15 @@ module Linkage
|
|
25
25
|
Linkage::Expectation::VALID_OPERATORS.each do |op|
|
26
26
|
define_method(op) do |other|
|
27
27
|
case other
|
28
|
-
when
|
29
|
-
@other = other.
|
30
|
-
if other.side == @
|
28
|
+
when DataWrapper
|
29
|
+
@other = other.data
|
30
|
+
if @other.static? || other.side == @data.side
|
31
31
|
@forced_kind = :filter
|
32
|
-
@side = @
|
32
|
+
@side = @data.side
|
33
33
|
end
|
34
34
|
else
|
35
35
|
@other = other
|
36
|
-
@side = @
|
36
|
+
@side = @data.side
|
37
37
|
end
|
38
38
|
add_expectation(op)
|
39
39
|
end
|
@@ -43,19 +43,14 @@ module Linkage
|
|
43
43
|
|
44
44
|
def add_expectation(operator)
|
45
45
|
klass = Expectation.get(@type)
|
46
|
-
exp = klass.new(operator, @
|
46
|
+
exp = klass.new(operator, @data.data, @other, @forced_kind)
|
47
47
|
@config.add_expectation(exp, @side)
|
48
48
|
end
|
49
49
|
end
|
50
50
|
|
51
51
|
# @private
|
52
|
-
class
|
53
|
-
attr_reader :
|
54
|
-
def initialize(field, side, config)
|
55
|
-
@field = field
|
56
|
-
@side = side
|
57
|
-
@config = config
|
58
|
-
end
|
52
|
+
class DataWrapper
|
53
|
+
attr_reader :data, :side
|
59
54
|
|
60
55
|
def must
|
61
56
|
ExpectationWrapper.new(:must, self, @config)
|
@@ -64,6 +59,41 @@ module Linkage
|
|
64
59
|
def must_not
|
65
60
|
ExpectationWrapper.new(:must_not, self, @config)
|
66
61
|
end
|
62
|
+
|
63
|
+
def static?
|
64
|
+
false
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# @private
|
69
|
+
class FunctionWrapper < DataWrapper
|
70
|
+
def initialize(klass, args, config)
|
71
|
+
@klass = klass
|
72
|
+
@args = args
|
73
|
+
@config = config
|
74
|
+
|
75
|
+
@side = args.inject(nil) do |side, arg|
|
76
|
+
if arg.kind_of?(DataWrapper)
|
77
|
+
raise "conflicting sides" if side && side != arg.side
|
78
|
+
arg.side
|
79
|
+
else
|
80
|
+
side
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def data
|
86
|
+
@klass.new(*@args.collect { |arg| arg.kind_of?(DataWrapper) ? arg.data : arg })
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# @private
|
91
|
+
class FieldWrapper < DataWrapper
|
92
|
+
def initialize(field, side, config)
|
93
|
+
@data = field
|
94
|
+
@side = side
|
95
|
+
@config = config
|
96
|
+
end
|
67
97
|
end
|
68
98
|
|
69
99
|
# @private
|
@@ -174,5 +204,15 @@ module Linkage
|
|
174
204
|
def inspect
|
175
205
|
to_s
|
176
206
|
end
|
207
|
+
|
208
|
+
# For handling functions
|
209
|
+
def method_missing(name, *args, &block)
|
210
|
+
klass = Function[name.to_s]
|
211
|
+
if klass
|
212
|
+
FunctionWrapper.new(klass, args, self)
|
213
|
+
else
|
214
|
+
super
|
215
|
+
end
|
216
|
+
end
|
177
217
|
end
|
178
218
|
end
|
data/lib/linkage/data.rb
ADDED
@@ -0,0 +1,171 @@
|
|
1
|
+
module Linkage
|
2
|
+
# Superclass to {Field} and {Function}.
|
3
|
+
#
|
4
|
+
# @abstract
|
5
|
+
class Data
|
6
|
+
# A "tree" used to find compatible types.
|
7
|
+
TYPE_CONVERSION_TREE = {
|
8
|
+
TrueClass => [Integer],
|
9
|
+
Integer => [Bignum, Float],
|
10
|
+
Bignum => [BigDecimal],
|
11
|
+
Float => [BigDecimal],
|
12
|
+
BigDecimal => [String],
|
13
|
+
String => nil,
|
14
|
+
DateTime => nil,
|
15
|
+
Date => nil,
|
16
|
+
Time => nil,
|
17
|
+
File => nil
|
18
|
+
}
|
19
|
+
|
20
|
+
# @return [Symbol] This object's name
|
21
|
+
attr_reader :name
|
22
|
+
|
23
|
+
def initialize(name)
|
24
|
+
@name = name
|
25
|
+
end
|
26
|
+
|
27
|
+
def ruby_type
|
28
|
+
raise NotImplementedError
|
29
|
+
end
|
30
|
+
|
31
|
+
def dataset
|
32
|
+
raise NotImplementedError
|
33
|
+
end
|
34
|
+
|
35
|
+
def to_expr
|
36
|
+
raise NotImplementedError
|
37
|
+
end
|
38
|
+
|
39
|
+
# Create a data object that can hold data from two other fields. If the fields
|
40
|
+
# have different types, the resulting type is determined via a
|
41
|
+
# type-conversion tree.
|
42
|
+
#
|
43
|
+
# @param [Linkage::Data] other
|
44
|
+
# @return [Linkage::Field]
|
45
|
+
def merge(other, new_name = nil)
|
46
|
+
schema_1 = self.ruby_type
|
47
|
+
schema_2 = other.ruby_type
|
48
|
+
if schema_1 == schema_2
|
49
|
+
result = schema_1
|
50
|
+
else
|
51
|
+
type_1 = schema_1[:type]
|
52
|
+
opts_1 = schema_1[:opts] || {}
|
53
|
+
type_2 = schema_2[:type]
|
54
|
+
opts_2 = schema_2[:opts] || {}
|
55
|
+
result_type = type_1
|
56
|
+
result_opts = schema_1[:opts] ? schema_1[:opts].dup : {}
|
57
|
+
|
58
|
+
# type
|
59
|
+
if type_1 != type_2
|
60
|
+
result_type = first_common_type(type_1, type_2)
|
61
|
+
end
|
62
|
+
|
63
|
+
# text
|
64
|
+
if opts_1[:text] != opts_2[:text]
|
65
|
+
# This can only be of type String.
|
66
|
+
result_opts[:text] = true
|
67
|
+
result_opts.delete(:size)
|
68
|
+
end
|
69
|
+
|
70
|
+
# size
|
71
|
+
if !result_opts[:text] && opts_1[:size] != opts_2[:size]
|
72
|
+
types = [type_1, type_2].uniq
|
73
|
+
if types.length == 1 && types[0] == BigDecimal
|
74
|
+
# Two decimals
|
75
|
+
if opts_1.has_key?(:size) && opts_2.has_key?(:size)
|
76
|
+
s_1 = opts_1[:size]
|
77
|
+
s_2 = opts_2[:size]
|
78
|
+
result_opts[:size] = [ s_1[0] > s_2[0] ? s_1[0] : s_2[0] ]
|
79
|
+
|
80
|
+
if s_1[1] && s_2[1]
|
81
|
+
result_opts[:size][1] = s_1[1] > s_2[1] ? s_1[1] : s_2[1]
|
82
|
+
else
|
83
|
+
result_opts[:size][1] = s_1[1] ? s_1[1] : s_2[1]
|
84
|
+
end
|
85
|
+
else
|
86
|
+
result_opts[:size] = opts_1.has_key?(:size) ? opts_1[:size] : opts_2[:size]
|
87
|
+
end
|
88
|
+
elsif types.include?(String) && types.include?(BigDecimal)
|
89
|
+
# Add one to the precision of the BigDecimal (for the dot)
|
90
|
+
if opts_1.has_key?(:size) && opts_2.has_key?(:size)
|
91
|
+
s_1 = opts_1[:size].is_a?(Array) ? opts_1[:size][0] + 1 : opts_1[:size]
|
92
|
+
s_2 = opts_2[:size].is_a?(Array) ? opts_2[:size][0] + 1 : opts_2[:size]
|
93
|
+
result_opts[:size] = s_1 > s_2 ? s_1 : s_2
|
94
|
+
elsif opts_1.has_key?(:size)
|
95
|
+
result_opts[:size] = opts_1[:size].is_a?(Array) ? opts_1[:size][0] + 1 : opts_1[:size]
|
96
|
+
elsif opts_2.has_key?(:size)
|
97
|
+
result_opts[:size] = opts_2[:size].is_a?(Array) ? opts_2[:size][0] + 1 : opts_2[:size]
|
98
|
+
end
|
99
|
+
else
|
100
|
+
# Treat as two strings
|
101
|
+
if opts_1.has_key?(:size) && opts_2.has_key?(:size)
|
102
|
+
result_opts[:size] = opts_1[:size] > opts_2[:size] ? opts_1[:size] : opts_2[:size]
|
103
|
+
elsif opts_1.has_key?(:size)
|
104
|
+
result_opts[:size] = opts_1[:size]
|
105
|
+
else
|
106
|
+
result_opts[:size] = opts_2[:size]
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# fixed
|
112
|
+
if opts_1[:fixed] != opts_2[:fixed]
|
113
|
+
# This can only be of type String.
|
114
|
+
result_opts[:fixed] = true
|
115
|
+
end
|
116
|
+
|
117
|
+
result = {:type => result_type}
|
118
|
+
result[:opts] = result_opts unless result_opts.empty?
|
119
|
+
end
|
120
|
+
|
121
|
+
if new_name
|
122
|
+
name = new_name.to_sym
|
123
|
+
else
|
124
|
+
name = self.name == other.name ? self.name : :"#{self.name}_#{other.name}"
|
125
|
+
end
|
126
|
+
Field.new(name, nil, result)
|
127
|
+
end
|
128
|
+
|
129
|
+
# Returns true if this data's name and dataset match the other's name
|
130
|
+
# and dataset (using {Dataset#==})
|
131
|
+
def ==(other)
|
132
|
+
if !other.is_a?(self.class)
|
133
|
+
super
|
134
|
+
elsif equal?(other)
|
135
|
+
true
|
136
|
+
else
|
137
|
+
self.name == other.name && self.dataset == other.dataset
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
# Returns true if this data source's dataset is equal to the given dataset
|
142
|
+
# (using Dataset#id).
|
143
|
+
#
|
144
|
+
# @param [Linkage::Dataset]
|
145
|
+
def belongs_to?(dataset)
|
146
|
+
self.dataset.id == dataset.id
|
147
|
+
end
|
148
|
+
|
149
|
+
private
|
150
|
+
|
151
|
+
def first_common_type(type_1, type_2)
|
152
|
+
types_1 = [type_1] + get_types(type_1)
|
153
|
+
types_2 = [type_2] + get_types(type_2)
|
154
|
+
(types_1 & types_2).first
|
155
|
+
end
|
156
|
+
|
157
|
+
# Get all types that the specified type can be converted to. Order
|
158
|
+
# matters.
|
159
|
+
def get_types(type)
|
160
|
+
result = []
|
161
|
+
types = TYPE_CONVERSION_TREE[type]
|
162
|
+
if types
|
163
|
+
result += types
|
164
|
+
types.each do |t|
|
165
|
+
result |= get_types(t)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
result
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
data/lib/linkage/dataset.rb
CHANGED
@@ -89,25 +89,25 @@ module Linkage
|
|
89
89
|
})
|
90
90
|
end
|
91
91
|
|
92
|
-
# Add a
|
92
|
+
# Add a data source to use for ordering the dataset.
|
93
93
|
#
|
94
|
-
# @param [Linkage::
|
94
|
+
# @param [Linkage::Data] data
|
95
95
|
# @param [nil, Symbol] desc nil or :desc (for descending order)
|
96
|
-
def add_order(
|
97
|
-
expr = desc == :desc ?
|
96
|
+
def add_order(data, desc = nil)
|
97
|
+
expr = desc == :desc ? data.to_expr.desc : data.to_expr
|
98
98
|
unless @order.include?(expr)
|
99
99
|
@order << expr
|
100
100
|
end
|
101
101
|
end
|
102
102
|
|
103
|
-
# Add a
|
104
|
-
# selects, all fields will be selected. The primary key is always
|
105
|
-
#
|
103
|
+
# Add a data source to be selected on the dataset. If you don't add any
|
104
|
+
# selects, all fields will be selected. The primary key is always selected
|
105
|
+
# in either case.
|
106
106
|
#
|
107
|
-
# @param [Linkage::
|
108
|
-
# @param [Symbol] as Optional field alias
|
109
|
-
def add_select(
|
110
|
-
expr = as ?
|
107
|
+
# @param [Linkage::Data] data
|
108
|
+
# @param [Symbol] as Optional field/function alias
|
109
|
+
def add_select(data, as = nil)
|
110
|
+
expr = as ? data.to_expr.as(as) : data.to_expr
|
111
111
|
unless @select.include?(expr)
|
112
112
|
@select << expr
|
113
113
|
end
|
@@ -115,12 +115,12 @@ module Linkage
|
|
115
115
|
|
116
116
|
# Add a filter (SQL WHERE) condition to the dataset.
|
117
117
|
#
|
118
|
-
# @param [Linkage::
|
118
|
+
# @param [Linkage::Data] data
|
119
119
|
# @param [Symbol] operator
|
120
|
-
# @param [Linkage::
|
121
|
-
def add_filter(
|
122
|
-
arg1 =
|
123
|
-
arg2 = other.is_a?(
|
120
|
+
# @param [Linkage::Data, Object] other
|
121
|
+
def add_filter(data, operator, other)
|
122
|
+
arg1 = data.to_expr
|
123
|
+
arg2 = other.is_a?(Data) ? other.to_expr : other
|
124
124
|
expr =
|
125
125
|
case operator
|
126
126
|
when :==
|
data/lib/linkage/expectation.rb
CHANGED
@@ -9,13 +9,13 @@ module Linkage
|
|
9
9
|
attr_reader :operator, :field_1, :field_2
|
10
10
|
|
11
11
|
# @param [Symbol] operator Currently, only :==
|
12
|
-
# @param [Linkage::Field, Object] field_1
|
13
|
-
# @param [Linkage::Field, Object] field_2
|
12
|
+
# @param [Linkage::Field, Linkage::Function, Object] field_1
|
13
|
+
# @param [Linkage::Field, Linkage::Function, Object] field_2
|
14
14
|
# @param [Symbol] force_kind Manually set type of expectation (useful for
|
15
15
|
# a filter between two fields)
|
16
16
|
def initialize(operator, field_1, field_2, force_kind = nil)
|
17
|
-
if !(field_1.
|
18
|
-
raise ArgumentError, "You must have at least one Linkage::Field"
|
17
|
+
if !((field_1.kind_of?(Data) && !field_1.static?) || (field_2.kind_of?(Data) && !field_2.static?))
|
18
|
+
raise ArgumentError, "You must have at least one data source (Linkage::Field or Linkage::Function)"
|
19
19
|
end
|
20
20
|
|
21
21
|
if !VALID_OPERATORS.include?(operator)
|
@@ -52,7 +52,7 @@ module Linkage
|
|
52
52
|
# @return [Symbol] :self, :dual, :cross, or :filter
|
53
53
|
def kind
|
54
54
|
@kind ||=
|
55
|
-
if !(@field_1.is_a?(
|
55
|
+
if !(@field_1.is_a?(Data) && !@field_1.static? && @field_2.is_a?(Data) && !@field_2.static?)
|
56
56
|
:filter
|
57
57
|
elsif @field_1 == @field_2
|
58
58
|
:self
|
@@ -95,7 +95,7 @@ module Linkage
|
|
95
95
|
else
|
96
96
|
as =
|
97
97
|
if kind == :self
|
98
|
-
nil
|
98
|
+
@field_1.is_a?(Function) ? @field_1.name : nil
|
99
99
|
else
|
100
100
|
name != @field_1.name ? name : nil
|
101
101
|
end
|
data/lib/linkage/field.rb
CHANGED
@@ -1,24 +1,7 @@
|
|
1
1
|
module Linkage
|
2
2
|
# This class is for holding information about a particular field in a
|
3
3
|
# dataset.
|
4
|
-
class Field
|
5
|
-
# A "tree" used to find compatible types.
|
6
|
-
TYPE_CONVERSION_TREE = {
|
7
|
-
TrueClass => [Integer],
|
8
|
-
Integer => [Bignum, Float],
|
9
|
-
Bignum => [BigDecimal],
|
10
|
-
Float => [BigDecimal],
|
11
|
-
BigDecimal => [String],
|
12
|
-
String => nil,
|
13
|
-
DateTime => nil,
|
14
|
-
Date => nil,
|
15
|
-
Time => nil,
|
16
|
-
File => nil
|
17
|
-
}
|
18
|
-
|
19
|
-
# @return [Symbol] This field's name
|
20
|
-
attr_reader :name
|
21
|
-
|
4
|
+
class Field < Data
|
22
5
|
# @return [Symbol] This field's schema information
|
23
6
|
attr_reader :schema
|
24
7
|
|
@@ -90,138 +73,16 @@ module Linkage
|
|
90
73
|
@ruby_type
|
91
74
|
end
|
92
75
|
|
93
|
-
|
94
|
-
|
95
|
-
# type-conversion tree.
|
96
|
-
#
|
97
|
-
# @param [Linkage::Field] other
|
98
|
-
# @return [Linkage::Field]
|
99
|
-
def merge(other, new_name = nil)
|
100
|
-
schema_1 = self.ruby_type
|
101
|
-
schema_2 = other.ruby_type
|
102
|
-
if schema_1 == schema_2
|
103
|
-
result = schema_1
|
104
|
-
else
|
105
|
-
type_1 = schema_1[:type]
|
106
|
-
opts_1 = schema_1[:opts] || {}
|
107
|
-
type_2 = schema_2[:type]
|
108
|
-
opts_2 = schema_2[:opts] || {}
|
109
|
-
result_type = type_1
|
110
|
-
result_opts = schema_1[:opts] ? schema_1[:opts].dup : {}
|
111
|
-
|
112
|
-
# type
|
113
|
-
if type_1 != type_2
|
114
|
-
result_type = first_common_type(type_1, type_2)
|
115
|
-
end
|
116
|
-
|
117
|
-
# text
|
118
|
-
if opts_1[:text] != opts_2[:text]
|
119
|
-
# This can only be of type String.
|
120
|
-
result_opts[:text] = true
|
121
|
-
result_opts.delete(:size)
|
122
|
-
end
|
123
|
-
|
124
|
-
# size
|
125
|
-
if !result_opts[:text] && opts_1[:size] != opts_2[:size]
|
126
|
-
types = [type_1, type_2].uniq
|
127
|
-
if types.length == 1 && types[0] == BigDecimal
|
128
|
-
# Two decimals
|
129
|
-
if opts_1.has_key?(:size) && opts_2.has_key?(:size)
|
130
|
-
s_1 = opts_1[:size]
|
131
|
-
s_2 = opts_2[:size]
|
132
|
-
result_opts[:size] = [ s_1[0] > s_2[0] ? s_1[0] : s_2[0] ]
|
133
|
-
|
134
|
-
if s_1[1] && s_2[1]
|
135
|
-
result_opts[:size][1] = s_1[1] > s_2[1] ? s_1[1] : s_2[1]
|
136
|
-
else
|
137
|
-
result_opts[:size][1] = s_1[1] ? s_1[1] : s_2[1]
|
138
|
-
end
|
139
|
-
else
|
140
|
-
result_opts[:size] = opts_1.has_key?(:size) ? opts_1[:size] : opts_2[:size]
|
141
|
-
end
|
142
|
-
elsif types.include?(String) && types.include?(BigDecimal)
|
143
|
-
# Add one to the precision of the BigDecimal (for the dot)
|
144
|
-
if opts_1.has_key?(:size) && opts_2.has_key?(:size)
|
145
|
-
s_1 = opts_1[:size].is_a?(Array) ? opts_1[:size][0] + 1 : opts_1[:size]
|
146
|
-
s_2 = opts_2[:size].is_a?(Array) ? opts_2[:size][0] + 1 : opts_2[:size]
|
147
|
-
result_opts[:size] = s_1 > s_2 ? s_1 : s_2
|
148
|
-
elsif opts_1.has_key?(:size)
|
149
|
-
result_opts[:size] = opts_1[:size].is_a?(Array) ? opts_1[:size][0] + 1 : opts_1[:size]
|
150
|
-
elsif opts_2.has_key?(:size)
|
151
|
-
result_opts[:size] = opts_2[:size].is_a?(Array) ? opts_2[:size][0] + 1 : opts_2[:size]
|
152
|
-
end
|
153
|
-
else
|
154
|
-
# Treat as two strings
|
155
|
-
if opts_1.has_key?(:size) && opts_2.has_key?(:size)
|
156
|
-
result_opts[:size] = opts_1[:size] > opts_2[:size] ? opts_1[:size] : opts_2[:size]
|
157
|
-
elsif opts_1.has_key?(:size)
|
158
|
-
result_opts[:size] = opts_1[:size]
|
159
|
-
else
|
160
|
-
result_opts[:size] = opts_2[:size]
|
161
|
-
end
|
162
|
-
end
|
163
|
-
end
|
164
|
-
|
165
|
-
# fixed
|
166
|
-
if opts_1[:fixed] != opts_2[:fixed]
|
167
|
-
# This can only be of type String.
|
168
|
-
result_opts[:fixed] = true
|
169
|
-
end
|
170
|
-
|
171
|
-
result = {:type => result_type}
|
172
|
-
result[:opts] = result_opts unless result_opts.empty?
|
173
|
-
end
|
174
|
-
|
175
|
-
if new_name
|
176
|
-
name = new_name.to_sym
|
177
|
-
else
|
178
|
-
name = self.name == other.name ? self.name : :"#{self.name}_#{other.name}"
|
179
|
-
end
|
180
|
-
Field.new(name, nil, result)
|
181
|
-
end
|
182
|
-
|
183
|
-
# Returns true if this field's name and dataset match the other's name
|
184
|
-
# and dataset (using {Dataset#==})
|
185
|
-
def ==(other)
|
186
|
-
if !other.is_a?(Field)
|
187
|
-
super
|
188
|
-
else
|
189
|
-
self.name == other.name && self.dataset == other.dataset
|
190
|
-
end
|
76
|
+
def to_expr
|
77
|
+
@name
|
191
78
|
end
|
192
79
|
|
193
|
-
|
194
|
-
|
195
|
-
#
|
196
|
-
# @param [Linkage::Dataset]
|
197
|
-
def belongs_to?(dataset)
|
198
|
-
self.dataset.id == dataset.id
|
80
|
+
def static?
|
81
|
+
false
|
199
82
|
end
|
200
83
|
|
201
84
|
def primary_key?
|
202
85
|
schema && schema[:primary_key]
|
203
86
|
end
|
204
|
-
|
205
|
-
private
|
206
|
-
|
207
|
-
def first_common_type(type_1, type_2)
|
208
|
-
types_1 = [type_1] + get_types(type_1)
|
209
|
-
types_2 = [type_2] + get_types(type_2)
|
210
|
-
(types_1 & types_2).first
|
211
|
-
end
|
212
|
-
|
213
|
-
# Get all types that the specified type can be converted to. Order
|
214
|
-
# matters.
|
215
|
-
def get_types(type)
|
216
|
-
result = []
|
217
|
-
types = TYPE_CONVERSION_TREE[type]
|
218
|
-
if types
|
219
|
-
result += types
|
220
|
-
types.each do |t|
|
221
|
-
result |= get_types(t)
|
222
|
-
end
|
223
|
-
end
|
224
|
-
result
|
225
|
-
end
|
226
87
|
end
|
227
88
|
end
|