Skip to content

Commit

Permalink
feat: updates an income
Browse files Browse the repository at this point in the history
  • Loading branch information
marceloboth committed Sep 3, 2023
1 parent 606b15b commit f03aa50
Show file tree
Hide file tree
Showing 14 changed files with 237 additions and 12 deletions.
25 changes: 25 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@ PATH
activejob
aggregate_root (~> 2.6.0)
arkency-command_bus
dry-logic
dry-monads
dry-struct
dry-types
dry-validation
ruby_event_store (~> 2.6.0)
ruby_event_store-transformations

Expand Down Expand Up @@ -122,14 +125,30 @@ GEM
warden (~> 1.2.3)
diff-lcs (1.5.0)
docile (1.4.0)
dry-configurable (1.1.0)
dry-core (~> 1.0, < 2)
zeitwerk (~> 2.6)
dry-core (1.0.0)
concurrent-ruby (~> 1.0)
zeitwerk (~> 2.6)
dry-inflector (1.0.0)
dry-initializer (3.1.1)
dry-logic (1.5.0)
concurrent-ruby (~> 1.0)
dry-core (~> 1.0, < 2)
zeitwerk (~> 2.6)
dry-monads (1.6.0)
concurrent-ruby (~> 1.0)
dry-core (~> 1.0, < 2)
zeitwerk (~> 2.6)
dry-schema (1.13.2)
concurrent-ruby (~> 1.0)
dry-configurable (~> 1.0, >= 1.0.1)
dry-core (~> 1.0, < 2)
dry-initializer (~> 3.0)
dry-logic (>= 1.4, < 2)
dry-types (>= 1.7, < 2)
zeitwerk (~> 2.6)
dry-struct (1.6.0)
dry-core (~> 1.0, < 2)
dry-types (>= 1.7, < 2)
Expand All @@ -141,6 +160,12 @@ GEM
dry-inflector (~> 1.0)
dry-logic (~> 1.4)
zeitwerk (~> 2.6)
dry-validation (1.10.0)
concurrent-ruby (~> 1.0)
dry-core (~> 1.0, < 2)
dry-initializer (~> 3.0)
dry-schema (>= 1.12, < 2)
zeitwerk (~> 2.6)
erubi (1.12.0)
factory_bot (6.2.1)
activesupport (>= 5.0.0)
Expand Down
2 changes: 1 addition & 1 deletion app/components/core/sumarized_table_component.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
<th scope="col" class="py-3.5 pl-3 pr-4 text-right text-sm font-semibold text-gray-900 sm:pr-6 md:pr-0">Value</th>
</tr>
</thead>
<tbody>
<tbody id="<%= id %>">
<% rows.each do |row| %>
<%= row %>
<% end %>
Expand Down
5 changes: 5 additions & 0 deletions app/components/core/sumarized_table_component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,10 @@ class SumarizedTableComponent < BaseViewComponent
renders_many :rows
renders_one :extra_sum
renders_one :total_sum

attr_reader :id
def initialize(id:)
@id = id
end
end
end
17 changes: 11 additions & 6 deletions app/components/incomes/list_component.html.erb
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
<%= render Core::SumarizedTableComponent.new do |t| %>
<%= render Core::SumarizedTableComponent.new(id: 'incomes-records') do |t| %>
<%= t.with_new_record_link do %>
<%= link_to 'New Income', new_income_path, data: { turbo_frame: 'modal' }, class: 'inline-flex items-center justify-center rounded-md border border-transparent bg-indigo-600 px-4 py-2 text-sm font-medium text-white shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 sm:w-auto' %>
<%= link_to 'New Income', new_income_path, data: { turbo_frame: 'modal' }, class: action_button_css %>
<% end %>
<% @incomes.each do |income| %>
<%= t.with_row do %>
<tr class="border-b border-gray-200">
<td class="py-4 pl-4 pr-3 text-sm sm:pl-6 md:pl-0">
<tr class="border-b border-gray-200" id=<%= dom_id(income) %>>
<td class="py-3 pl-4 pr-3 text-sm sm:pl-6 md:pl-0">
<div class="font-medium text-gray-900"><%= income.description %></div>
</td>
<td class="hidden py-4 px-3 text-right text-sm text-gray-500 sm:table-cell"><%= l(income.received_at, format: "%d/%m/%Y") %></td>
<td class="py-4 pl-3 pr-4 text-right text-sm text-gray-500 sm:pr-6 md:pr-0"><%= income.value %></td>
<td class="hidden py-3 px-3 text-right text-sm text-gray-500 sm:table-cell"><%= l(income.received_at, format: "%d/%m/%Y") %></td>
<td class="py-3 pl-3 pr-3 text-right text-sm text-gray-500 sm:pr-6 md:pr-0"><%= income.value %></td>
<td class="py-3 pl-3 pr-3 text-center text-sm">
<%= link_to 'Edit', edit_income_path(income), data: { turbo_frame: 'modal' }, class: action_button_css %>
|
<%= link_to 'Delete', income_path(income), method: :delete, data: { confirm: 'Are you sure?' }, class: danger_action_button_css %>
</td>
</tr>
<% end %>
<% end %>
Expand Down
10 changes: 10 additions & 0 deletions app/components/incomes/list_component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,15 @@ def initialize(incomes:)
def total_amount
@incomes.sum(&:value).to_f
end

def action_button_css
'inline-flex items-center justify-center rounded-md border border-transparent bg-indigo-600 px-4 py-2 ' +
'text-sm font-medium text-white shadow-sm hover:bg-indigo-700 ' +
'focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 sm:w-auto'
end

def danger_action_button_css
'inline-flex w-full justify-center rounded-md bg-red-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-red-500 sm:ml-3 sm:w-auto'
end
end
end
57 changes: 52 additions & 5 deletions app/controllers/incomes_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,65 @@ def index
@incomes = Incomes::Income.where(user_id: current_user.id)
end

def new; end
def new
end

def create
command = Incoming::Commands::CreateIncome.new(
contract = Income::NewContract.new.(
id: SecureRandom.uuid,
description: params[:description],
value: params[:value],
received_at: params[:received_at],
user_id: current_user.id
)

contract.to_monad.fmap do |income|
create_command(income: income.to_h)

redirect_to incomes_path, notice: 'Success'
end.or do |result|
@errors = result.errors.to_h
@income = OpenStruct.new(result.to_h)

render :new, status: :unprocessable_entity
end
end

def edit
@income = Incomes::Income.find(params[:id])
end

def update
contract = Income::UpdateContract.new.(
id: params[:id],
description: params[:description],
value: params[:value],
received_at: Date.parse(params[:received_at].to_s),
received_at: params[:received_at],
user_id: current_user.id
)
command_bus.call(command)

redirect_to incomes_path, notice: 'Success'
contract.to_monad.fmap do |income|
update_command(income: income.to_h)

redirect_to incomes_path, notice: 'Success'
end.or do |result|
pp result.to_h
@errors = result.errors.to_h
@income = OpenStruct.new(result.to_h)

render :edit, status: :unprocessable_entity
end
end

private

def create_command(income: {})
command = Incoming::Commands::CreateIncome.new(income)
command_bus.(command)
end

def update_command(income: {})
command = Incoming::Commands::UpdateIncome.new(income)
command_bus.(command)
end
end
15 changes: 15 additions & 0 deletions app/read_models/incomes/change_income.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# frozen_string_literal: true

module Incomes
class ChangeIncome # < Infra::EventHandler
def call(event)
@income = Income.find_by(id: event.data.fetch(:id))
@income.update(
value: event.data.fetch(:value),
description: event.data.fetch(:description),
received_at: event.data.fetch(:received_at),
user_id: event.data.fetch(:user_id)
)
end
end
end
2 changes: 2 additions & 0 deletions config/initializers/rails_event_store.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
# store.subscribe_to_all_events(lambda { |event| Rails.logger.info(event.event_type) })

store.subscribe(Incomes::RegisterIncome, to: [Incoming::Events::IncomeCreated])
store.subscribe(Incomes::ChangeIncome, to: [Incoming::Events::IncomeUpdated])

store.subscribe_to_all_events(RailsEventStore::LinkByEventType.new)
store.subscribe_to_all_events(RailsEventStore::LinkByCorrelationId.new)
Expand All @@ -30,5 +31,6 @@
# bus.register(PrintInvoice, Invoicing::OnPrint.new)
# bus.register(SubmitOrder, ->(cmd) { Ordering::OnSubmitOrder.new.call(cmd) })
bus.register(Incoming::Commands::CreateIncome, Incoming::Services::OnCreateMoneyIncome.new)
bus.register(Incoming::Commands::UpdateIncome, Incoming::Services::OnUpdateMoneyIncome.new)
end
end
13 changes: 13 additions & 0 deletions incoming/lib/incoming/commands/update_income.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# frozen_string_literal: true

module Incoming
module Commands
class UpdateIncome < Infra::Command
attribute :id, Infra::Types::UUID
attribute :value, Infra::Types::Coercible::Float
attribute :description, Infra::Types::String
attribute :received_at, Infra::Types::Date
attribute :user_id, Infra::Types::ID
end
end
end
13 changes: 13 additions & 0 deletions incoming/lib/incoming/events/income_updated.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# frozen_string_literal: true

module Incoming
module Events
class IncomeUpdated < Infra::Event
attribute :id, Infra::Types::UUID
attribute :value, Infra::Types::Coercible::Float
attribute :description, Infra::Types::String
attribute :received_at, Infra::Types::JSON::Date
attribute :user_id, Infra::Types::ID
end
end
end
8 changes: 8 additions & 0 deletions incoming/lib/incoming/income.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,16 @@ def create(id: , value:, description:, received_at:, user_id:)
apply(Events::IncomeCreated.new(data: { id:, value:, description:, received_at:, user_id: }))
end

def update(id: , value:, description:, received_at:, user_id:)
apply(Events::IncomeUpdated.new(data: { id:, value:, description:, received_at:, user_id: }))
end

on Events::IncomeCreated do |_event|
@state = :created
end

on Events::IncomeUpdated do |_event|
@state = :updated
end
end
end
23 changes: 23 additions & 0 deletions incoming/lib/incoming/services/on_update_money_income.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# frozen_string_literal: true

module Incoming
module Services
class OnUpdateMoneyIncome
def initialize(event_store = Rails.configuration.event_store)
@repository = AggregateRoot::Repository.new(event_store)
end

def call(command)
@repository.with_aggregate(Income.new, "Income$#{SecureRandom.uuid}") do |income|
income.update(
id: command.id,
value: command.value,
description: command.description,
received_at: command.received_at,
user_id: command.user_id
)
end
end
end
end
end
9 changes: 9 additions & 0 deletions spec/factories/incomes/incomes.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
FactoryBot.define do
factory :incomes_income, class: Incomes::Income do
id { SecureRandom.uuid }
description { Faker::Name.name }
value { Faker::Number.decimal(l_digits: 2) }
user_id { Faker::Number.number(digits: 1) }
received_at { Faker::Date.backward(days: 1) }
end
end
50 changes: 50 additions & 0 deletions spec/features/incoming/update_incoming_money_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# frozen_string_literal: true

require 'rails_helper'

describe 'Update incoming money' do
before do
sign_in user
visit root_path
end

let(:user) { create(:user) }
let!(:income) { create(:incomes_income, user_id: user.id) }

it 'by navigating to incomes and filling the form' do
within '#desktop-nav' do
click_link 'Incomes'
end

within "#incomes_income_#{income.id}" do
click_link 'Edit'
end

fill_income('Salary', '20/01/2022', 10_000)
expect_income_content('Salary', '20/01/2022', '10000.0')

fill_income('Freelance', '21/01/2022', 1_000)
expect_income_content('Freelance', '20/01/2022', '1000.0')

expect(page).to have_content 'Total'
expect(page).to have_content '$11000.0'
end

private

def fill_income(description, received_at, value)
fill_in 'Description', with: description
fill_in 'Value', with: value
fill_in 'Received at', with: received_at

click_button 'Save'

expect(page).to have_content 'Success'
end

def expect_income_content(description, received_at, value)
expect(page).to have_content description
expect(page).to have_content received_at
expect(page).to have_content value
end
end

0 comments on commit f03aa50

Please sign in to comment.