Skip to content

Dashboard Events #348

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
May 18, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ end

group :test do
gem "database_cleaner", '1.6.2'
gem 'shoulda', ">= 3.5"
gem 'shoulda-matchers', '~> 3.1.2'
gem 'fabrication'
gem 'faker'
gem 'capybara', '~> 2.7'
Expand Down
10 changes: 3 additions & 7 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -329,12 +329,8 @@ GEM
selenium-webdriver (3.11.0)
childprocess (~> 0.5)
rubyzip (~> 1.2)
shoulda (3.5.0)
shoulda-context (~> 1.0, >= 1.0.1)
shoulda-matchers (>= 1.4.1, < 3.0)
shoulda-context (1.2.1)
shoulda-matchers (2.8.0)
activesupport (>= 3.0.0)
shoulda-matchers (3.1.2)
activesupport (>= 4.0.0)
simple_form (3.1.0)
actionpack (~> 4.0)
activemodel (~> 4.0)
Expand Down Expand Up @@ -427,7 +423,7 @@ DEPENDENCIES
rubocop (~> 0.52.1)
sass-rails (~> 5.0.7)
select2-rails
shoulda (>= 3.5)
shoulda-matchers (~> 3.1.2)
simple_form (>= 3.0.0)
skylight
uglifier (= 2.7.2)
Expand Down
20 changes: 20 additions & 0 deletions app/models/event.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
class Event < ActiveRecord::Base
enum action: { created: 0, updated: 1 }

belongs_to :post
belongs_to :member
belongs_to :transfer

validates :action, presence: true
validate :resource_presence

private

# Validates that only one resource id is set
#
def resource_presence
return if post_id.present? ^ member_id.present? ^ transfer_id.present?

errors.add(:base, 'Specify only one resource id: `post_id`, `member_id` or `transfer_id`')
end
end
28 changes: 27 additions & 1 deletion app/services/persister/member_persister.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,33 @@ def initialize(member)
end

def save
member.save
::ActiveRecord::Base.transaction do
member.save!
create_save_event!
member
end
rescue ActiveRecord::RecordInvalid => _exception
false
end

def update_attributes(params)
::ActiveRecord::Base.transaction do
member.update_attributes!(params)
create_update_event!
member
end
rescue ActiveRecord::RecordInvalid => _exception
false
end

private

def create_save_event!
::Event.create! action: :created, member: member
end

def create_update_event!
::Event.create! action: :updated, member: member
end
end
end
26 changes: 24 additions & 2 deletions app/services/persister/post_persister.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,33 @@ def initialize(post)
end

def save
post.save
::ActiveRecord::Base.transaction do
post.save!
create_save_event!
post
end
rescue ActiveRecord::RecordInvalid => _exception
false
end

def update_attributes(params)
post.update_attributes(params)
::ActiveRecord::Base.transaction do
post.update_attributes!(params)
create_update_event!
post
end
rescue ActiveRecord::RecordInvalid => _exception
false
end

private

def create_save_event!
::Event.create! action: :created, post: post
end

def create_update_event!
::Event.create! action: :updated, post: post
end
end
end
28 changes: 27 additions & 1 deletion app/services/persister/transfer_persister.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,33 @@ def initialize(transfer)
end

def save
transfer.save
::ActiveRecord::Base.transaction do
transfer.save!
create_save_event!
transfer
end
rescue ActiveRecord::RecordInvalid => _exception
false
end

def update_attributes(params)
::ActiveRecord::Base.transaction do
transfer.update_attributes!(params)
create_update_event!
transfer
end
rescue ActiveRecord::RecordInvalid => _exception
false
end

private

def create_save_event!
::Event.create! action: :created, transfer: transfer
end

def create_update_event!
::Event.create! action: :updated, transfer: transfer
end
end
end
43 changes: 43 additions & 0 deletions db/migrate/20180501093846_create_events.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# This migration does not use Rails ActiveRecord ORM DSL since
# it doesn't provide data integrity (foreign key) for polymorphic models.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I fully understand what you mean.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Read the article 😂

#
# The technique applied is called Exclusive Belongs To (AKA Exclusive Arc)
#
# Please read the following article for more details:
# https://hashrocket.com/blog/posts/modeling-polymorphic-associations-in-a-relational-database
#
class CreateEvents < ActiveRecord::Migration
def up
create_table :events do |t|
t.integer :action, null:false
t.integer :post_id
t.integer :member_id
t.integer :transfer_id
t.timestamps
end

add_foreign_key :events, :posts, name: 'events_post_id_fkey'
add_foreign_key :events, :members, name: 'events_member_id_fkey'
add_foreign_key :events, :transfers, name: 'events_transfer_id_fkey'

add_index :events, :post_id, unique: true, where: 'post_id IS NOT NULL'
add_index :events, :member_id, unique: true, where: 'member_id IS NOT NULL'
add_index :events, :transfer_id, unique: true, where: 'transfer_id IS NOT NULL'

execute <<-SQL
ALTER TABLE events
ADD CHECK(action IN (0, 1)),
ADD CHECK(
(
(post_id IS NOT NULL)::integer +
(member_id IS NOT NULL)::integer +
(transfer_id IS NOT NULL)::integer
) = 1
);
SQL
end

def down
drop_table :events
end
end
18 changes: 17 additions & 1 deletion db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema.define(version: 20180221161343) do
ActiveRecord::Schema.define(version: 20180501093846) do

# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
Expand Down Expand Up @@ -76,6 +76,19 @@
add_index "documents", ["documentable_id", "documentable_type"], name: "index_documents_on_documentable_id_and_documentable_type", using: :btree
add_index "documents", ["label"], name: "index_documents_on_label", using: :btree

create_table "events", force: :cascade do |t|
t.integer "action", null: false
t.integer "post_id"
t.integer "member_id"
t.integer "transfer_id"
t.datetime "created_at"
t.datetime "updated_at"
end

add_index "events", ["member_id"], name: "index_events_on_member_id", unique: true, where: "(member_id IS NOT NULL)", using: :btree
add_index "events", ["post_id"], name: "index_events_on_post_id", unique: true, where: "(post_id IS NOT NULL)", using: :btree
add_index "events", ["transfer_id"], name: "index_events_on_transfer_id", unique: true, where: "(transfer_id IS NOT NULL)", using: :btree

create_table "members", force: :cascade do |t|
t.integer "user_id"
t.integer "organization_id"
Expand Down Expand Up @@ -198,4 +211,7 @@
add_index "users", ["email"], name: "index_users_on_email", using: :btree

add_foreign_key "accounts", "organizations"
add_foreign_key "events", "members", name: "events_member_id_fkey"
add_foreign_key "events", "posts", name: "events_post_id_fkey"
add_foreign_key "events", "transfers", name: "events_transfer_id_fkey"
end
5 changes: 5 additions & 0 deletions spec/fabricators/transfer_fabricator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Fabricator(:transfer) do
source { Fabricate(:account) }
destination { Fabricate(:account) }
amount 10
end
49 changes: 49 additions & 0 deletions spec/models/event_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
require 'spec_helper'

describe Event do
describe 'Validations' do
it { is_expected.to validate_presence_of(:action) }
it do
is_expected.to define_enum_for(:action)
.with([:created, :updated])
end

describe '#resource_presence validation' do
let(:post) { Fabricate(:post) }
let(:member) { Fabricate(:member) }
let(:event) { Event.new action: 'created' }

context 'has no resources' do
it { expect(event).to_not be_valid }
end

context 'has one resource' do
before { event.post = post }

it { expect(event).to be_valid }
end

context 'has two resources' do
before { event.post = post; event.member = member }

it { expect(event).to_not be_valid }
end
end
end

describe 'Relations' do
it { is_expected.to belong_to(:post) }
it { is_expected.to belong_to(:member) }
it { is_expected.to belong_to(:transfer) }

it { is_expected.to have_db_column(:post_id) }
it { is_expected.to have_db_column(:member_id) }
it { is_expected.to have_db_column(:transfer_id) }
end

describe 'Indexes' do
it { is_expected.to have_db_index(:post_id) }
it { is_expected.to have_db_index(:member_id) }
it { is_expected.to have_db_index(:transfer_id) }
end
end
25 changes: 21 additions & 4 deletions spec/services/persister/member_persister_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,32 @@
describe Persister::MemberPersister do
let(:organization) { Fabricate(:organization) }
let(:user) { Fabricate(:user) }
let(:member) { Fabricate.build(:member, user: user, organization: organization) }
let(:persister) { ::Persister::MemberPersister.new(member) }

describe '#save' do
before { persister.save }

it 'saves the member' do
member = Member.new(user: user, organization: organization)
expect(member).to be_persisted
end

persister = ::Persister::MemberPersister.new(member)
persister.save
# TODO: write better expectation
it 'creates an event' do
expect(Event.where(member_id: member.id).first.action).to eq('created')
end
end

expect(member).to be_persisted
describe '#update_attributes' do
before { persister.update_attributes(member_uid: 666) }

it 'updates the resource attributes' do
expect(member.member_uid).to eq(666)
end

# TODO: write better expectation
it 'creates an event' do
expect(Event.where(member_id: member.id).first.action).to eq('updated')
end
end
end
33 changes: 24 additions & 9 deletions spec/services/persister/post_persister_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,40 @@
let(:organization) { Fabricate(:organization) }
let(:user) { Fabricate(:user) }
let(:category) { Fabricate(:category) }
let(:post) { Fabricate(:post, organization: organization) }
let(:post) do
Fabricate.build(
:offer,
organization: organization,
user: user,
category: category,
title: 'Title'
)
end
let(:persister) { ::Persister::PostPersister.new(post) }

describe '#save' do
it 'saves the post' do
post = Offer.new(organization: organization, user: user, category: category, title: 'Title')
persister = ::Persister::PostPersister.new(post)

persister.save
before { persister.save }

it 'saves the post' do
expect(post).to be_persisted
end

# TODO: write better expectation
it 'creates an event' do
expect(Event.where(post_id: post.id).first.action).to eq('created')
end
end

describe '#update_attributes' do
it 'updates the attributes' do
persister = ::Persister::PostPersister.new(post)
persister.update_attributes(title: 'New title')
before { persister.update_attributes(title: 'New title') }

it 'updates the resource attributes' do
expect(post.title).to eq('New title')
end

# TODO: write better expectation
it 'creates an event' do
expect(Event.where(post_id: post.id).first.action).to eq('updated')
end
end
end
Loading