activerecord-tablefree 3.0.1 → 3.1.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 +4 -4
- data/Appraisals +6 -6
- data/Gemfile +3 -3
- data/README.md +9 -25
- data/Rakefile +12 -12
- data/activerecord-tablefree.gemspec +17 -17
- data/features/step_definitions/rails_steps.rb +32 -35
- data/features/step_definitions/tablefree.rb +6 -6
- data/features/step_definitions/web_steps.rb +9 -10
- data/features/support/env.rb +3 -3
- data/features/support/paths.rb +4 -4
- data/features/support/rails.rb +5 -6
- data/features/support/selectors.rb +4 -4
- data/lib/activerecord-tablefree.rb +58 -141
- data/lib/activerecord-tablefree/cast_type.rb +28 -0
- data/lib/activerecord-tablefree/connection.rb +70 -0
- data/lib/activerecord-tablefree/schema_cache.rb +7 -0
- data/lib/activerecord-tablefree/transaction.rb +4 -0
- data/lib/activerecord-tablefree/version.rb +1 -3
- data/spec/lib/activerecord-tablefree_spec.rb +149 -122
- data/spec/spec_helper.rb +1 -1
- metadata +36 -32
data/features/support/env.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
|
-
#require 'bundler'
|
1
|
+
# require 'bundler'
|
2
2
|
begin
|
3
3
|
Bundler.setup(:default, :development)
|
4
4
|
rescue Bundler::BundlerError => e
|
5
|
-
|
6
|
-
|
5
|
+
warn e.message
|
6
|
+
warn 'Run `bundle install` to install missing gems'
|
7
7
|
exit e.status_code
|
8
8
|
end
|
9
9
|
|
data/features/support/paths.rb
CHANGED
@@ -15,11 +15,11 @@ module NavigationHelpers
|
|
15
15
|
else
|
16
16
|
begin
|
17
17
|
page_name =~ /the (.*) page/
|
18
|
-
path_components =
|
19
|
-
|
18
|
+
path_components = Regexp.last_match(1).split(/\s+/)
|
19
|
+
send(path_components.push('path').join('_').to_sym)
|
20
20
|
rescue Object => e
|
21
|
-
raise "Can't find mapping from \"#{page_name}\" to a path.\n"
|
22
|
-
|
21
|
+
raise "Can't find mapping from \"#{page_name}\" to a path.\n" \
|
22
|
+
"Now, go and add a mapping in #{__FILE__}"
|
23
23
|
end
|
24
24
|
end
|
25
25
|
end
|
data/features/support/rails.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PROJECT_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..', '..')).freeze
|
2
2
|
APP_NAME = 'testapp'.freeze
|
3
|
-
BUNDLE_ENV_VARS = %w
|
4
|
-
ORIGINAL_BUNDLE_VARS = Hash[ENV.select{ |key,
|
3
|
+
BUNDLE_ENV_VARS = %w[RUBYOPT BUNDLE_PATH BUNDLE_BIN_PATH BUNDLE_GEMFILE].freeze
|
4
|
+
ORIGINAL_BUNDLE_VARS = Hash[ENV.select { |key, _value| BUNDLE_ENV_VARS.include?(key) }]
|
5
5
|
|
6
6
|
ENV['RAILS_ENV'] = 'test'
|
7
7
|
|
@@ -32,7 +32,7 @@ module RailsCommandHelpers
|
|
32
32
|
end
|
33
33
|
|
34
34
|
def framework_major_version
|
35
|
-
framework_version.split(
|
35
|
+
framework_version.split('.').first.to_i
|
36
36
|
end
|
37
37
|
|
38
38
|
def new_application_command(app_name)
|
@@ -40,13 +40,12 @@ module RailsCommandHelpers
|
|
40
40
|
end
|
41
41
|
|
42
42
|
def generator_command
|
43
|
-
framework_major_version >= 3 ?
|
43
|
+
framework_major_version >= 3 ? 'rails generate' : 'script/generate'
|
44
44
|
end
|
45
45
|
|
46
46
|
def runner_command
|
47
|
-
framework_major_version >= 3 ?
|
47
|
+
framework_major_version >= 3 ? 'rails runner' : 'script/runner'
|
48
48
|
end
|
49
|
-
|
50
49
|
end
|
51
50
|
|
52
51
|
World(RailsCommandHelpers)
|
@@ -7,11 +7,11 @@ module HtmlSelectorsHelpers
|
|
7
7
|
#
|
8
8
|
def selector_for(locator)
|
9
9
|
case locator
|
10
|
-
when
|
11
|
-
|
10
|
+
when 'the page'
|
11
|
+
'html > body'
|
12
12
|
else
|
13
|
-
raise "Can't find mapping from \"#{locator}\" to a selector.\n"
|
14
|
-
|
13
|
+
raise "Can't find mapping from \"#{locator}\" to a selector.\n" \
|
14
|
+
"Now, go and add a mapping in #{__FILE__}"
|
15
15
|
end
|
16
16
|
end
|
17
17
|
end
|
@@ -1,8 +1,13 @@
|
|
1
|
-
|
1
|
+
require 'cgi'
|
2
|
+
require 'active_record'
|
3
|
+
|
2
4
|
require 'activerecord-tablefree/version'
|
5
|
+
require 'activerecord-tablefree/cast_type'
|
6
|
+
require 'activerecord-tablefree/schema_cache'
|
7
|
+
require 'activerecord-tablefree/connection'
|
8
|
+
require 'activerecord-tablefree/transaction'
|
3
9
|
|
4
10
|
module ActiveRecord
|
5
|
-
|
6
11
|
# = ActiveRecord::Tablefree
|
7
12
|
#
|
8
13
|
# Allow classes to behave like ActiveRecord models, but without an associated
|
@@ -29,26 +34,24 @@ module ActiveRecord
|
|
29
34
|
# end
|
30
35
|
#
|
31
36
|
module Tablefree
|
32
|
-
require 'active_record'
|
33
37
|
|
34
38
|
class NoDatabase < StandardError; end
|
35
39
|
class Unsupported < StandardError; end
|
36
40
|
|
37
|
-
def self.included(
|
41
|
+
def self.included(base) #:nodoc:
|
38
42
|
base.send :extend, ActsMethods
|
39
43
|
end
|
40
44
|
|
41
45
|
module ActsMethods #:nodoc:
|
42
|
-
|
43
46
|
# A model that needs to be tablefree will call this method to indicate
|
44
47
|
# it.
|
45
|
-
def has_no_table(options = {:
|
46
|
-
raise ArgumentError
|
48
|
+
def has_no_table(options = { database: :fail_fast })
|
49
|
+
raise ArgumentError, "Invalid database option '#{options[:database]}'" unless %i[fail_fast pretend_success].member? options[:database]
|
47
50
|
# keep our options handy
|
48
51
|
class_attribute :tablefree_options
|
49
52
|
self.tablefree_options = {
|
50
|
-
:
|
51
|
-
:
|
53
|
+
database: options[:database],
|
54
|
+
columns_hash: {}
|
52
55
|
}
|
53
56
|
|
54
57
|
# extend
|
@@ -66,20 +69,18 @@ module ActiveRecord
|
|
66
69
|
def tablefree?
|
67
70
|
false
|
68
71
|
end
|
69
|
-
|
70
72
|
end
|
71
73
|
|
72
74
|
module SingletonMethods
|
73
|
-
|
74
75
|
# Used internally by ActiveRecord 5. This is the special hook that makes everything else work.
|
75
76
|
def load_schema!
|
76
77
|
@columns_hash = tablefree_options[:columns_hash].except(*ignored_columns)
|
77
78
|
@columns_hash.each do |name, column|
|
78
79
|
define_attribute(
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
80
|
+
name,
|
81
|
+
connection.lookup_cast_type_from_column(column),
|
82
|
+
default: column.default,
|
83
|
+
user_provided_default: false
|
83
84
|
)
|
84
85
|
end
|
85
86
|
end
|
@@ -97,12 +98,12 @@ module ActiveRecord
|
|
97
98
|
end
|
98
99
|
end
|
99
100
|
|
100
|
-
def destroy(*
|
101
|
+
def destroy(*_args)
|
101
102
|
case tablefree_options[:database]
|
102
103
|
when :pretend_success
|
103
|
-
|
104
|
+
new
|
104
105
|
when :fail_fast
|
105
|
-
raise NoDatabase
|
106
|
+
raise NoDatabase, "Can't #destroy on Tablefree class"
|
106
107
|
end
|
107
108
|
end
|
108
109
|
|
@@ -111,33 +112,32 @@ module ActiveRecord
|
|
111
112
|
when :pretend_success
|
112
113
|
[]
|
113
114
|
when :fail_fast
|
114
|
-
raise NoDatabase
|
115
|
+
raise NoDatabase, "Can't #destroy_all on Tablefree class"
|
115
116
|
end
|
116
117
|
end
|
117
118
|
|
118
119
|
case ActiveRecord::VERSION::MAJOR
|
119
120
|
when 5
|
120
|
-
def find_by_sql(*
|
121
|
+
def find_by_sql(*_args)
|
121
122
|
case tablefree_options[:database]
|
122
123
|
when :pretend_success
|
123
124
|
[]
|
124
125
|
when :fail_fast
|
125
|
-
raise NoDatabase
|
126
|
+
raise NoDatabase, "Can't #find_by_sql on Tablefree class"
|
126
127
|
end
|
127
|
-
|
128
128
|
end
|
129
129
|
else
|
130
|
-
raise Unsupported
|
130
|
+
raise Unsupported, 'Unsupported ActiveRecord version'
|
131
131
|
end
|
132
132
|
|
133
|
-
def transaction
|
134
|
-
# case tablefree_options[:database]
|
135
|
-
# when :pretend_success
|
136
|
-
|
137
|
-
|
138
|
-
# when :fail_fast
|
139
|
-
# raise NoDatabase.new("Can't #transaction on Tablefree class")
|
140
|
-
# end
|
133
|
+
def transaction
|
134
|
+
# case tablefree_options[:database]
|
135
|
+
# when :pretend_success
|
136
|
+
@_current_transaction_records ||= []
|
137
|
+
yield
|
138
|
+
# when :fail_fast
|
139
|
+
# raise NoDatabase.new("Can't #transaction on Tablefree class")
|
140
|
+
# end
|
141
141
|
end
|
142
142
|
|
143
143
|
def tablefree?
|
@@ -150,123 +150,43 @@ module ActiveRecord
|
|
150
150
|
end
|
151
151
|
|
152
152
|
module ClassMethods
|
153
|
-
|
154
153
|
def from_query_string(query_string)
|
155
|
-
|
154
|
+
if query_string.blank?
|
155
|
+
new
|
156
|
+
else
|
156
157
|
params = query_string.split('&').collect do |chunk|
|
157
158
|
next if chunk.empty?
|
158
159
|
key, value = chunk.split('=', 2)
|
159
160
|
next if key.empty?
|
160
161
|
value = value.nil? ? nil : CGI.unescape(value)
|
161
|
-
[
|
162
|
+
[CGI.unescape(key), value]
|
162
163
|
end.compact.to_h
|
163
164
|
|
164
165
|
new(params)
|
165
|
-
else
|
166
|
-
new
|
167
166
|
end
|
168
167
|
end
|
169
168
|
|
170
169
|
def connection
|
171
|
-
|
172
|
-
def conn.quote_table_name(*_args)
|
173
|
-
""
|
174
|
-
end
|
175
|
-
def conn.quote_column_name(*_args)
|
176
|
-
""
|
177
|
-
end
|
178
|
-
def conn.substitute_at(*_args)
|
179
|
-
nil
|
180
|
-
end
|
181
|
-
def conn.schema_cache(*_args)
|
182
|
-
schema_cache = Object.new()
|
183
|
-
def schema_cache.columns_hash(*_args)
|
184
|
-
Hash.new()
|
185
|
-
end
|
186
|
-
schema_cache
|
187
|
-
end
|
188
|
-
# Fixes Issue #17. https://github.com/softace/activerecord-tablefree/issues/17
|
189
|
-
# The following method is from the ActiveRecord gem: /lib/active_record/connection_adapters/abstract/database_statements.rb .
|
190
|
-
# Sanitizes the given LIMIT parameter in order to prevent SQL injection.
|
191
|
-
#
|
192
|
-
# The +limit+ may be anything that can evaluate to a string via #to_s. It
|
193
|
-
# should look like an integer, or a comma-delimited list of integers, or
|
194
|
-
# an Arel SQL literal.
|
195
|
-
#
|
196
|
-
# Returns Integer and Arel::Nodes::SqlLiteral limits as is.
|
197
|
-
# Returns the sanitized limit parameter, either as an integer, or as a
|
198
|
-
# string which contains a comma-delimited list of integers.
|
199
|
-
def conn.sanitize_limit(limit)
|
200
|
-
if limit.is_a?(Integer) || limit.is_a?(Arel::Nodes::SqlLiteral)
|
201
|
-
limit
|
202
|
-
elsif limit.to_s.include?(',')
|
203
|
-
Arel.sql limit.to_s.split(',').map{ |i| Integer(i) }.join(',')
|
204
|
-
else
|
205
|
-
Integer(limit)
|
206
|
-
end
|
207
|
-
end
|
208
|
-
|
209
|
-
# Called by bound_attributes in /lib/active_record/relation/query_methods.rb
|
210
|
-
# Returns a SQL string with the from, join, where, and having clauses, in addition to the limit and offset.
|
211
|
-
def conn.combine_bind_parameters(**_args)
|
212
|
-
""
|
213
|
-
end
|
214
|
-
|
215
|
-
def conn.lookup_cast_type_from_column(*_args)
|
216
|
-
lct = Object.new
|
217
|
-
def lct.assert_valid_value(*_args)
|
218
|
-
true
|
219
|
-
end
|
220
|
-
# Needed for Rails 5.0
|
221
|
-
def lct.serialize(args)
|
222
|
-
args
|
223
|
-
end
|
224
|
-
def lct.deserialize(args)
|
225
|
-
args
|
226
|
-
end
|
227
|
-
def lct.cast(args)
|
228
|
-
args
|
229
|
-
end
|
230
|
-
def lct.changed?(*_args)
|
231
|
-
false
|
232
|
-
end
|
233
|
-
def lct.changed_in_place?(*_args)
|
234
|
-
false
|
235
|
-
end
|
236
|
-
lct
|
237
|
-
end
|
238
|
-
|
239
|
-
# This is used in the StatementCache object. It returns an object that
|
240
|
-
# can be used to query the database repeatedly.
|
241
|
-
def conn.cacheable_query(arel) # :nodoc:
|
242
|
-
if prepared_statements
|
243
|
-
ActiveRecord::StatementCache.query visitor, arel.ast
|
244
|
-
else
|
245
|
-
ActiveRecord::StatementCache.partial_query visitor, arel.ast, collector
|
246
|
-
end
|
247
|
-
end
|
248
|
-
conn
|
170
|
+
@_connection ||= ActiveRecord::Tablefree::Connection.new
|
249
171
|
end
|
250
|
-
|
251
172
|
end
|
252
173
|
|
253
174
|
module InstanceMethods
|
254
|
-
|
255
175
|
def to_query_string(prefix = nil)
|
256
|
-
attributes.to_a.collect{|(name,value)| escaped_var_name(name, prefix) +
|
176
|
+
attributes.to_a.collect { |(name, value)| escaped_var_name(name, prefix) + '=' + escape_for_url(value) if value }.compact.join('&')
|
257
177
|
end
|
258
178
|
|
259
179
|
def quote_value(_value, _column = nil)
|
260
|
-
|
180
|
+
''
|
261
181
|
end
|
262
182
|
|
263
|
-
%w
|
264
|
-
define_method(method_name) do |*
|
183
|
+
%w[create create_record _create_record update update_record _update_record].each do |method_name|
|
184
|
+
define_method(method_name) do |*_args|
|
265
185
|
case self.class.tablefree_options[:database]
|
266
186
|
when :pretend_success
|
267
187
|
true
|
268
188
|
when :fail_fast
|
269
|
-
raise NoDatabase
|
189
|
+
raise NoDatabase, "Can't ##{method_name} a Tablefree object"
|
270
190
|
end
|
271
191
|
end
|
272
192
|
end
|
@@ -277,42 +197,39 @@ module ActiveRecord
|
|
277
197
|
@destroyed = true
|
278
198
|
freeze
|
279
199
|
when :fail_fast
|
280
|
-
raise NoDatabase
|
200
|
+
raise NoDatabase, "Can't #destroy a Tablefree object"
|
281
201
|
end
|
282
202
|
end
|
283
203
|
|
284
|
-
def reload(*
|
204
|
+
def reload(*_args)
|
285
205
|
case self.class.tablefree_options[:database]
|
286
206
|
when :pretend_success
|
287
207
|
self
|
288
208
|
when :fail_fast
|
289
|
-
raise NoDatabase
|
209
|
+
raise NoDatabase, "Can't #reload a Tablefree object"
|
290
210
|
end
|
291
211
|
end
|
292
212
|
|
293
|
-
def add_to_transaction
|
294
|
-
end
|
213
|
+
def add_to_transaction; end
|
295
214
|
|
296
215
|
private
|
297
216
|
|
298
|
-
|
299
|
-
|
300
|
-
|
217
|
+
def escaped_var_name(name, prefix = nil)
|
218
|
+
prefix ? "#{CGI.escape(prefix)}[#{CGI.escape(name)}]" : CGI.escape(name)
|
219
|
+
end
|
301
220
|
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
end
|
309
|
-
rescue
|
310
|
-
""
|
221
|
+
def escape_for_url(value)
|
222
|
+
case value
|
223
|
+
when true then '1'
|
224
|
+
when false then '0'
|
225
|
+
when nil then ''
|
226
|
+
else CGI.escape(value.to_s)
|
311
227
|
end
|
312
|
-
|
228
|
+
rescue
|
229
|
+
''
|
230
|
+
end
|
313
231
|
end
|
314
|
-
|
315
232
|
end
|
316
233
|
end
|
317
234
|
|
318
|
-
ActiveRecord::Base.send(
|
235
|
+
ActiveRecord::Base.send(:include, ActiveRecord::Tablefree)
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module ActiveRecord::Tablefree
|
2
|
+
class CastType
|
3
|
+
def assert_valid_value(*_args)
|
4
|
+
true
|
5
|
+
end
|
6
|
+
|
7
|
+
# Needed for Rails 5.0
|
8
|
+
def serialize(args)
|
9
|
+
args
|
10
|
+
end
|
11
|
+
|
12
|
+
def deserialize(args)
|
13
|
+
args
|
14
|
+
end
|
15
|
+
|
16
|
+
def cast(args)
|
17
|
+
args
|
18
|
+
end
|
19
|
+
|
20
|
+
def changed?(*_args)
|
21
|
+
false
|
22
|
+
end
|
23
|
+
|
24
|
+
def changed_in_place?(*_args)
|
25
|
+
false
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module ActiveRecord::Tablefree
|
2
|
+
class Connection
|
3
|
+
def quote_table_name(*_args)
|
4
|
+
''
|
5
|
+
end
|
6
|
+
|
7
|
+
def quote_column_name(*_args)
|
8
|
+
''
|
9
|
+
end
|
10
|
+
|
11
|
+
def substitute_at(*_args)
|
12
|
+
nil
|
13
|
+
end
|
14
|
+
|
15
|
+
def schema_cache(*_args)
|
16
|
+
@_schema_cache ||= ActiveRecord::Tablefree::SchemaCache.new
|
17
|
+
end
|
18
|
+
|
19
|
+
# Fixes Issue #17. https://github.com/softace/activerecord-tablefree/issues/17
|
20
|
+
# The following method is from the ActiveRecord gem:
|
21
|
+
# /lib/active_record/connection_adapters/abstract/database_statements.rb .
|
22
|
+
# Sanitizes the given LIMIT parameter in order to prevent SQL injection.
|
23
|
+
#
|
24
|
+
# The +limit+ may be anything that can evaluate to a string via #to_s. It
|
25
|
+
# should look like an integer, or a comma-delimited list of integers, or
|
26
|
+
# an Arel SQL literal.
|
27
|
+
#
|
28
|
+
# Returns Integer and Arel::Nodes::SqlLiteral limits as is.
|
29
|
+
# Returns the sanitized limit parameter, either as an integer, or as a
|
30
|
+
# string which contains a comma-delimited list of integers.
|
31
|
+
def sanitize_limit(limit)
|
32
|
+
if limit.is_a?(Integer) || limit.is_a?(Arel::Nodes::SqlLiteral)
|
33
|
+
limit
|
34
|
+
elsif limit.to_s.include?(',')
|
35
|
+
Arel.sql limit.to_s.split(',').map { |i| Integer(i) }.join(',')
|
36
|
+
else
|
37
|
+
Integer(limit)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Called by bound_attributes in /lib/active_record/relation/query_methods.rb
|
42
|
+
# Returns a SQL string with the from, join, where, and having clauses,
|
43
|
+
# in addition to the limit and offset.
|
44
|
+
def combine_bind_parameters(**_args)
|
45
|
+
''
|
46
|
+
end
|
47
|
+
|
48
|
+
def lookup_cast_type_from_column(*_args)
|
49
|
+
@_cast_type ||= ActiveRecord::Tablefree::CastType.new
|
50
|
+
end
|
51
|
+
|
52
|
+
def current_transaction
|
53
|
+
@_current_transaction ||= ActiveRecord::Tablefree::Transaction.new
|
54
|
+
end
|
55
|
+
|
56
|
+
def execute(*_args)
|
57
|
+
{}
|
58
|
+
end
|
59
|
+
|
60
|
+
# This is used in the StatementCache object. It returns an object that
|
61
|
+
# can be used to query the database repeatedly.
|
62
|
+
def cacheable_query(arel) # :nodoc:
|
63
|
+
if prepared_statements
|
64
|
+
ActiveRecord::StatementCache.query visitor, arel.ast
|
65
|
+
else
|
66
|
+
ActiveRecord::StatementCache.partial_query visitor, arel.ast, collector
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|