Skip to content

Commit 6d05e26

Browse files
committed
Add ActiveRecord extensions to coerce :date/:time types from columns. Endowed SQLServerColumn to know about it's #table_name and hence its matching (best pratice) ActiveRecord class name. Use this along with custom #simplified_datetime hooked into #simplified_type case statment. Will put in README shortly.
1 parent 0a3231e commit 6d05e26

File tree

5 files changed

+91
-16
lines changed

5 files changed

+91
-16
lines changed

lib/active_record/connection_adapters/sqlserver_adapter.rb

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,23 @@ def self.sqlserver_connection(config) #:nodoc:
3232

3333
module ConnectionAdapters
3434

35-
class SQLServerColumn < Column #:nodoc:
36-
35+
class SQLServerColumn < Column
36+
3737
def initialize(name, default, sql_type = nil, null = true, sqlserver_options = {})
38-
super(name, default, sql_type, null)
3938
@sqlserver_options = sqlserver_options
39+
super(name, default, sql_type, null)
40+
end
41+
42+
class << self
43+
44+
def string_to_binary(value)
45+
"0x#{value.unpack("H*")[0]}"
46+
end
47+
48+
def binary_to_string(value)
49+
value =~ /[^[:xdigit:]]/ ? value : [value].pack('H*')
50+
end
51+
4052
end
4153

4254
def is_identity?
@@ -52,17 +64,14 @@ def is_utf8?
5264
sql_type =~ /nvarchar|ntext|nchar|nvarchar(max)/i
5365
end
5466

55-
class << self
56-
57-
def string_to_binary(value)
58-
"0x#{value.unpack("H*")[0]}"
59-
end
60-
61-
def binary_to_string(value)
62-
value =~ /[^[:xdigit:]]/ ? value : [value].pack('H*')
63-
end
64-
65-
end #class << self
67+
def table_name
68+
@sqlserver_options[:table_name]
69+
end
70+
71+
def table_klass
72+
@table_klass ||= table_name.classify.constantize rescue nil
73+
(@table_klass && @table_klass < ActiveRecord::Base) ? @table_klass : nil
74+
end
6675

6776
private
6877

@@ -82,10 +91,21 @@ def simplified_type(field_type)
8291
when /image/i then :binary
8392
when /bit/i then :boolean
8493
when /uniqueidentifier/i then :string
94+
when /datetime/i then simplified_datetime
8595
else super
8696
end
8797
end
8898

99+
def simplified_datetime
100+
if table_klass && table_klass.coerced_sqlserver_date_columns.include?(name)
101+
:date
102+
elsif table_klass && table_klass.coerced_sqlserver_time_columns.include?(name)
103+
:time
104+
else
105+
:datetime
106+
end
107+
end
108+
89109
end #SQLServerColumn
90110

91111
# In ADO mode, this adapter will ONLY work on Windows systems,
@@ -818,6 +838,7 @@ def column_definitions(table_name)
818838
else
819839
"#{ci[:type]}(#{ci[:length]})"
820840
end
841+
ci[:table_name] = table_name
821842
ci[:default_value] = ci[:default_value].match(/\A\(+N?'?(.*?)'?\)+\Z/)[1] if ci[:default_value]
822843
ci[:null] = ci[:is_nullable].to_i == 1 ; ci.delete(:is_nullable)
823844
ci

lib/core_ext/active_record.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,22 @@ class << klass
1212

1313
module ClassMethods
1414

15+
def coerce_sqlserver_date(*attributes)
16+
write_inheritable_attribute :coerced_sqlserver_date_columns, Set.new(attributes.map(&:to_s))
17+
end
18+
19+
def coerce_sqlserver_time(*attributes)
20+
write_inheritable_attribute :coerced_sqlserver_time_columns, Set.new(attributes.map(&:to_s))
21+
end
22+
23+
def coerced_sqlserver_date_columns
24+
read_inheritable_attribute(:coerced_sqlserver_date_columns) || []
25+
end
26+
27+
def coerced_sqlserver_time_columns
28+
read_inheritable_attribute(:coerced_sqlserver_time_columns) || []
29+
end
30+
1531
def reset_column_information_with_sqlserver_columns_cache_support
1632
connection.instance_variable_set :@sqlserver_columns_cache, {}
1733
reset_column_information_without_sqlserver_columns_cache_support

test/cases/adapter_test_sqlserver.rb

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ def setup
106106
assert !@connection.send(:insert_sql?,'UPDATE...')
107107
assert !@connection.send(:insert_sql?,'SELECT...')
108108
end
109-
109+
110110
context 'for #sql_for_association_limiting?' do
111111

112112
should 'return false for simple selects with no GROUP BY and ORDER BY' do
@@ -216,6 +216,33 @@ def setup
216216

217217
end
218218

219+
context 'which have coerced types' do
220+
221+
setup do
222+
christmas_08 = "2008-12-25".to_time
223+
christmas_08_afternoon = "2008-12-25 12:00".to_time
224+
@chronic_date = SqlServerChronic.create!(:date => christmas_08).reload
225+
@chronic_time = SqlServerChronic.create!(:time => christmas_08_afternoon).reload
226+
end
227+
228+
should 'have an inheritable attribute ' do
229+
assert SqlServerChronic.coerced_sqlserver_date_columns.include?('date')
230+
end
231+
232+
should 'have column and objects cast to date' do
233+
date_column = SqlServerChronic.columns_hash['date']
234+
assert_equal :date, date_column.type, "This column: \n#{date_column.inspect}"
235+
assert_instance_of Date, @chronic_date.date
236+
end
237+
238+
should 'have column objects cast to time' do
239+
time_column = SqlServerChronic.columns_hash['time']
240+
assert_equal :time, time_column.type, "This column: \n#{time_column.inspect}"
241+
assert_instance_of Time, @chronic_time.time
242+
end
243+
244+
end
245+
219246
end
220247

221248
context 'For identity inserts' do

test/cases/column_test_sqlserver.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,13 @@ def setup
1212
assert_equal :float, TableWithRealColumn.columns_hash["real_number"].type
1313
end
1414

15+
should 'know its #table_name and #table_klass' do
16+
Topic.columns.each do |column|
17+
assert_equal 'topics', column.table_name, "This column #{column.inspect} did not know it's #table_name"
18+
assert_equal Topic, column.table_klass, "This column #{column.inspect} did not know it's #table_klass"
19+
end
20+
end
21+
1522
context 'For :binary columns' do
1623

1724
setup do

test/cases/sqlserver_helper.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,11 @@ class TableWithRealColumn < ActiveRecord::Base; end
1616
class FkTestHasFk < ActiveRecord::Base ; end
1717
class FkTestHasPk < ActiveRecord::Base ; end
1818
class NumericData < ActiveRecord::Base ; self.table_name = 'numeric_data' ; end
19-
class SqlServerChronic < ActiveRecord::Base ; default_timezone = :utc ; end
19+
class SqlServerChronic < ActiveRecord::Base
20+
coerce_sqlserver_date :date
21+
coerce_sqlserver_time :time
22+
default_timezone = :utc
23+
end
2024

2125
# A module that we can include in classes where we want to override an active record test.
2226

0 commit comments

Comments
 (0)