linkage 0.0.8 → 0.1.0.pre

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.
Files changed (105) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +1 -0
  3. data/.yardopts +1 -0
  4. data/Gemfile +1 -19
  5. data/Gemfile-java +3 -0
  6. data/README.markdown +88 -34
  7. data/Rakefile +16 -15
  8. data/TODO +4 -0
  9. data/lib/linkage/comparator.rb +139 -144
  10. data/lib/linkage/comparators/compare.rb +236 -29
  11. data/lib/linkage/comparators/strcompare.rb +85 -0
  12. data/lib/linkage/comparators/within.rb +24 -20
  13. data/lib/linkage/configuration.rb +44 -466
  14. data/lib/linkage/dataset.rb +28 -127
  15. data/lib/linkage/exceptions.rb +5 -0
  16. data/lib/linkage/field.rb +6 -37
  17. data/lib/linkage/field_set.rb +3 -3
  18. data/lib/linkage/match_recorder.rb +22 -0
  19. data/lib/linkage/match_set.rb +34 -0
  20. data/lib/linkage/match_sets/csv.rb +39 -0
  21. data/lib/linkage/match_sets/database.rb +45 -0
  22. data/lib/linkage/matcher.rb +30 -0
  23. data/lib/linkage/result_set.rb +25 -110
  24. data/lib/linkage/result_sets/csv.rb +54 -0
  25. data/lib/linkage/result_sets/database.rb +42 -0
  26. data/lib/linkage/runner.rb +57 -16
  27. data/lib/linkage/score_recorder.rb +30 -0
  28. data/lib/linkage/score_set.rb +49 -0
  29. data/lib/linkage/score_sets/csv.rb +64 -0
  30. data/lib/linkage/score_sets/database.rb +77 -0
  31. data/lib/linkage/version.rb +1 -1
  32. data/lib/linkage.rb +14 -17
  33. data/linkage.gemspec +13 -1
  34. data/linkage.gemspec-java +32 -0
  35. data/test/helper.rb +30 -23
  36. data/test/integration/test_cross_linkage.rb +46 -25
  37. data/test/integration/test_database_result_set.rb +55 -0
  38. data/test/integration/test_dual_linkage.rb +19 -94
  39. data/test/integration/test_self_linkage.rb +100 -203
  40. data/test/integration/test_within_comparator.rb +24 -77
  41. data/test/unit/comparators/test_compare.rb +254 -50
  42. data/test/unit/comparators/test_strcompare.rb +45 -0
  43. data/test/unit/comparators/test_within.rb +14 -26
  44. data/test/unit/match_sets/test_csv.rb +78 -0
  45. data/test/unit/match_sets/test_database.rb +63 -0
  46. data/test/unit/result_sets/test_csv.rb +111 -0
  47. data/test/unit/result_sets/test_database.rb +68 -0
  48. data/test/unit/score_sets/test_csv.rb +151 -0
  49. data/test/unit/score_sets/test_database.rb +149 -0
  50. data/test/unit/test_comparator.rb +46 -83
  51. data/test/unit/test_comparators.rb +4 -0
  52. data/test/unit/test_configuration.rb +99 -145
  53. data/test/unit/test_dataset.rb +52 -73
  54. data/test/unit/test_field.rb +4 -55
  55. data/test/unit/test_field_set.rb +6 -6
  56. data/test/unit/test_match_recorder.rb +23 -0
  57. data/test/unit/test_match_set.rb +23 -0
  58. data/test/unit/test_match_sets.rb +4 -0
  59. data/test/unit/test_matcher.rb +44 -0
  60. data/test/unit/test_result_set.rb +24 -223
  61. data/test/unit/test_result_sets.rb +4 -0
  62. data/test/unit/test_runner.rb +122 -17
  63. data/test/unit/test_runners.rb +4 -0
  64. data/test/unit/test_score_recorder.rb +25 -0
  65. data/test/unit/test_score_set.rb +37 -0
  66. data/test/unit/test_score_sets.rb +4 -0
  67. metadata +183 -90
  68. data/Gemfile.lock +0 -92
  69. data/lib/linkage/comparators/binary.rb +0 -12
  70. data/lib/linkage/data.rb +0 -175
  71. data/lib/linkage/decollation.rb +0 -93
  72. data/lib/linkage/expectation.rb +0 -21
  73. data/lib/linkage/expectations/exhaustive.rb +0 -63
  74. data/lib/linkage/expectations/simple.rb +0 -168
  75. data/lib/linkage/function.rb +0 -148
  76. data/lib/linkage/functions/binary.rb +0 -30
  77. data/lib/linkage/functions/cast.rb +0 -54
  78. data/lib/linkage/functions/length.rb +0 -29
  79. data/lib/linkage/functions/strftime.rb +0 -33
  80. data/lib/linkage/functions/trim.rb +0 -30
  81. data/lib/linkage/group.rb +0 -55
  82. data/lib/linkage/meta_object.rb +0 -139
  83. data/lib/linkage/runner/single_threaded.rb +0 -187
  84. data/lib/linkage/utils.rb +0 -164
  85. data/lib/linkage/warnings.rb +0 -5
  86. data/test/integration/test_collation.rb +0 -45
  87. data/test/integration/test_configuration.rb +0 -268
  88. data/test/integration/test_dataset.rb +0 -116
  89. data/test/integration/test_functions.rb +0 -88
  90. data/test/integration/test_result_set.rb +0 -85
  91. data/test/integration/test_scoring.rb +0 -84
  92. data/test/unit/expectations/test_exhaustive.rb +0 -111
  93. data/test/unit/expectations/test_simple.rb +0 -303
  94. data/test/unit/functions/test_binary.rb +0 -54
  95. data/test/unit/functions/test_cast.rb +0 -98
  96. data/test/unit/functions/test_length.rb +0 -52
  97. data/test/unit/functions/test_strftime.rb +0 -60
  98. data/test/unit/functions/test_trim.rb +0 -43
  99. data/test/unit/runner/test_single_threaded.rb +0 -12
  100. data/test/unit/test_data.rb +0 -445
  101. data/test/unit/test_decollation.rb +0 -201
  102. data/test/unit/test_function.rb +0 -233
  103. data/test/unit/test_group.rb +0 -38
  104. data/test/unit/test_meta_object.rb +0 -208
  105. data/test/unit/test_utils.rb +0 -341
@@ -1,93 +0,0 @@
1
- # encoding: utf-8
2
- module Linkage
3
- module Decollation
4
- def decollate(string, database_type, collation)
5
- case database_type
6
- when :mysql
7
- decollate_mysql(string, collation)
8
- else
9
- string
10
- end
11
- end
12
-
13
- def decollate_mysql(string, collation)
14
- case collation
15
- when "latin1_swedish_ci"
16
- decollate_mysql_latin1_swedish_ci(string)
17
- else
18
- string
19
- end
20
- end
21
-
22
- def decollate_mysql_latin1_swedish_ci(string)
23
- result = string.strip
24
- result.each_char.with_index do |char, i|
25
- case char
26
- when 'A', 'a', 'À', 'Á', 'Â', 'Ã', 'à', 'á', 'â', 'ã'
27
- result[i] = 'A'
28
- when 'B', 'b'
29
- result[i] = 'B'
30
- when 'C', 'c', 'Ç', 'ç'
31
- result[i] = 'C'
32
- when 'D', 'd', 'Ð', 'ð'
33
- result[i] = 'D'
34
- when 'E', 'e', 'È', 'É', 'Ê', 'Ë', 'è', 'é', 'ê', 'ë'
35
- result[i] = 'E'
36
- when 'F', 'f'
37
- result[i] = 'F'
38
- when 'G', 'g'
39
- result[i] = 'G'
40
- when 'H', 'h'
41
- result[i] = 'H'
42
- when 'I', 'i', 'Ì', 'Í', 'Î', 'Ï', 'ì', 'í', 'î', 'ï'
43
- result[i] = 'I'
44
- when 'J', 'j'
45
- result[i] = 'J'
46
- when 'K', 'k'
47
- result[i] = 'K'
48
- when 'L', 'l'
49
- result[i] = 'L'
50
- when 'M', 'm'
51
- result[i] = 'M'
52
- when 'N', 'n', 'Ñ', 'ñ'
53
- result[i] = 'N'
54
- when 'O', 'o', 'Ò', 'Ó', 'Ô', 'Õ', 'ò', 'ó', 'ô', 'õ'
55
- result[i] = 'O'
56
- when 'P', 'p'
57
- result[i] = 'P'
58
- when 'Q', 'q'
59
- result[i] = 'Q'
60
- when 'R', 'r'
61
- result[i] = 'R'
62
- when 'S', 's'
63
- result[i] = 'S'
64
- when 'T', 't'
65
- result[i] = 'T'
66
- when 'U', 'u', 'Ù', 'Ú', 'Û', 'ù', 'ú', 'û'
67
- result[i] = 'U'
68
- when 'V', 'v'
69
- result[i] = 'V'
70
- when 'W', 'w'
71
- result[i] = 'W'
72
- when 'X', 'x'
73
- result[i] = 'X'
74
- when 'Y', 'y', 'Ü', 'Ý', 'ü', 'ý'
75
- result[i] = 'Y'
76
- when 'Z', 'z'
77
- result[i] = 'Z'
78
- when '[', 'Å', 'å'
79
- result[i] = '['
80
- when '\\', 'Ä', 'Æ', 'ä', 'æ'
81
- result[i] = '\\'
82
- when ']', 'Ö', 'ö'
83
- result[i] = ']'
84
- when 'Ø', 'ø'
85
- result[i] = 'Ø'
86
- when 'Þ', 'þ'
87
- result[i] = 'Þ'
88
- end
89
- end
90
- result
91
- end
92
- end
93
- end
@@ -1,21 +0,0 @@
1
- module Linkage
2
- # The Expectation class contains information about how two datasets
3
- # should be linked.
4
- class Expectation
5
- def kind
6
- raise NotImplementedError
7
- end
8
-
9
- def apply_to(*args)
10
- raise NotImplementedError
11
- end
12
-
13
- def decollation_needed?
14
- false
15
- end
16
- end
17
- end
18
-
19
- Dir.glob(File.expand_path(File.join(File.dirname(__FILE__), "expectations", "*.rb"))).each do |filename|
20
- require filename
21
- end
@@ -1,63 +0,0 @@
1
- module Linkage
2
- module Expectations
3
- class Exhaustive < Expectation
4
- attr_reader :comparator, :threshold, :mode
5
-
6
- def initialize(comparator, threshold, mode)
7
- @comparator = comparator
8
- @threshold = threshold
9
- @mode = mode
10
- end
11
-
12
- def kind
13
- if @kind.nil?
14
- if @comparator.lhs_args.length != @comparator.rhs_args.length
15
- @kind = :cross
16
- else
17
- @kind = :self
18
- @comparator.lhs_args.each_with_index do |lhs_arg, index|
19
- rhs_arg = @comparator.rhs_args[index]
20
- if !lhs_arg.objects_equal?(rhs_arg)
21
- @kind = :cross
22
- break
23
- end
24
- end
25
- end
26
-
27
- # Check for dual-linkage.
28
- if @kind == :cross
29
- # Assume that all lhs arguments have the same dataset, as well
30
- # as all the rhs arguments. Only check the first argument of each
31
- # side.
32
- lhs_arg = @comparator.lhs_args[0]
33
- rhs_arg = @comparator.rhs_args[0]
34
- if !lhs_arg.datasets_equal?(rhs_arg)
35
- @kind = :dual
36
- end
37
- end
38
- end
39
- @kind
40
- end
41
-
42
- def apply_to(dataset, side)
43
- exprs =
44
- case side
45
- when :lhs
46
- comparator.lhs_args.collect { |arg| arg.to_expr.as(arg.name) }
47
- when :rhs
48
- comparator.rhs_args.collect { |arg| arg.to_expr.as(arg.name) }
49
- end
50
- dataset.select_more(*exprs)
51
- end
52
-
53
- def satisfied?(score)
54
- case mode
55
- when :equal
56
- score == threshold
57
- when :min
58
- score >= threshold
59
- end
60
- end
61
- end
62
- end
63
- end
@@ -1,168 +0,0 @@
1
- module Linkage
2
- module Expectations
3
- class Simple < Expectation
4
- # The dataset this expectation applies to: `:lhs` or `:rhs`. This
5
- # only applies to filter expectations.
6
- # @return [Symbol]
7
- attr_reader :side
8
-
9
- attr_reader :meta_object_1, :meta_object_2, :operator
10
-
11
- VALID_OPERATORS = [:==, :'!=', :>, :<, :>=, :<=]
12
-
13
- # Automatically create an expectation type depending on the arguments.
14
- #
15
- # @param [Linkage::MetaObject] meta_object_1
16
- # @param [Linkage::MetaObject] meta_object_2
17
- # @param [Symbol] operator Valid operators: `:==`, `:'!='`, `:>`, `:<`, `:>=`, `:<=`
18
- def self.create(meta_object_1, meta_object_2, operator)
19
- klass =
20
- if meta_object_1.static? && meta_object_2.static?
21
- raise ArgumentError, "An expectation with two static objects is invalid"
22
- elsif meta_object_1.static? || meta_object_2.static?
23
- Filter
24
- elsif meta_object_1.side == meta_object_2.side
25
- if !meta_object_1.datasets_equal?(meta_object_2)
26
- raise ArgumentError, "An expectation with two dynamic objects with the same side but different datasets is invalid"
27
- end
28
- Filter
29
- elsif meta_object_1.objects_equal?(meta_object_2)
30
- Self
31
- elsif meta_object_1.datasets_equal?(meta_object_2)
32
- Cross
33
- else
34
- Dual
35
- end
36
-
37
- klass.new(meta_object_1, meta_object_2, operator)
38
- end
39
-
40
- # Creates a new Simple.
41
- #
42
- # @param [Linkage::MetaObject] meta_object_1
43
- # @param [Linkage::MetaObject] meta_object_2
44
- # @param [Symbol] operator Valid operators: `:==`, `:'!='`, `:>`, `:<`, `:>=`, `:<=`
45
- def initialize(meta_object_1, meta_object_2, operator)
46
- @meta_object_1 = meta_object_1
47
- @meta_object_2 = meta_object_2
48
- @operator = operator
49
-
50
- if !VALID_OPERATORS.include?(operator)
51
- raise ArgumentError, "Invalid operator: #{operator.inspect}"
52
- end
53
-
54
- after_initialize
55
- end
56
-
57
- def same_except_side?(other)
58
- other.is_a?(Simple) &&
59
- operator == other.operator &&
60
- meta_object_1.objects_equal?(other.meta_object_1) &&
61
- meta_object_2.objects_equal?(other.meta_object_2)
62
- end
63
-
64
- def exactly!
65
- function_1 = Function['binary'].new(@meta_object_1.object, :dataset => @meta_object_1.dataset)
66
- function_2 = Function['binary'].new(@meta_object_2.object, :dataset => @meta_object_2.dataset)
67
- @meta_object_1 = MetaObject.new(function_1, @meta_object_1.side)
68
- @meta_object_2 = MetaObject.new(function_2, @meta_object_2.side)
69
- end
70
-
71
- # Display any warnings about this expectation.
72
- def display_warnings
73
- end
74
-
75
- def decollation_needed?
76
- merged_field.ruby_type[:type] == String && (
77
- @meta_object_1.collation != @meta_object_2.collation ||
78
- @meta_object_1.database_type != @meta_object_2.database_type
79
- )
80
- end
81
-
82
- protected
83
-
84
- def after_initialize
85
- end
86
- end
87
-
88
- class Filter < Simple
89
- def kind; :filter; end
90
-
91
- def to_expr
92
- case @operator
93
- when :==, :'!='
94
- expr = { @meta_object_1.to_expr => @meta_object_2.to_expr }
95
- @operator == :== ? expr : ~expr
96
- else
97
- Sequel::SQL::BooleanExpression.new(@operator,
98
- @meta_object_1.to_identifier, @meta_object_2.to_identifier)
99
- end
100
- end
101
-
102
- def apply_to(dataset, side)
103
- if side != @side
104
- return dataset
105
- end
106
-
107
- dataset.filter(self.to_expr)
108
- end
109
-
110
- def decollation_needed?
111
- false
112
- end
113
-
114
- private
115
-
116
- def after_initialize
117
- super
118
- @side = @meta_object_1.static? ? @meta_object_2.side : @meta_object_1.side
119
- end
120
- end
121
-
122
- class Match < Simple
123
- def apply_to(dataset, side)
124
- target =
125
- if @meta_object_1.side == side
126
- @meta_object_1
127
- elsif @meta_object_2.side == side
128
- @meta_object_2
129
- else
130
- raise ArgumentError, "Invalid `side` argument: #{side}"
131
- end
132
-
133
- dataset.group_match_more({
134
- :meta_object => target,
135
- :alias => merged_field.name
136
- })
137
- end
138
-
139
- def merged_field
140
- @merged_field ||= @meta_object_1.merge(@meta_object_2)
141
- end
142
-
143
- def display_warnings
144
- object_1 = @meta_object_1.object
145
- object_2 = @meta_object_2.object
146
- if object_1.ruby_type[:type] == String && object_2.ruby_type[:type] == String
147
- if @meta_object_1.dataset.database_type != @meta_object_2.dataset.database_type
148
- warn "NOTE: You are comparing two string fields (#{object_1.name} and #{object_2.name}) from different databases. This may result in unexpected results, as different databases compare strings differently. Consider using the =binary= function."
149
- elsif object_1.respond_to?(:collation) && object_1.respond_to?(:collation) && object_1.collation != object_2.collation
150
- warn "NOTE: The two string fields you are comparing (#{object_1.name} and #{object_2.name}) have different collations (#{ldata.collation} vs. #{rdata.collation}). This may result in unexpected results, as the database may compare them differently. Consider using the =exactly= method."
151
- end
152
- end
153
- end
154
- end
155
-
156
- class Self < Match
157
- def kind; :self; end
158
- end
159
-
160
- class Cross < Match
161
- def kind; :cross; end
162
- end
163
-
164
- class Dual < Match
165
- def kind; :dual; end
166
- end
167
- end
168
- end
@@ -1,148 +0,0 @@
1
- module Linkage
2
- # Abstract class to represent SQL functions. No attempts are made to
3
- # ensure that the function actually exists in the database you're using.
4
- #
5
- # @abstract
6
- class Function < Data
7
- # Register a new function.
8
- #
9
- # @param [Class] klass Function class (probably a subclass of {Function})
10
- def self.register(klass)
11
- if klass.instance_methods(false).any? { |m| m.to_s == "ruby_type" }
12
- @functions ||= {}
13
- @functions[klass.function_name] = klass
14
- else
15
- raise ArgumentError, "ruby_type instance method must be defined"
16
- end
17
- end
18
-
19
- def self.[](name)
20
- @functions ? @functions[name] : nil
21
- end
22
-
23
- # Subclasses must define this.
24
- def self.function_name
25
- raise NotImplementedError
26
- end
27
-
28
- # Subclasses can define this to require a specific number of arguments
29
- # of a certain class. To require two parameters of either String or
30
- # Integer, do something like this:
31
- #
32
- # @@parameters = [[String, Integer], [String, Integer]]
33
- # def self.parameters
34
- # @@parameters
35
- # end
36
- #
37
- def self.parameters
38
- nil
39
- end
40
-
41
- attr_reader :args
42
-
43
- # Creates a new Function object. If the arguments contain only
44
- # static objects, you should specify the dataset that this function
45
- # belongs to as the last argument like so:
46
- #
47
- # Function.new(foo, bar, :dataset => dataset)
48
- #
49
- # Optionally, you can use the `dataset=` setter to do it later. Many
50
- # functions require a dataset to work properly. If you try to use
51
- # such a function without setting a dataset, it will raise a RuntimeError.
52
- #
53
- # @param [Linkage::Data, Object] args Function arguments
54
- def initialize(*args)
55
- @names = [self.class.function_name]
56
- @args = args
57
- @options = args.last.is_a?(Hash) ? args.pop : {}
58
- process_args
59
- end
60
-
61
- def name
62
- @name ||= @names.join("_").to_sym
63
- end
64
-
65
- def dataset
66
- if @dataset.nil?
67
- raise RuntimeError, "You must specify a dataset for static functions"
68
- end
69
- @dataset
70
- end
71
-
72
- def dataset=(dataset)
73
- @dataset = dataset
74
- end
75
-
76
- def static?
77
- @static
78
- end
79
-
80
- def ==(other)
81
- equal?(other) || (other.is_a?(Function) && name == other.name && args == other.args && dataset == other.dataset)
82
- end
83
-
84
- # Subclasses must define this. The return value should be a Hash with
85
- # the following elements:
86
- # :type - column type (Ruby class) of the result
87
- # :opts - Optional hash with additional options (like :size)
88
- def ruby_type
89
- raise NotImplementedError
90
- end
91
-
92
- # Returns `nil` by default. Subclasses should redefine this if
93
- # there is a collation.
94
- def collation
95
- nil
96
- end
97
-
98
- # @return [Sequel::SQL::Function]
99
- def to_expr(options = {})
100
- self.class.function_name.to_sym.sql_function(*@values)
101
- end
102
-
103
- private
104
-
105
- def process_args
106
- parameters = self.class.parameters
107
- if parameters && parameters.length != @args.length
108
- raise ArgumentError, "wrong number of arguments (#{@args.length} for #{parameters.length})"
109
- end
110
-
111
- @static = true
112
- @values = []
113
- @args.each_with_index do |arg, i|
114
- if arg.kind_of?(Data)
115
- @names << arg.name
116
- @static &&= arg.static?
117
-
118
- # possibly set dataset
119
- if @dataset.nil?
120
- @dataset = arg.dataset
121
- elsif @dataset != arg.dataset
122
- raise ArgumentError, "Using dynamic data sources with different datasets is not permitted"
123
- end
124
-
125
- type = arg.ruby_type[:type]
126
- value = arg.to_expr
127
- else
128
- @names << arg.to_s.gsub(/\W/, "")
129
- type = arg.class
130
- value = arg
131
- end
132
- if parameters && parameters[i] != [:any] && !parameters[i].include?(type)
133
- raise TypeError, "expected type #{parameters[i].join(" or ")}, got #{type}"
134
- end
135
- @values << value
136
- end
137
-
138
- if @dataset.nil? && @options[:dataset]
139
- # Set dataset for static functions manually
140
- @dataset = @options[:dataset]
141
- end
142
- end
143
- end
144
- end
145
-
146
- Dir.glob(File.expand_path(File.join(File.dirname(__FILE__), "functions", "*.rb"))).each do |filename|
147
- require filename
148
- end
@@ -1,30 +0,0 @@
1
- module Linkage
2
- module Functions
3
- class Binary < Function
4
- def self.function_name
5
- "binary"
6
- end
7
-
8
- def self.parameters
9
- [[String]]
10
- end
11
-
12
- def ruby_type
13
- {:type => File}
14
- end
15
-
16
- def to_expr(options = {})
17
- expr =
18
- case dataset.database_type
19
- when :sqlite
20
- @values[0].cast(:blob)
21
- when :postgres
22
- @values[0].cast(:bytea)
23
- else
24
- @values[0].cast(:binary)
25
- end
26
- end
27
- end
28
- Function.register(Binary)
29
- end
30
- end
@@ -1,54 +0,0 @@
1
- module Linkage
2
- module Functions
3
- class Cast < Function
4
- def self.function_name
5
- "cast"
6
- end
7
-
8
- def self.parameters
9
- [[:any], [String]]
10
- end
11
-
12
- def ruby_type
13
- type =
14
- case @values[1]
15
- when 'integer'
16
- Fixnum
17
- when 'binary'
18
- File
19
- else
20
- raise "unknown type: #{@values[1]}"
21
- end
22
-
23
- {:type => type}
24
- end
25
-
26
- def to_expr(options = {})
27
- cast =
28
- case @values[1]
29
- when 'integer'
30
- case dataset.database_type
31
- when :sqlite, :postgres, :h2
32
- :integer
33
- when :mysql
34
- :signed
35
- end
36
- when 'binary'
37
- case dataset.database_type
38
- when :sqlite
39
- :blob
40
- when :postgres
41
- :bytea
42
- when :mysql, :h2
43
- :binary
44
- end
45
- end
46
-
47
- if cast
48
- @values[0].cast(cast)
49
- end
50
- end
51
- end
52
- Function.register(Cast)
53
- end
54
- end
@@ -1,29 +0,0 @@
1
- module Linkage
2
- module Functions
3
- # Returns the number of characters in a string.
4
- class Length < Function
5
- def self.function_name
6
- "length"
7
- end
8
-
9
- def self.parameters
10
- [[String]]
11
- end
12
-
13
- def ruby_type
14
- {:type => Fixnum}
15
- end
16
-
17
- def to_expr(options = {})
18
- expr =
19
- case dataset.database_type
20
- when :mysql, :postgres
21
- :char_length.sql_function(@values[0])
22
- else
23
- :length.sql_function(@values[0])
24
- end
25
- end
26
- end
27
- Function.register(Length)
28
- end
29
- end
@@ -1,33 +0,0 @@
1
- module Linkage
2
- module Functions
3
- class Strftime < Function
4
- def self.function_name
5
- "strftime"
6
- end
7
-
8
- def self.parameters
9
- [[Date, Time, DateTime], [String]]
10
- end
11
-
12
- def ruby_type
13
- # TODO: string length needed
14
- {:type => String}
15
- end
16
-
17
- def to_expr(options = {})
18
- expr =
19
- case dataset.database_type
20
- when :mysql
21
- :date_format.sql_function(*@values)
22
- when :sqlite
23
- :strftime.sql_function(@values[1], @values[0])
24
- when :postgres
25
- :to_char.sql_function(*@values)
26
- else
27
- :strftime.sql_function(@values[0], @values[1])
28
- end
29
- end
30
- end
31
- Function.register(Strftime)
32
- end
33
- end
@@ -1,30 +0,0 @@
1
- module Linkage
2
- module Functions
3
- class Trim < Function
4
- def self.function_name
5
- "trim"
6
- end
7
-
8
- def self.parameters
9
- [[String]]
10
- end
11
-
12
- def ruby_type
13
- if @args[0].kind_of?(Data)
14
- @args[0].ruby_type
15
- else
16
- {:type => String}
17
- end
18
- end
19
-
20
- def collation
21
- if @args[0].kind_of?(Data)
22
- @args[0].collation
23
- else
24
- super
25
- end
26
- end
27
- end
28
- Function.register(Trim)
29
- end
30
- end