Skip to content
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

Upvote for multiple recipients #35

Closed
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
rvm:
- 2.3.1
- 2.3.3
5 changes: 4 additions & 1 deletion Gemfile
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
source 'https://rubygems.org'

ruby '2.3.1'
ruby '2.3.3'

gem 'aggregate_root'
gem 'arkency-command_bus', require: false
gem 'rails', '4.2.6'
gem 'pg'
gem 'rails_12factor', group: :production
gem 'rails_event_store', require: false

group :development do
gem 'sqlite3'
Expand Down
20 changes: 19 additions & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,11 @@ GEM
minitest (~> 5.1)
thread_safe (~> 0.3, >= 0.3.4)
tzinfo (~> 1.1)
aggregate_root (0.4.0)
activesupport (>= 3.0)
arel (6.0.3)
arkency-command_bus (0.4.0)
thread_safe
builder (3.2.2)
byebug (9.0.5)
concurrent-ruby (1.0.2)
Expand Down Expand Up @@ -87,6 +91,16 @@ GEM
rails_12factor (0.0.3)
rails_serve_static_assets
rails_stdout_logging
rails_event_store (0.14.3)
activemodel (>= 3.0)
activesupport (>= 3.0)
aggregate_root (~> 0.4.0)
rails_event_store_active_record (~> 0.6.10)
ruby_event_store (~> 0.13.0)
rails_event_store_active_record (0.6.10)
activemodel (>= 3.0)
activesupport (>= 3.0)
ruby_event_store (~> 0.13.0)
rails_serve_static_assets (0.0.5)
rails_stdout_logging (0.0.5)
railties (4.2.6)
Expand All @@ -95,6 +109,7 @@ GEM
rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0)
rake (11.2.2)
ruby_event_store (0.13.0)
spring (1.7.2)
sprockets (3.7.0)
concurrent-ruby (~> 1.0)
Expand All @@ -113,16 +128,19 @@ PLATFORMS
ruby

DEPENDENCIES
aggregate_root
arkency-command_bus
byebug
dotenv-rails
pg
rails (= 4.2.6)
rails_12factor
rails_event_store
spring
sqlite3

RUBY VERSION
ruby 2.3.1p112
ruby 2.3.3p222

BUNDLED WITH
1.13.6
2 changes: 2 additions & 0 deletions app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,6 @@ class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception

include CommandBusSetup
end
21 changes: 16 additions & 5 deletions app/controllers/slack_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,23 @@ class SlackController < ApplicationController

def plus
team = PrepareTeam.new.call(team_params)
result = PlusOne.new(team).call(plus_params)
upvote_uuid = SecureRandom.uuid
ActiveRecord::Base.transaction do
cmd = Command::GiveUpvote.new(team_id: team.id, params: plus_params, upvote_uuid: upvote_uuid)
command_bus.(cmd)
end

upvote = Upvote.find_by(uuid: upvote_uuid)
result =
{
text: "#{upvote.sender.slack_user_name}(#{upvote.sender.points}) gave +1 for #{upvote.recipients.first.slack_user_name}(#{upvote.recipients.first.points})",
parse: "none"
}

render json: result
rescue PlusOne::CannotPlusOneYourself
cant_plus_one_yourself
rescue PlusOne::InvalidSlackToken
rescue Domain::Sender::CannotUpvoteYourself
cant_upvote_yourself
rescue Domain::Sender::InvalidSlackToken
invalid_slack_token
end

Expand Down Expand Up @@ -44,7 +55,7 @@ def bot_instruction
"Want to help with PlusOne development? Feel welcome: https://github.com/arkency/plusone"
end

def cant_plus_one_yourself
def cant_upvote_yourself
render json: {text: "Nope... not gonna happen."}
end

Expand Down
4 changes: 4 additions & 0 deletions app/models/recipients_upvote.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
class RecipientsUpvote < ActiveRecord::Base
belongs_to :upvote
belongs_to :recipient, class_name: "TeamMember"
end
3 changes: 3 additions & 0 deletions app/models/upvote.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
class Upvote < ActiveRecord::Base
belongs_to :sender, class_name: "TeamMember"
belongs_to :recipient, class_name: "TeamMember"

has_many :recipients_upvotes
has_many :recipients, through: :recipients_upvotes, class_name: 'TeamMember'
end
8 changes: 6 additions & 2 deletions app/services/message_parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,14 @@ def initialize(text, trigger_word)
self.trigger_word = trigger_word
end

def recipient_name
def first_recipient
recipients.first || ''
end

def recipients
beginning = trigger_word.size
remaining = text[beginning..text.size-1]
remaining.strip.split.first || ""
remaining.strip.split
end

private
Expand Down
29 changes: 0 additions & 29 deletions app/services/plus_one.rb

This file was deleted.

16 changes: 10 additions & 6 deletions app/services/prepare_transaction_actors.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ def initialize team, slack_adapter

def call(params)
sender_username = fetch_name(params[:user_name])
recipient_username = fetch_name(recipient_name(params.slice(:text, :trigger_word)))
recipients_usernames = fetch_recipients(params.slice(:text, :trigger_word)).map(&method(:fetch_name))

raise MissingRecipient unless recipient_username.present?
raise MissingRecipient unless recipients_usernames.any?
recipients = recipients_usernames.map { |name| prepare_team_member.call(@team, name) }

sender = prepare_team_member.call(@team, sender_username, params[:user_id])
recipient = prepare_team_member.call(@team, recipient_username)
[sender, recipient]
[sender, recipients]
end

private
Expand All @@ -27,10 +27,14 @@ def clean_name(name)
name.gsub(/^(@+)/, "")
end

def recipient_name(text_params)
MessageParser.new(text_params[:text], text_params[:trigger_word]).recipient_name
def fetch_recipients(text_params)
MessageParser.new(text_params[:text], text_params[:trigger_word]).recipients
end

# def recipient_name(text_params)
# MessageParser.new(text_params[:text], text_params[:trigger_word]).first_recipient
# end

def prepare_team_member
PrepareTeamMember.new
end
Expand Down
1 change: 1 addition & 0 deletions config/application.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,6 @@ class Application < Rails::Application
# The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
# config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
# config.i18n.default_locale = :de
config.autoload_paths << Rails.root.join('lib')
end
end
7 changes: 7 additions & 0 deletions config/initializers/aggregate_root.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
require 'rails_event_store'

AggregateRoot.configure do |config|
config.default_event_store = RailsEventStore::Client.new.tap do |es|
es.subscribe(Denormalizers::UpvoteGiven.new, [Events::UpvoteGiven])
end
end
6 changes: 3 additions & 3 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ def matches?(request)
MessageParser.new(
request.request_parameters['text'],
request.request_parameters['trigger_word']
).recipient_name == "!stats"
).first_recipient == "!stats"
end
end

Expand All @@ -13,7 +13,7 @@ def matches?(request)
MessageParser.new(
request.request_parameters['text'],
request.request_parameters['trigger_word']
).recipient_name == "!givers"
).first_recipient == "!givers"
end
end

Expand All @@ -22,7 +22,7 @@ def matches?(request)
MessageParser.new(
request.request_parameters['text'],
request.request_parameters['trigger_word']
).recipient_name.empty?
).first_recipient.empty?
end
end

Expand Down
6 changes: 6 additions & 0 deletions db/migrate/20161205212622_add_uuid_to_upvotes.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
class AddUuidToUpvotes < ActiveRecord::Migration
def change
add_column :upvotes, :uuid, :string
add_index :upvotes, :uuid
end
end
10 changes: 10 additions & 0 deletions db/migrate/20161205215156_create_recipients_upvotes.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
class CreateRecipientsUpvotes < ActiveRecord::Migration
def change
create_table :recipients_upvotes do |t|
t.integer :recipient_id
t.integer :upvote_id
end
add_index :recipients_upvotes, :recipient_id
add_index :recipients_upvotes, :upvote_id
end
end
26 changes: 25 additions & 1 deletion db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,29 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema.define(version: 20160823213239) do
ActiveRecord::Schema.define(version: 20161205215156) do

create_table "event_store_events", force: :cascade do |t|
t.string "stream", null: false
t.string "event_type", null: false
t.string "event_id", null: false
t.text "metadata"
t.text "data", null: false
t.datetime "created_at", null: false
end

add_index "event_store_events", ["created_at"], name: "index_event_store_events_on_created_at"
add_index "event_store_events", ["event_id"], name: "index_event_store_events_on_event_id", unique: true
add_index "event_store_events", ["event_type"], name: "index_event_store_events_on_event_type"
add_index "event_store_events", ["stream"], name: "index_event_store_events_on_stream"

create_table "recipients_upvotes", force: :cascade do |t|
t.integer "recipient_id"
t.integer "upvote_id"
end

add_index "recipients_upvotes", ["recipient_id"], name: "index_recipients_upvotes_on_recipient_id"
add_index "recipients_upvotes", ["upvote_id"], name: "index_recipients_upvotes_on_upvote_id"

create_table "team_members", force: :cascade do |t|
t.integer "team_id", null: false
Expand All @@ -35,9 +57,11 @@
t.integer "recipient_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "uuid"
end

add_index "upvotes", ["recipient_id"], name: "index_upvotes_on_recipient_id"
add_index "upvotes", ["sender_id"], name: "index_upvotes_on_sender_id"
add_index "upvotes", ["uuid"], name: "index_upvotes_on_uuid"

end
16 changes: 16 additions & 0 deletions lib/command/base.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
module Command
class Base
include ActiveModel::Model
include ActiveModel::Validations
include ActiveModel::Conversion

def initialize(attributes={})
super
raise ValidationError, errors unless valid?
end

def persisted?
false
end
end
end
13 changes: 13 additions & 0 deletions lib/command/give_upvote.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module Command
class GiveUpvote < Base
attr_accessor :team_id
attr_accessor :upvote_uuid
attr_accessor :params

validates :team_id, presence: true
validates :upvote_uuid, presence: true
validates :params, presence: true

alias :aggregate_id :upvote_uuid
end
end
11 changes: 11 additions & 0 deletions lib/command_bus_setup.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
require 'arkency/command_bus'
module CommandBusSetup
def command_bus
command_bus ||= Arkency::CommandBus.new.tap do |cb|
register = cb.method(:register)
{
Command::GiveUpvote => CommandHandler::GiveUpvote.new
}.map { |ch| cb.register(*ch) }
end
end
end
22 changes: 22 additions & 0 deletions lib/command_handler/base.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
module CommandHandler
class Base
protected
def with_aggregate(aggregate_id)
aggregate = build(aggregate_id)
yield aggregate
aggregate.store(stream_name)
end

private

def build(aggregate_id)
aggregate_class.new(aggregate_id).tap do |aggregate|
aggregate.load(stream_name)
end
end

def stream_name
'stream'
end
end
end
Loading