|
# frozen_string_literal: true |
|
|
|
# -- RAILS SETUP (ignore) -- |
|
|
|
require "bundler/inline" |
|
|
|
gemfile(true) do |
|
source "https://rubygems.org" |
|
|
|
git_source(:github) { |repo| "https://github.com/#{repo}.git" } |
|
|
|
gem "rails", github: "rails/rails", branch: "main" |
|
gem 'rspec-rails', '~> 4.0.2' |
|
gem "sqlite3" |
|
gem 'byebug' |
|
end |
|
|
|
require 'byebug' |
|
require "active_record" |
|
require "minitest/autorun" |
|
require "logger" |
|
require 'rails/all' |
|
|
|
# This connection will do for database-independent bug reports. |
|
ActiveRecord::Base.configurations = { "test" => { adapter: "sqlite3", database: "database.sqlite" } } |
|
ActiveRecord::Base.establish_connection ActiveRecord::Base.configurations['test'] |
|
ActiveRecord::Base.logger = Logger.new(STDOUT) |
|
|
|
# -- RAILS APP -- |
|
|
|
ActiveRecord::Schema.define do |
|
create_table :posts, force: true do |t| |
|
t.datetime :reviewed_at |
|
t.string :title, null: false |
|
end |
|
end |
|
|
|
# requires #reviewed_at:datetime column |
|
module Reviewable |
|
extend ActiveSupport::Concern |
|
|
|
included do |
|
scope :reviewed, -> { where.not(reviewed_at: nil) } |
|
scope :unreviewed, -> { where(reviewed_at: nil) } |
|
end |
|
|
|
def reviewed? |
|
reviewed_at.present? |
|
end |
|
|
|
def review(time = DateTime.current) |
|
update(reviewed_at: time) |
|
end |
|
end |
|
|
|
class Post < ActiveRecord::Base |
|
include Reviewable |
|
|
|
validates_presence_of :title |
|
end |
|
|
|
# -- RAILS SPEC HELPER -- |
|
|
|
module InMemoryDatabaseHelpers |
|
extend ActiveSupport::Concern |
|
|
|
class_methods do |
|
def switch_to_SQLite(&block) |
|
before(:all) { switch_to_in_memory_database(&block) } |
|
after(:all) { switch_back_to_test_database } |
|
end |
|
end |
|
|
|
private |
|
|
|
def switch_to_in_memory_database(&block) |
|
raise 'No migration given' unless block_given? |
|
|
|
ActiveRecord::Migration.verbose = false |
|
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:") |
|
ActiveRecord::Schema.define(version: 1, &block) |
|
end |
|
|
|
def switch_back_to_test_database |
|
ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations['test']) |
|
end |
|
end |
|
|
|
# -- RAILS SPEC SUITE -- |
|
|
|
# requires a :reviewable object |
|
shared_examples 'reviewable'do |
|
describe 'the interface' do |
|
subject { described_class.new } |
|
|
|
it 'has the correct interface' do |
|
expect(subject).to respond_to(:reviewed?) |
|
expect(subject).to respond_to(:review) |
|
expect(described_class).to respond_to(:reviewed) |
|
expect(described_class).to respond_to(:unreviewed) |
|
end |
|
end |
|
|
|
describe 'scopes' do |
|
let!(:records) { described_class.where(id: [reviewed, unreviewed]) } |
|
|
|
before do |
|
expect(reviewed.reviewed?).to eql true |
|
expect(unreviewed.reviewed?).to eql false |
|
end |
|
|
|
describe '.reviewed' do |
|
subject { records.reviewed } |
|
|
|
it { is_expected.to eq [reviewed] } |
|
end |
|
|
|
describe '.unreviewed' do |
|
subject { records.unreviewed } |
|
|
|
it { is_expected.to eq [unreviewed] } |
|
end |
|
end |
|
|
|
describe '#reviewed?' do |
|
subject { described_class.new } |
|
|
|
it 'returns the correct boolean based on #reviewed_at' do |
|
subject.reviewed_at = nil |
|
expect(subject.reviewed?).to eql false |
|
|
|
subject.reviewed_at = DateTime.current |
|
expect(subject.reviewed?).to eql true |
|
end |
|
end |
|
|
|
describe '#review' do |
|
let(:time) { DateTime.current } |
|
|
|
subject { unreviewed.review(time) } |
|
|
|
it 'updates the reviewed_at attribute' do |
|
expect { subject }.to change { unreviewed.reload.reviewed_at }.from(nil).to(time) |
|
end |
|
end |
|
end |
|
|
|
class FakeReviewable < ActiveRecord::Base |
|
include Reviewable |
|
end |
|
|
|
describe Reviewable do |
|
include InMemoryDatabaseHelpers |
|
|
|
switch_to_SQLite do |
|
create_table :fake_reviewables do |t| |
|
t.datetime :reviewed_at |
|
end |
|
end |
|
|
|
describe FakeReviewable, type: :model do |
|
include_examples 'reviewable' do |
|
let(:reviewed) { FakeReviewable.create! reviewed_at: DateTime.current } |
|
let(:unreviewed) { FakeReviewable.create! reviewed_at: nil } |
|
end |
|
end |
|
end |
|
|
|
# This example is at the end to show that the Rails app went back to the current testing database configuration defined in the setup |
|
describe Post do |
|
include_examples 'reviewable' do |
|
let(:reviewed) { Post.create! title: 'title1', reviewed_at: DateTime.current } |
|
let(:unreviewed) { Post.create! title: 'title2', reviewed_at: nil } |
|
end |
|
end |
Thanks for providing the examples! I find it funny that "The Rails Way" praises using concerns yet there is no easy way to test it. You can see it in Basecamp's own Campfire code that they have concerns yet their tests are coupled to models directly.