chrono_model 1.2.2 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (95) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +19 -20
  3. data/README.md +62 -40
  4. data/lib/active_record/connection_adapters/chronomodel_adapter.rb +17 -11
  5. data/lib/active_record/tasks/chronomodel_database_tasks.rb +64 -23
  6. data/lib/chrono_model/adapter/ddl.rb +168 -153
  7. data/lib/chrono_model/adapter/indexes.rb +99 -94
  8. data/lib/chrono_model/adapter/migrations.rb +81 -104
  9. data/lib/chrono_model/adapter/migrations_modules/legacy.rb +41 -0
  10. data/lib/chrono_model/adapter/migrations_modules/stable.rb +41 -0
  11. data/lib/chrono_model/adapter/tsrange.rb +20 -5
  12. data/lib/chrono_model/adapter/upgrade.rb +89 -91
  13. data/lib/chrono_model/adapter.rb +64 -31
  14. data/lib/chrono_model/chrono.rb +17 -0
  15. data/lib/chrono_model/conversions.rb +15 -9
  16. data/lib/chrono_model/db_console.rb +9 -0
  17. data/lib/chrono_model/json.rb +9 -6
  18. data/lib/chrono_model/patches/as_of_time_holder.rb +2 -2
  19. data/lib/chrono_model/patches/as_of_time_relation.rb +2 -2
  20. data/lib/chrono_model/patches/association.rb +15 -12
  21. data/lib/chrono_model/patches/batches.rb +17 -0
  22. data/lib/chrono_model/patches/db_console.rb +20 -4
  23. data/lib/chrono_model/patches/join_node.rb +4 -4
  24. data/lib/chrono_model/patches/preloader.rb +41 -11
  25. data/lib/chrono_model/patches/relation.rb +53 -8
  26. data/lib/chrono_model/patches.rb +3 -1
  27. data/lib/chrono_model/railtie.rb +29 -24
  28. data/lib/chrono_model/time_gate.rb +3 -3
  29. data/lib/chrono_model/time_machine/history_model.rb +65 -31
  30. data/lib/chrono_model/time_machine/time_query.rb +65 -49
  31. data/lib/chrono_model/time_machine/timeline.rb +52 -28
  32. data/lib/chrono_model/time_machine.rb +66 -25
  33. data/lib/chrono_model/utilities.rb +3 -3
  34. data/lib/chrono_model/version.rb +3 -1
  35. data/lib/chrono_model.rb +31 -36
  36. metadata +39 -136
  37. data/.gitignore +0 -21
  38. data/.rspec +0 -2
  39. data/.travis.yml +0 -41
  40. data/Gemfile +0 -4
  41. data/README.sql +0 -161
  42. data/Rakefile +0 -25
  43. data/chrono_model.gemspec +0 -33
  44. data/gemfiles/rails_5.0.gemfile +0 -6
  45. data/gemfiles/rails_5.1.gemfile +0 -6
  46. data/gemfiles/rails_5.2.gemfile +0 -6
  47. data/spec/aruba/dbconsole_spec.rb +0 -25
  48. data/spec/aruba/fixtures/database_with_default_username_and_password.yml +0 -14
  49. data/spec/aruba/fixtures/database_without_username_and_password.yml +0 -11
  50. data/spec/aruba/fixtures/empty_structure.sql +0 -27
  51. data/spec/aruba/fixtures/migrations/56/20160812190335_create_impressions.rb +0 -10
  52. data/spec/aruba/fixtures/migrations/56/20171115195229_add_temporal_extension_to_impressions.rb +0 -10
  53. data/spec/aruba/fixtures/railsapp/config/application.rb +0 -17
  54. data/spec/aruba/fixtures/railsapp/config/boot.rb +0 -5
  55. data/spec/aruba/fixtures/railsapp/config/environments/development.rb +0 -38
  56. data/spec/aruba/migrations_spec.rb +0 -48
  57. data/spec/aruba/rake_task_spec.rb +0 -71
  58. data/spec/chrono_model/adapter/base_spec.rb +0 -157
  59. data/spec/chrono_model/adapter/ddl_spec.rb +0 -243
  60. data/spec/chrono_model/adapter/indexes_spec.rb +0 -72
  61. data/spec/chrono_model/adapter/migrations_spec.rb +0 -312
  62. data/spec/chrono_model/conversions_spec.rb +0 -43
  63. data/spec/chrono_model/history_models_spec.rb +0 -32
  64. data/spec/chrono_model/json_ops_spec.rb +0 -59
  65. data/spec/chrono_model/time_machine/as_of_spec.rb +0 -188
  66. data/spec/chrono_model/time_machine/changes_spec.rb +0 -50
  67. data/spec/chrono_model/time_machine/counter_cache_race_spec.rb +0 -46
  68. data/spec/chrono_model/time_machine/default_scope_spec.rb +0 -37
  69. data/spec/chrono_model/time_machine/history_spec.rb +0 -104
  70. data/spec/chrono_model/time_machine/keep_cool_spec.rb +0 -27
  71. data/spec/chrono_model/time_machine/manipulations_spec.rb +0 -84
  72. data/spec/chrono_model/time_machine/model_identification_spec.rb +0 -46
  73. data/spec/chrono_model/time_machine/sequence_spec.rb +0 -74
  74. data/spec/chrono_model/time_machine/sti_spec.rb +0 -100
  75. data/spec/chrono_model/time_machine/time_query_spec.rb +0 -261
  76. data/spec/chrono_model/time_machine/timeline_spec.rb +0 -63
  77. data/spec/chrono_model/time_machine/timestamps_spec.rb +0 -43
  78. data/spec/chrono_model/time_machine/transactions_spec.rb +0 -69
  79. data/spec/config.travis.yml +0 -5
  80. data/spec/config.yml.example +0 -9
  81. data/spec/spec_helper.rb +0 -33
  82. data/spec/support/adapter/helpers.rb +0 -53
  83. data/spec/support/adapter/structure.rb +0 -44
  84. data/spec/support/aruba.rb +0 -44
  85. data/spec/support/connection.rb +0 -70
  86. data/spec/support/matchers/base.rb +0 -56
  87. data/spec/support/matchers/column.rb +0 -99
  88. data/spec/support/matchers/function.rb +0 -79
  89. data/spec/support/matchers/index.rb +0 -69
  90. data/spec/support/matchers/schema.rb +0 -39
  91. data/spec/support/matchers/table.rb +0 -275
  92. data/spec/support/time_machine/helpers.rb +0 -47
  93. data/spec/support/time_machine/structure.rb +0 -111
  94. data/sql/json_ops.sql +0 -56
  95. data/sql/uninstall-json_ops.sql +0 -24
@@ -1,56 +0,0 @@
1
- module ChronoTest::Matchers
2
- class Base
3
- include ActiveRecord::Sanitization::ClassMethods
4
-
5
- attr_reader :table
6
-
7
- def matches?(table)
8
- table = table.table_name if table.respond_to?(:table_name)
9
- @table = table
10
- end
11
- private :matches? # This is an abstract class
12
-
13
- def failure_message_for_should_not
14
- failure_message_for_should.gsub(/to /, 'to not ')
15
- end
16
-
17
- protected
18
-
19
- def connection
20
- ChronoTest.connection
21
- end
22
-
23
- def temporal_schema
24
- ChronoModel::Adapter::TEMPORAL_SCHEMA
25
- end
26
-
27
- def history_schema
28
- ChronoModel::Adapter::HISTORY_SCHEMA
29
- end
30
-
31
- def public_schema
32
- 'public'
33
- end
34
-
35
- def select_value(sql, binds, name = nil)
36
- result = exec_query(sql, binds, name || 'select_value')
37
- result.rows.first.try(:[], 0)
38
- end
39
-
40
- def select_values(sql, binds, name = nil)
41
- result = exec_query(sql, binds, name || 'select_values')
42
- result.rows.map(&:first)
43
- end
44
-
45
- def select_rows(sql, binds, name = nil)
46
- exec_query(sql, binds, name || 'select_rows').rows
47
- end
48
-
49
- private
50
- def exec_query(sql, binds, name)
51
- sql = sanitize_sql_array([ sql, *Array.wrap(binds) ])
52
- connection.exec_query(sql, name)
53
- end
54
- end
55
-
56
- end
@@ -1,99 +0,0 @@
1
- module ChronoTest::Matchers
2
-
3
- module Column
4
- class HaveColumns < ChronoTest::Matchers::Base
5
- def initialize(columns, schema = 'public')
6
- @columns = columns
7
- @schema = schema
8
- end
9
-
10
- def description
11
- 'have columns'
12
- end
13
-
14
- def matches?(table)
15
- super(table)
16
-
17
- @matches = @columns.inject({}) do |h, (name, type)|
18
- h.update([name, type] => has_column?(name, type))
19
- end
20
-
21
- @matches.values.all?
22
- end
23
-
24
- def failure_message
25
- message_matches("expected #{@schema}.#{table} to have")
26
- end
27
-
28
- def failure_message_when_negated
29
- message_matches("expected #{@schema}.#{table} to not have")
30
- end
31
-
32
- protected
33
- def has_column?(name, type)
34
- column_type(name) == [name, type]
35
- end
36
-
37
- def column_type(name)
38
- table = "#{@schema}.#{self.table}"
39
-
40
- select_rows(<<-SQL, [table, name], 'Check column').first
41
- SELECT attname, FORMAT_TYPE(atttypid, atttypmod)
42
- FROM pg_attribute
43
- WHERE attrelid = ?::regclass::oid
44
- AND attname = ?
45
- SQL
46
- end
47
-
48
- private
49
- def message_matches(message)
50
- (message << ' ').tap do |message|
51
- message << @matches.map do |(name, type), match|
52
- "a #{name}(#{type}) column" unless match
53
- end.compact.to_sentence
54
- end
55
- end
56
- end
57
-
58
- def have_columns(*args)
59
- HaveColumns.new(*args)
60
- end
61
-
62
-
63
- class HaveTemporalColumns < HaveColumns
64
- def initialize(columns)
65
- super(columns, temporal_schema)
66
- end
67
- end
68
-
69
- def have_temporal_columns(*args)
70
- HaveTemporalColumns.new(*args)
71
- end
72
-
73
-
74
- class HaveHistoryColumns < HaveColumns
75
- def initialize(columns)
76
- super(columns, history_schema)
77
- end
78
- end
79
-
80
- def have_history_columns(*args)
81
- HaveHistoryColumns.new(*args)
82
- end
83
-
84
-
85
- class HaveHistoryExtraColumns < HaveColumns
86
- def initialize
87
- super([
88
- ['validity', 'tsrange'],
89
- ['recorded_at', 'timestamp without time zone'],
90
- ['hid', 'integer']
91
- ], history_schema)
92
- end
93
- end
94
-
95
- def have_history_extra_columns
96
- HaveHistoryExtraColumns.new
97
- end
98
- end
99
- end
@@ -1,79 +0,0 @@
1
- module ChronoTest::Matchers
2
-
3
- module Column
4
- class HaveFunctions < ChronoTest::Matchers::Base
5
- def initialize(functions, schema = 'public')
6
- @functions = functions
7
- @schema = schema
8
- end
9
-
10
- def description
11
- 'have functions'
12
- end
13
-
14
- def matches?(table)
15
- super(table)
16
-
17
- @matches = @functions.inject({}) do |h, name|
18
- h.update(name => has_function?(name))
19
- end
20
-
21
- @matches.values.all?
22
- end
23
-
24
- def failure_message
25
- message_matches("expected #{@schema}.#{table} to have")
26
- end
27
-
28
- def failure_message_when_negated
29
- message_matches("expected #{@schema}.#{table} to not have")
30
- end
31
-
32
- protected
33
- def has_function?(name)
34
- select_value(<<-SQL, [name], 'Check function') == true
35
- SELECT EXISTS(
36
- SELECT 1
37
- FROM pg_catalog.pg_proc p, pg_catalog.pg_namespace n
38
- WHERE p.pronamespace = n.oid
39
- AND n.nspname = 'public'
40
- AND p.proname = ?
41
- )
42
- SQL
43
- end
44
-
45
- private
46
- def message_matches(message)
47
- (message << ' ').tap do |message|
48
- message << @matches.map do |name, match|
49
- "a #{name} function"
50
- end.compact.to_sentence
51
- end
52
- end
53
- end
54
-
55
- def have_functions(*args)
56
- HaveFunctions.new(*args)
57
- end
58
-
59
- class HaveHistoryFunctions < HaveFunctions
60
- def initialize
61
- @function_templates = [
62
- 'chronomodel_%s_insert',
63
- 'chronomodel_%s_update',
64
- 'chronomodel_%s_delete',
65
- ]
66
- end
67
-
68
- def matches?(table)
69
- @functions = @function_templates.map {|t| t % [table] }
70
-
71
- super(table)
72
- end
73
- end
74
-
75
- def have_history_functions
76
- HaveHistoryFunctions.new
77
- end
78
- end
79
- end
@@ -1,69 +0,0 @@
1
- module ChronoTest::Matchers
2
-
3
- module Index
4
- class HaveIndex < ChronoTest::Matchers::Base
5
- attr_reader :name, :columns, :schema
6
-
7
- def initialize(name, columns, schema = 'public')
8
- @name = name
9
- @columns = columns.sort
10
- @schema = schema
11
- end
12
-
13
- def description
14
- 'have index'
15
- end
16
-
17
- def matches?(table)
18
- super(table)
19
-
20
- select_values(<<-SQL, [ table, name, schema ], 'Check index') == columns
21
- SELECT a.attname
22
- FROM pg_class t
23
- JOIN pg_index d ON t.oid = d.indrelid
24
- JOIN pg_class i ON i.oid = d.indexrelid
25
- JOIN pg_attribute a ON a.attrelid = t.oid AND a.attnum = ANY(d.indkey)
26
- WHERE i.relkind = 'i'
27
- AND t.relname = ?
28
- AND i.relname = ?
29
- AND i.relnamespace = (
30
- SELECT oid FROM pg_namespace WHERE nspname = ?
31
- )
32
- ORDER BY a.attname
33
- SQL
34
- end
35
-
36
- def failure_message
37
- "expected #{schema}.#{table} to have a #{name} index on #{columns}"
38
- end
39
-
40
- def failure_message_when_negated
41
- "expected #{schema}.#{table} to not have a #{name} index on #{columns}"
42
- end
43
- end
44
-
45
- def have_index(*args)
46
- HaveIndex.new(*args)
47
- end
48
-
49
- class HaveTemporalIndex < HaveIndex
50
- def initialize(name, columns)
51
- super(name, columns, temporal_schema)
52
- end
53
- end
54
-
55
- def have_temporal_index(*args)
56
- HaveTemporalIndex.new(*args)
57
- end
58
-
59
- class HaveHistoryIndex < HaveIndex
60
- def initialize(name, columns)
61
- super(name, columns, history_schema)
62
- end
63
- end
64
-
65
- def have_history_index(*args)
66
- HaveHistoryIndex.new(*args)
67
- end
68
- end
69
- end
@@ -1,39 +0,0 @@
1
- require 'support/matchers/base'
2
-
3
- module ChronoTest::Matchers
4
-
5
- module Schema
6
- class BeInSchema < ChronoTest::Matchers::Base
7
-
8
- def initialize(expected)
9
- @expected = expected
10
- @expected = '"$user", public' if @expected == :default
11
- end
12
-
13
- def description
14
- 'be in schema'
15
- end
16
-
17
- def failure_message
18
- "expected to be in schema #@expected, but was in #@current"
19
- end
20
-
21
- def failure_message_when_negated
22
- "expected to be in schema #@current, but was in #@expected"
23
- end
24
-
25
- def matches?(*)
26
- @current = select_value(<<-SQL, [], 'Current schema')
27
- SHOW search_path
28
- SQL
29
-
30
- @current == @expected
31
- end
32
- end
33
-
34
- def be_in_schema(schema)
35
- BeInSchema.new(schema)
36
- end
37
- end
38
-
39
- end
@@ -1,275 +0,0 @@
1
- require 'support/matchers/base'
2
-
3
- module ChronoTest::Matchers
4
-
5
- module Table
6
- class Base < ChronoTest::Matchers::Base
7
-
8
- protected
9
- # Database statements
10
- #
11
- def relation_exists?(options)
12
- schema = options[:in]
13
- kind = options[:kind] == :view ? 'v' : 'r'
14
-
15
- select_value(<<-SQL, [ table, schema ], 'Check table exists') == true
16
- SELECT EXISTS (
17
- SELECT 1
18
- FROM pg_class c
19
- LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
20
- WHERE c.relkind = '#{kind}'
21
- AND c.relname = ?
22
- AND n.nspname = ?
23
- )
24
- SQL
25
- end
26
- end
27
-
28
- # ##################################################################
29
- # Checks that a table exists in the Public schema
30
- #
31
- class HavePublicBacking < Base
32
- def matches?(table)
33
- super(table)
34
-
35
- relation_exists? :in => public_schema
36
- end
37
-
38
- def description
39
- 'be in the public schema'
40
- end
41
-
42
- def failure_message
43
- "expected #{table} to exist in the #{public_schema} schema"
44
- end
45
-
46
- def failure_message_when_negated
47
- "expected #{table} to not exist in the #{public_schema} schema"
48
- end
49
- end
50
-
51
- def have_public_backing
52
- HavePublicBacking.new
53
- end
54
-
55
-
56
- # ##################################################################
57
- # Checks that a table exists in the Temporal schema
58
- #
59
- class HaveTemporalBacking < Base
60
- def matches?(table)
61
- super(table)
62
-
63
- relation_exists? :in => temporal_schema
64
- end
65
-
66
- def description
67
- 'be in the temporal schema'
68
- end
69
-
70
- def failure_message
71
- "expected #{table} to exist in the #{temporal_schema} schema"
72
- end
73
-
74
- def failure_message_when_negated
75
- "expected #{table} to not exist in the #{temporal_schema} schema"
76
- end
77
- end
78
-
79
- def have_temporal_backing
80
- HaveTemporalBacking.new
81
- end
82
-
83
-
84
- # ##################################################################
85
- # Checks that a table exists in the History schema and inherits from
86
- # the one in the Temporal schema
87
- #
88
- class HaveHistoryBacking < Base
89
- def matches?(table)
90
- super(table)
91
-
92
- table_exists? &&
93
- inherits_from_temporal? &&
94
- has_consistency_constraint? &&
95
- has_history_indexes?
96
- end
97
-
98
- def description
99
- 'be in history schema'
100
- end
101
-
102
- def failure_message
103
- "expected #{table} ".tap do |message|
104
- message << [
105
- ("to exist in the #{history_schema} schema" unless @existance),
106
- ("to inherit from #{temporal_schema}.#{table}" unless @inheritance),
107
- ("to have a timeline consistency constraint" unless @constraint),
108
- ("to have history indexes" unless @indexes)
109
- ].compact.to_sentence
110
- end
111
- end
112
-
113
- def failure_message_when_negated
114
- "expected #{table} ".tap do |message|
115
- message << [
116
- ("to not exist in the #{history_schema} schema" if @existance),
117
- ("to not inherit from #{temporal_schema}.#{table}" if @inheritance),
118
- ("to not have a timeline consistency constraint" if @constraint),
119
- ("to not have history indexes" if @indexes)
120
- ].compact.to_sentence
121
- end
122
- end
123
-
124
- private
125
- def table_exists?
126
- @existance = relation_exists? :in => history_schema
127
- end
128
-
129
- def inherits_from_temporal?
130
- binds = ["#{history_schema}.#{table}", "#{temporal_schema}.#{table}"]
131
-
132
- @inheritance = select_value(<<-SQL, binds, 'Check inheritance') == true
133
- SELECT EXISTS (
134
- SELECT 1 FROM pg_catalog.pg_inherits
135
- WHERE inhrelid = ?::regclass::oid
136
- AND inhparent = ?::regclass::oid
137
- )
138
- SQL
139
- end
140
-
141
- def has_history_indexes?
142
- binds = [ history_schema, table ]
143
-
144
- indexes = select_values(<<-SQL, binds, 'Check history indexes')
145
- SELECT indexdef FROM pg_indexes
146
- WHERE schemaname = ?
147
- AND tablename = ?
148
- SQL
149
-
150
- fqtn = [history_schema, table].join('.')
151
-
152
- expected = [
153
- "CREATE INDEX index_#{table}_temporal_on_lower_validity ON #{fqtn} USING btree (lower(validity))",
154
- "CREATE INDEX index_#{table}_temporal_on_upper_validity ON #{fqtn} USING btree (upper(validity))",
155
- "CREATE INDEX index_#{table}_temporal_on_validity ON #{fqtn} USING gist (validity)",
156
-
157
- "CREATE INDEX #{table}_inherit_pkey ON #{fqtn} USING btree (id)",
158
- "CREATE INDEX #{table}_instance_history ON #{fqtn} USING btree (id, recorded_at)",
159
- "CREATE UNIQUE INDEX #{table}_pkey ON #{fqtn} USING btree (hid)",
160
- "CREATE INDEX #{table}_recorded_at ON #{fqtn} USING btree (recorded_at)",
161
- "CREATE INDEX #{table}_timeline_consistency ON #{fqtn} USING gist (id, validity)"
162
- ]
163
-
164
- @indexes = (expected - indexes).empty?
165
- end
166
-
167
- def has_consistency_constraint?
168
- binds = {
169
- conname: connection.timeline_consistency_constraint_name(table),
170
- connamespace: history_schema,
171
- conrelid: [history_schema, table].join('.'),
172
- attname: connection.primary_key(table)
173
- }
174
-
175
- @constraint = select_value(<<-SQL, binds, 'Check Consistency Constraint') == true
176
- SELECT EXISTS (
177
- SELECT 1 FROM pg_catalog.pg_constraint
178
- WHERE conname = :conname
179
- AND contype = 'x'
180
- AND conrelid = :conrelid::regclass
181
- AND connamespace = (
182
- SELECT oid FROM pg_catalog.pg_namespace WHERE nspname = :connamespace
183
- )
184
- AND conkey = (
185
- SELECT array_agg(attnum) FROM pg_catalog.pg_attribute
186
- WHERE attname IN (:attname, 'validity')
187
- AND attrelid = :conrelid::regclass
188
- )
189
- )
190
- SQL
191
- end
192
- end
193
-
194
- def have_history_backing
195
- HaveHistoryBacking.new
196
- end
197
-
198
-
199
- # ##################################################################
200
- # Checks that a table exists in the Public schema, is an updatable
201
- # view and has an INSERT, UPDATE and DELETE triggers.
202
- #
203
- class HavePublicInterface < Base
204
- def matches?(table)
205
- super(table)
206
-
207
- view_exists? && [ is_updatable?, has_triggers? ].all?
208
- end
209
-
210
- def description
211
- 'be an updatable view'
212
- end
213
-
214
- def failure_message
215
- "expected #{table} ".tap do |message|
216
- message << [
217
- ("to exist in the #{public_schema} schema" unless @existance ),
218
- ('to be an updatable view' unless @updatable ),
219
- ('to have an INSERT trigger' unless @insert_trigger),
220
- ('to have an UPDATE trigger' unless @update_trigger),
221
- ('to have a DELETE trigger' unless @delete_trigger)
222
- ].compact.to_sentence
223
- end
224
- end
225
-
226
- def failure_message_when_negated
227
- "expected #{table} ".tap do |message|
228
- message << [
229
- ("to not exist in the #{public_schema} schema" if @existance ),
230
- ('to not be an updatable view' if @updatable ),
231
- ('to not have an INSERT trigger' if @insert_trigger),
232
- ('to not have an UPDATE trigger' if @update_trigger),
233
- ('to not have a DELETE trigger' if @delete_trigger)
234
- ].compact.to_sentence
235
- end
236
- end
237
-
238
- private
239
- def view_exists?
240
- @existance = relation_exists? :in => public_schema, :kind => :view
241
- end
242
-
243
- def is_updatable?
244
- binds = [ public_schema, table ]
245
-
246
- @updatable = select_value(<<-SQL, binds, 'Check updatable') == 'YES'
247
- SELECT is_updatable FROM information_schema.views
248
- WHERE table_schema = ? AND table_name = ?
249
- SQL
250
- end
251
-
252
- def has_triggers?
253
- triggers = select_values(<<-SQL, [ public_schema, table ], 'Check triggers')
254
- SELECT t.tgname
255
- FROM pg_catalog.pg_trigger t, pg_catalog.pg_class c, pg_catalog.pg_namespace n
256
- WHERE t.tgrelid = c.relfilenode
257
- AND n.oid = c.relnamespace
258
- AND n.nspname = ?
259
- AND c.relname = ?;
260
- SQL
261
-
262
- @insert_trigger = triggers.include? 'chronomodel_insert'
263
- @update_trigger = triggers.include? 'chronomodel_update'
264
- @delete_trigger = triggers.include? 'chronomodel_delete'
265
-
266
- @insert_trigger && @update_trigger && @delete_trigger
267
- end
268
- end
269
-
270
- def have_public_interface
271
- HavePublicInterface.new
272
- end
273
- end
274
-
275
- end
@@ -1,47 +0,0 @@
1
- module ChronoTest::TimeMachine
2
-
3
- # This module contains helpers used throughout the
4
- # +ChronoModel::TimeMachine+ specs.
5
- #
6
- module Helpers
7
- def self.included(base)
8
- base.extend(self)
9
- end
10
-
11
- def adapter
12
- ChronoTest.connection
13
- end
14
-
15
- def with_revert
16
- adapter.transaction do
17
- adapter.create_savepoint 'revert'
18
-
19
- yield
20
-
21
- adapter.exec_rollback_to_savepoint 'revert'
22
- end
23
- end
24
-
25
- # If a context object is given, evaluates the given
26
- # block in its instance context, then defines a `ts`
27
- # on it, backed by an Array, and adds the current
28
- # database timestamp to it.
29
- #
30
- # If a context object is not given, the block is
31
- # evaluated in the current context and the above
32
- # mangling is done on the blocks' return value.
33
- #
34
- def ts_eval(ctx = nil, &block)
35
- ret = (ctx || self).instance_eval(&block)
36
- (ctx || ret).tap do |obj|
37
- obj.singleton_class.instance_eval do
38
- define_method(:ts) { @_ts ||= [] }
39
- end unless obj.methods.include?(:ts)
40
-
41
- now = ChronoTest.connection.select_value('select now()::timestamp') + 'Z'
42
- obj.ts.push(Time.parse(now))
43
- end
44
- end
45
- end
46
-
47
- end