Skip to content

Commit f26377d

Browse files
authored
Merge pull request #348 from coopdevs/feature/dashboard-events
Dashboard Events
2 parents e45a9bb + b495581 commit f26377d

File tree

14 files changed

+297
-30
lines changed

14 files changed

+297
-30
lines changed

Gemfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ end
5454

5555
group :test do
5656
gem "database_cleaner", '1.6.2'
57-
gem 'shoulda', ">= 3.5"
57+
gem 'shoulda-matchers', '~> 3.1.2'
5858
gem 'fabrication'
5959
gem 'faker'
6060
gem 'capybara', '~> 2.7'

Gemfile.lock

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -329,12 +329,8 @@ GEM
329329
selenium-webdriver (3.11.0)
330330
childprocess (~> 0.5)
331331
rubyzip (~> 1.2)
332-
shoulda (3.5.0)
333-
shoulda-context (~> 1.0, >= 1.0.1)
334-
shoulda-matchers (>= 1.4.1, < 3.0)
335-
shoulda-context (1.2.1)
336-
shoulda-matchers (2.8.0)
337-
activesupport (>= 3.0.0)
332+
shoulda-matchers (3.1.2)
333+
activesupport (>= 4.0.0)
338334
simple_form (3.1.0)
339335
actionpack (~> 4.0)
340336
activemodel (~> 4.0)
@@ -427,7 +423,7 @@ DEPENDENCIES
427423
rubocop (~> 0.52.1)
428424
sass-rails (~> 5.0.7)
429425
select2-rails
430-
shoulda (>= 3.5)
426+
shoulda-matchers (~> 3.1.2)
431427
simple_form (>= 3.0.0)
432428
skylight
433429
uglifier (= 2.7.2)

app/models/event.rb

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
class Event < ActiveRecord::Base
2+
enum action: { created: 0, updated: 1 }
3+
4+
belongs_to :post
5+
belongs_to :member
6+
belongs_to :transfer
7+
8+
validates :action, presence: true
9+
validate :resource_presence
10+
11+
private
12+
13+
# Validates that only one resource id is set
14+
#
15+
def resource_presence
16+
return if post_id.present? ^ member_id.present? ^ transfer_id.present?
17+
18+
errors.add(:base, 'Specify only one resource id: `post_id`, `member_id` or `transfer_id`')
19+
end
20+
end

app/services/persister/member_persister.rb

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,33 @@ def initialize(member)
77
end
88

99
def save
10-
member.save
10+
::ActiveRecord::Base.transaction do
11+
member.save!
12+
create_save_event!
13+
member
14+
end
15+
rescue ActiveRecord::RecordInvalid => _exception
16+
false
17+
end
18+
19+
def update_attributes(params)
20+
::ActiveRecord::Base.transaction do
21+
member.update_attributes!(params)
22+
create_update_event!
23+
member
24+
end
25+
rescue ActiveRecord::RecordInvalid => _exception
26+
false
27+
end
28+
29+
private
30+
31+
def create_save_event!
32+
::Event.create! action: :created, member: member
33+
end
34+
35+
def create_update_event!
36+
::Event.create! action: :updated, member: member
1137
end
1238
end
1339
end

app/services/persister/post_persister.rb

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,33 @@ def initialize(post)
77
end
88

99
def save
10-
post.save
10+
::ActiveRecord::Base.transaction do
11+
post.save!
12+
create_save_event!
13+
post
14+
end
15+
rescue ActiveRecord::RecordInvalid => _exception
16+
false
1117
end
1218

1319
def update_attributes(params)
14-
post.update_attributes(params)
20+
::ActiveRecord::Base.transaction do
21+
post.update_attributes!(params)
22+
create_update_event!
23+
post
24+
end
25+
rescue ActiveRecord::RecordInvalid => _exception
26+
false
27+
end
28+
29+
private
30+
31+
def create_save_event!
32+
::Event.create! action: :created, post: post
33+
end
34+
35+
def create_update_event!
36+
::Event.create! action: :updated, post: post
1537
end
1638
end
1739
end

app/services/persister/transfer_persister.rb

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,33 @@ def initialize(transfer)
77
end
88

99
def save
10-
transfer.save
10+
::ActiveRecord::Base.transaction do
11+
transfer.save!
12+
create_save_event!
13+
transfer
14+
end
15+
rescue ActiveRecord::RecordInvalid => _exception
16+
false
17+
end
18+
19+
def update_attributes(params)
20+
::ActiveRecord::Base.transaction do
21+
transfer.update_attributes!(params)
22+
create_update_event!
23+
transfer
24+
end
25+
rescue ActiveRecord::RecordInvalid => _exception
26+
false
27+
end
28+
29+
private
30+
31+
def create_save_event!
32+
::Event.create! action: :created, transfer: transfer
33+
end
34+
35+
def create_update_event!
36+
::Event.create! action: :updated, transfer: transfer
1137
end
1238
end
1339
end
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# This migration does not use Rails ActiveRecord ORM DSL since
2+
# it doesn't provide data integrity (foreign key) for polymorphic models.
3+
#
4+
# The technique applied is called Exclusive Belongs To (AKA Exclusive Arc)
5+
#
6+
# Please read the following article for more details:
7+
# https://hashrocket.com/blog/posts/modeling-polymorphic-associations-in-a-relational-database
8+
#
9+
class CreateEvents < ActiveRecord::Migration
10+
def up
11+
create_table :events do |t|
12+
t.integer :action, null:false
13+
t.integer :post_id
14+
t.integer :member_id
15+
t.integer :transfer_id
16+
t.timestamps
17+
end
18+
19+
add_foreign_key :events, :posts, name: 'events_post_id_fkey'
20+
add_foreign_key :events, :members, name: 'events_member_id_fkey'
21+
add_foreign_key :events, :transfers, name: 'events_transfer_id_fkey'
22+
23+
add_index :events, :post_id, unique: true, where: 'post_id IS NOT NULL'
24+
add_index :events, :member_id, unique: true, where: 'member_id IS NOT NULL'
25+
add_index :events, :transfer_id, unique: true, where: 'transfer_id IS NOT NULL'
26+
27+
execute <<-SQL
28+
ALTER TABLE events
29+
ADD CHECK(action IN (0, 1)),
30+
ADD CHECK(
31+
(
32+
(post_id IS NOT NULL)::integer +
33+
(member_id IS NOT NULL)::integer +
34+
(transfer_id IS NOT NULL)::integer
35+
) = 1
36+
);
37+
SQL
38+
end
39+
40+
def down
41+
drop_table :events
42+
end
43+
end

db/schema.rb

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
#
1212
# It's strongly recommended that you check this file into your version control system.
1313

14-
ActiveRecord::Schema.define(version: 20180221161343) do
14+
ActiveRecord::Schema.define(version: 20180501093846) do
1515

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

79+
create_table "events", force: :cascade do |t|
80+
t.integer "action", null: false
81+
t.integer "post_id"
82+
t.integer "member_id"
83+
t.integer "transfer_id"
84+
t.datetime "created_at"
85+
t.datetime "updated_at"
86+
end
87+
88+
add_index "events", ["member_id"], name: "index_events_on_member_id", unique: true, where: "(member_id IS NOT NULL)", using: :btree
89+
add_index "events", ["post_id"], name: "index_events_on_post_id", unique: true, where: "(post_id IS NOT NULL)", using: :btree
90+
add_index "events", ["transfer_id"], name: "index_events_on_transfer_id", unique: true, where: "(transfer_id IS NOT NULL)", using: :btree
91+
7992
create_table "members", force: :cascade do |t|
8093
t.integer "user_id"
8194
t.integer "organization_id"
@@ -198,4 +211,7 @@
198211
add_index "users", ["email"], name: "index_users_on_email", using: :btree
199212

200213
add_foreign_key "accounts", "organizations"
214+
add_foreign_key "events", "members", name: "events_member_id_fkey"
215+
add_foreign_key "events", "posts", name: "events_post_id_fkey"
216+
add_foreign_key "events", "transfers", name: "events_transfer_id_fkey"
201217
end
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Fabricator(:transfer) do
2+
source { Fabricate(:account) }
3+
destination { Fabricate(:account) }
4+
amount 10
5+
end

spec/models/event_spec.rb

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
require 'spec_helper'
2+
3+
describe Event do
4+
describe 'Validations' do
5+
it { is_expected.to validate_presence_of(:action) }
6+
it do
7+
is_expected.to define_enum_for(:action)
8+
.with([:created, :updated])
9+
end
10+
11+
describe '#resource_presence validation' do
12+
let(:post) { Fabricate(:post) }
13+
let(:member) { Fabricate(:member) }
14+
let(:event) { Event.new action: 'created' }
15+
16+
context 'has no resources' do
17+
it { expect(event).to_not be_valid }
18+
end
19+
20+
context 'has one resource' do
21+
before { event.post = post }
22+
23+
it { expect(event).to be_valid }
24+
end
25+
26+
context 'has two resources' do
27+
before { event.post = post; event.member = member }
28+
29+
it { expect(event).to_not be_valid }
30+
end
31+
end
32+
end
33+
34+
describe 'Relations' do
35+
it { is_expected.to belong_to(:post) }
36+
it { is_expected.to belong_to(:member) }
37+
it { is_expected.to belong_to(:transfer) }
38+
39+
it { is_expected.to have_db_column(:post_id) }
40+
it { is_expected.to have_db_column(:member_id) }
41+
it { is_expected.to have_db_column(:transfer_id) }
42+
end
43+
44+
describe 'Indexes' do
45+
it { is_expected.to have_db_index(:post_id) }
46+
it { is_expected.to have_db_index(:member_id) }
47+
it { is_expected.to have_db_index(:transfer_id) }
48+
end
49+
end

0 commit comments

Comments
 (0)