activerecord-tablefree 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,37 @@
1
+ Given /^I delete all migrations$/ do
2
+ steps %{
3
+ When I successfully run `bash -c 'rm db/migrate/*.rb'`
4
+ }
5
+ end
6
+
7
+ Given /^I update my new user model to be tablefree$/ do
8
+ in_current_dir do
9
+ file_name = 'app/models/user.rb'
10
+ content = File.read(file_name)
11
+ if framework_version < "3.0"
12
+ content = "require 'activerecord-tablefree'\n" + content
13
+ end
14
+
15
+ content.gsub!(/^(class .* < ActiveRecord::Base)$/, "\\1\n" + <<-TABLELESS)
16
+ has_no_table
17
+ column :id, :integer
18
+ column :name, :string
19
+
20
+ TABLELESS
21
+ File.open(file_name, 'w') { |f| f << content }
22
+ end
23
+ end
24
+
25
+ Given /^I update my users controller to render instead of redirect$/ do
26
+ in_current_dir do
27
+ transform_file('app/controllers/users_controller.rb') do |content|
28
+ ##Changes in #create method
29
+ content.gsub!(/@user = User.new\((.*?)\)/,
30
+ '@user = User.new(\1); @user.id = 1')
31
+ content.gsub!("if @user.save",
32
+ "if @user.valid?")
33
+ content.gsub!(/redirect_to([\( ])@user, .*?([\)]| \}|$)/,
34
+ "render\\1:action => 'show'\\2")
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,140 @@
1
+ # TL;DR: YOU SHOULD DELETE THIS FILE
2
+ #
3
+ # This file was generated by Cucumber-Rails and is only here to get you a head start
4
+ # These step definitions are thin wrappers around the Capybara/Webrat API that lets you
5
+ # visit pages, interact with widgets and make assertions about page content.
6
+ #
7
+ # If you use these step definitions as basis for your features you will quickly end up
8
+ # with features that are:
9
+ #
10
+ # * Hard to maintain
11
+ # * Verbose to read
12
+ #
13
+ # A much better approach is to write your own higher level step definitions, following
14
+ # the advice in the following blog posts:
15
+ #
16
+ # * http://benmabey.com/2008/05/19/imperative-vs-declarative-scenarios-in-user-stories.html
17
+ # * http://dannorth.net/2011/01/31/whose-domain-is-it-anyway/
18
+ # * http://elabs.se/blog/15-you-re-cuking-it-wrong
19
+ #
20
+
21
+
22
+ require 'uri'
23
+ require 'cgi'
24
+ require File.expand_path(File.join(File.dirname(__FILE__), "..", "support", "paths"))
25
+ require File.expand_path(File.join(File.dirname(__FILE__), "..", "support", "selectors"))
26
+
27
+ module WithinHelpers
28
+ def with_scope(locator)
29
+ locator ? within(*selector_for(locator)) { yield } : yield
30
+ end
31
+ end
32
+ World(WithinHelpers)
33
+
34
+ # Single-line step scoper
35
+ When /^(.*) within (.*[^:])$/ do |step, parent|
36
+ with_scope(parent) { When step }
37
+ end
38
+
39
+ # Multi-line step scoper
40
+ When /^(.*) within (.*[^:]):$/ do |step, parent, table_or_string|
41
+ with_scope(parent) { When "#{step}:", table_or_string }
42
+ end
43
+
44
+ Given /^(?:|I )am on (.+)$/ do |page_name|
45
+ visit path_to(page_name)
46
+ end
47
+
48
+ When /^(?:|I )go to (.+)$/ do |page_name|
49
+ visit path_to(page_name)
50
+ end
51
+
52
+ When /^(?:|I )press "([^"]*)"$/ do |button|
53
+ click_button(button)
54
+ end
55
+
56
+ When /^(?:|I )follow "([^"]*)"$/ do |link|
57
+ click_link(link)
58
+ end
59
+
60
+ When /^(?:|I )fill in "([^"]*)" with "([^"]*)"$/ do |field, value|
61
+ fill_in(field, :with => value)
62
+ end
63
+
64
+ When /^(?:|I )fill in "([^"]*)" for "([^"]*)"$/ do |value, field|
65
+ fill_in(field, :with => value)
66
+ end
67
+
68
+ # Use this to fill in an entire form with data from a table. Example:
69
+ #
70
+ # When I fill in the following:
71
+ # | Account Number | 5002 |
72
+ # | Expiry date | 2009-11-01 |
73
+ # | Note | Nice guy |
74
+ # | Wants Email? | |
75
+ #
76
+ # TODO: Add support for checkbox, select og option
77
+ # based on naming conventions.
78
+ #
79
+ When /^(?:|I )fill in the following:$/ do |fields|
80
+ fields.rows_hash.each do |name, value|
81
+ When %{I fill in "#{name}" with "#{value}"}
82
+ end
83
+ end
84
+
85
+ When /^(?:|I )select "([^"]*)" from "([^"]*)"$/ do |value, field|
86
+ select(value, :from => field)
87
+ end
88
+
89
+ When /^(?:|I )check "([^"]*)"$/ do |field|
90
+ check(field)
91
+ end
92
+
93
+ When /^(?:|I )uncheck "([^"]*)"$/ do |field|
94
+ uncheck(field)
95
+ end
96
+
97
+ When /^(?:|I )choose "([^"]*)"$/ do |field|
98
+ choose(field)
99
+ end
100
+
101
+ When /^(?:|I )attach the file "([^"]*)" to "([^"]*)"$/ do |path, field|
102
+ attach_file(field, File.expand_path(path))
103
+ end
104
+
105
+ Then /^(?:|I )should (not )?see "([^"]*)"$/ do |negate, text|
106
+ should_name = negate ? :should_not : :should
107
+ if page.respond_to? should_name
108
+ page.send should_name, have_content(text)
109
+ else
110
+ assert(negate ? page.has_no_content?(text) : page.has_content?(text))
111
+ end
112
+ end
113
+
114
+ Then /^(?:|I )should (not )?see \/([^\/]*)\/$/ do |negate, regexp|
115
+ regexp = Regexp.new(regexp)
116
+ should_name = negate ? :should_not : :should
117
+ if page.respond_to? should_name
118
+ page.send should_name, have_xpath('//*', :text => regexp)
119
+ else
120
+ assert(negate ? page.has_no_xpath?('//*', :text => regexp) : page.has_xpath?('//*', :text => regexp))
121
+ end
122
+ end
123
+
124
+ Then /^(?:|I )should be on (.+)$/ do |page_name|
125
+ current_path = URI.parse(current_url).path
126
+ expect(current_path).to eq path_to(page_name)
127
+ end
128
+
129
+ Then /^(?:|I )should have the following query string:$/ do |expected_pairs|
130
+ query = URI.parse(current_url).query
131
+ actual_params = query ? CGI.parse(query) : {}
132
+ expected_params = {}
133
+ expected_pairs.rows_hash.each_pair{|k,v| expected_params[k] = v.split(',')}
134
+
135
+ expect(actual_params).to eq expected_params
136
+ end
137
+
138
+ Then /^show me the page$/ do
139
+ save_and_open_page
140
+ end
@@ -0,0 +1,15 @@
1
+ #require 'bundler'
2
+ begin
3
+ Bundler.setup(:default, :development)
4
+ rescue Bundler::BundlerError => e
5
+ $stderr.puts e.message
6
+ $stderr.puts "Run `bundle install` to install missing gems"
7
+ exit e.status_code
8
+ end
9
+
10
+ require 'aruba/cucumber'
11
+ require 'capybara/cucumber'
12
+
13
+ Before do
14
+ @aruba_timeout_seconds = 120
15
+ end
@@ -0,0 +1,28 @@
1
+ module NavigationHelpers
2
+ # Maps a name to a path. Used by the
3
+ #
4
+ # When /^I go to (.+)$/ do |page_name|
5
+ #
6
+ # step definition in web_steps.rb
7
+ #
8
+ def path_to(page_name)
9
+ case page_name
10
+
11
+ when /the home\s?page/
12
+ '/'
13
+ when /the new user page/
14
+ '/users/new'
15
+ else
16
+ begin
17
+ page_name =~ /the (.*) page/
18
+ path_components = $1.split(/\s+/)
19
+ self.send(path_components.push('path').join('_').to_sym)
20
+ rescue Object => e
21
+ raise "Can't find mapping from \"#{page_name}\" to a path.\n" +
22
+ "Now, go and add a mapping in #{__FILE__}"
23
+ end
24
+ end
25
+ end
26
+ end
27
+
28
+ World(NavigationHelpers)
@@ -0,0 +1,52 @@
1
+ PROJECT_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..', '..')).freeze
2
+ APP_NAME = 'testapp'.freeze
3
+ BUNDLE_ENV_VARS = %w(RUBYOPT BUNDLE_PATH BUNDLE_BIN_PATH BUNDLE_GEMFILE)
4
+ ORIGINAL_BUNDLE_VARS = Hash[ENV.select{ |key,value| BUNDLE_ENV_VARS.include?(key) }]
5
+
6
+ ENV['RAILS_ENV'] = 'test'
7
+
8
+ Before do
9
+ ENV['BUNDLE_GEMFILE'] = File.join(Dir.pwd, ENV['BUNDLE_GEMFILE']) unless ENV['BUNDLE_GEMFILE'].start_with?(Dir.pwd)
10
+ @framework_version = nil
11
+ end
12
+
13
+ After do
14
+ ORIGINAL_BUNDLE_VARS.each_pair do |key, value|
15
+ ENV[key] = value
16
+ end
17
+ end
18
+
19
+ When /^I reset Bundler environment variable$/ do
20
+ BUNDLE_ENV_VARS.each do |key|
21
+ ENV[key] = nil
22
+ end
23
+ end
24
+
25
+ module RailsCommandHelpers
26
+ def framework_version?(version_string)
27
+ framework_version =~ /^#{version_string}/
28
+ end
29
+
30
+ def framework_version
31
+ @framework_version ||= `rails -v`[/^Rails (.+)$/, 1]
32
+ end
33
+
34
+ def framework_major_version
35
+ framework_version.split(".").first.to_i
36
+ end
37
+
38
+ def new_application_command(app_name)
39
+ framework_major_version >= 3 ? "rails new #{app_name} --skip-sprockets --skip-javascript --skip-bundle" : "rails #{app_name}"
40
+ end
41
+
42
+ def generator_command
43
+ framework_major_version >= 3 ? "rails generate" : "script/generate"
44
+ end
45
+
46
+ def runner_command
47
+ framework_major_version >= 3 ? "rails runner" : "script/runner"
48
+ end
49
+
50
+ end
51
+
52
+ World(RailsCommandHelpers)
@@ -0,0 +1,19 @@
1
+ module HtmlSelectorsHelpers
2
+ # Maps a name to a selector. Used primarily by the
3
+ #
4
+ # When /^(.+) within (.+)$/ do |step, scope|
5
+ #
6
+ # step definitions in web_steps.rb
7
+ #
8
+ def selector_for(locator)
9
+ case locator
10
+ when "the page"
11
+ "html > body"
12
+ else
13
+ raise "Can't find mapping from \"#{locator}\" to a selector.\n" +
14
+ "Now, go and add a mapping in #{__FILE__}"
15
+ end
16
+ end
17
+ end
18
+
19
+ World(HtmlSelectorsHelpers)
@@ -0,0 +1,11 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "mime-types", "< 2.0", :platform => :ruby_18
6
+ gem "rubyzip", "< 1.0", :platform => :ruby_18
7
+ gem "byebug"
8
+ gem "rails", "~> 5.0.0"
9
+ gem "jquery-rails"
10
+
11
+ gemspec :path => "../"
@@ -0,0 +1,11 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "mime-types", "< 2.0", :platform => :ruby_18
6
+ gem "rubyzip", "< 1.0", :platform => :ruby_18
7
+ gem "byebug"
8
+ gem "rails", "~> 5.1.0"
9
+ gem "jquery-rails"
10
+
11
+ gemspec :path => "../"
data/init.rb ADDED
@@ -0,0 +1,4 @@
1
+ # For use by Railed and de-Railed applications alike
2
+
3
+ require 'active_record'
4
+ require File.join(File.dirname(__FILE__), 'lib', 'activerecord-tablefree')
@@ -0,0 +1,318 @@
1
+ # See #ActiveRecord::Tablefree
2
+ require 'activerecord-tablefree/version'
3
+
4
+ module ActiveRecord
5
+
6
+ # = ActiveRecord::Tablefree
7
+ #
8
+ # Allow classes to behave like ActiveRecord models, but without an associated
9
+ # database table. A great way to capitalize on validations. Based on the
10
+ # original post at http://www.railsweenie.com/forums/2/topics/724 (which seems
11
+ # to have disappeared from the face of the earth).
12
+ #
13
+ # = Example usage
14
+ #
15
+ # class ContactMessage < ActiveRecord::Base
16
+ #
17
+ # has_no_table
18
+ #
19
+ # column :name, :string
20
+ # column :email, :string
21
+ # column :message, :string
22
+ #
23
+ # end
24
+ #
25
+ # msg = ContactMessage.new( params[:msg] )
26
+ # if msg.valid?
27
+ # ContactMessageSender.deliver_message( msg )
28
+ # redirect_to :action => :sent
29
+ # end
30
+ #
31
+ module Tablefree
32
+ require 'active_record'
33
+
34
+ class NoDatabase < StandardError; end
35
+ class Unsupported < StandardError; end
36
+
37
+ def self.included( base ) #:nodoc:
38
+ base.send :extend, ActsMethods
39
+ end
40
+
41
+ module ActsMethods #:nodoc:
42
+
43
+ # A model that needs to be tablefree will call this method to indicate
44
+ # it.
45
+ def has_no_table(options = {:database => :fail_fast})
46
+ raise ArgumentError.new("Invalid database option '#{options[:database]}'") unless [:fail_fast, :pretend_success].member? options[:database]
47
+ # keep our options handy
48
+ class_attribute :tablefree_options
49
+ self.tablefree_options = {
50
+ :database => options[:database],
51
+ :columns_hash => {}
52
+ }
53
+
54
+ # extend
55
+ extend ActiveRecord::Tablefree::SingletonMethods
56
+ extend ActiveRecord::Tablefree::ClassMethods
57
+
58
+ # include
59
+ include ActiveRecord::Tablefree::InstanceMethods
60
+
61
+ # setup columns
62
+ include ActiveModel::AttributeAssignment
63
+ include ActiveRecord::ModelSchema
64
+ end
65
+
66
+ def tablefree?
67
+ false
68
+ end
69
+
70
+ end
71
+
72
+ module SingletonMethods
73
+
74
+ # Used internally by ActiveRecord 5. This is the special hook that makes everything else work.
75
+ def load_schema!
76
+ @columns_hash = tablefree_options[:columns_hash].except(*ignored_columns)
77
+ @columns_hash.each do |name, column|
78
+ define_attribute(
79
+ name,
80
+ connection.lookup_cast_type_from_column(column),
81
+ default: column.default,
82
+ user_provided_default: false
83
+ )
84
+ end
85
+ end
86
+
87
+ # Register a new column.
88
+ def column(name, sql_type = nil, default = nil, null = true)
89
+ cast_type = "ActiveRecord::Type::#{sql_type.to_s.camelize}".constantize.new
90
+ tablefree_options[:columns_hash][name.to_s] = ActiveRecord::ConnectionAdapters::Column.new(name.to_s, default, cast_type, sql_type.to_s, null)
91
+ end
92
+
93
+ # Register a set of columns with the same SQL type
94
+ def add_columns(sql_type, *args)
95
+ args.each do |col|
96
+ column col, sql_type
97
+ end
98
+ end
99
+
100
+ def destroy(*args)
101
+ case tablefree_options[:database]
102
+ when :pretend_success
103
+ self.new()
104
+ when :fail_fast
105
+ raise NoDatabase.new("Can't #destroy on Tablefree class")
106
+ end
107
+ end
108
+
109
+ def destroy_all(*_args)
110
+ case tablefree_options[:database]
111
+ when :pretend_success
112
+ []
113
+ when :fail_fast
114
+ raise NoDatabase.new("Can't #destroy_all on Tablefree class")
115
+ end
116
+ end
117
+
118
+ case ActiveRecord::VERSION::MAJOR
119
+ when 5
120
+ def find_by_sql(*args)
121
+ case tablefree_options[:database]
122
+ when :pretend_success
123
+ []
124
+ when :fail_fast
125
+ raise NoDatabase.new("Can't #find_by_sql on Tablefree class")
126
+ end
127
+
128
+ end
129
+ else
130
+ raise Unsupported.new("Unsupported ActiveRecord version")
131
+ end
132
+
133
+ def transaction(&block)
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
+ end
142
+
143
+ def tablefree?
144
+ true
145
+ end
146
+
147
+ def table_exists?
148
+ false
149
+ end
150
+ end
151
+
152
+ module ClassMethods
153
+
154
+ def from_query_string(query_string)
155
+ unless query_string.blank?
156
+ params = query_string.split('&').collect do |chunk|
157
+ next if chunk.empty?
158
+ key, value = chunk.split('=', 2)
159
+ next if key.empty?
160
+ value = value.nil? ? nil : CGI.unescape(value)
161
+ [ CGI.unescape(key), value ]
162
+ end.compact.to_h
163
+
164
+ new(params)
165
+ else
166
+ new
167
+ end
168
+ end
169
+
170
+ def connection
171
+ conn = Object.new()
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
249
+ end
250
+
251
+ end
252
+
253
+ module InstanceMethods
254
+
255
+ def to_query_string(prefix = nil)
256
+ attributes.to_a.collect{|(name,value)| escaped_var_name(name, prefix) + "=" + escape_for_url(value) if value }.compact.join("&")
257
+ end
258
+
259
+ def quote_value(_value, _column = nil)
260
+ ""
261
+ end
262
+
263
+ %w(create create_record _create_record update update_record _update_record).each do |method_name|
264
+ define_method(method_name) do |*args|
265
+ case self.class.tablefree_options[:database]
266
+ when :pretend_success
267
+ true
268
+ when :fail_fast
269
+ raise NoDatabase.new("Can't ##{method_name} a Tablefree object")
270
+ end
271
+ end
272
+ end
273
+
274
+ def destroy
275
+ case self.class.tablefree_options[:database]
276
+ when :pretend_success
277
+ @destroyed = true
278
+ freeze
279
+ when :fail_fast
280
+ raise NoDatabase.new("Can't #destroy a Tablefree object")
281
+ end
282
+ end
283
+
284
+ def reload(*args)
285
+ case self.class.tablefree_options[:database]
286
+ when :pretend_success
287
+ self
288
+ when :fail_fast
289
+ raise NoDatabase.new("Can't #reload a Tablefree object")
290
+ end
291
+ end
292
+
293
+ def add_to_transaction
294
+ end
295
+
296
+ private
297
+
298
+ def escaped_var_name(name, prefix = nil)
299
+ prefix ? "#{URI.escape(prefix)}[#{URI.escape(name)}]" : URI.escape(name)
300
+ end
301
+
302
+ def escape_for_url(value)
303
+ case value
304
+ when true then "1"
305
+ when false then "0"
306
+ when nil then ""
307
+ else URI.escape(value.to_s)
308
+ end
309
+ rescue
310
+ ""
311
+ end
312
+
313
+ end
314
+
315
+ end
316
+ end
317
+
318
+ ActiveRecord::Base.send( :include, ActiveRecord::Tablefree )