Skip to content

Move give time action to transfers controller #264

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 26 commits into from
Jan 26, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
72b7bad
Move #give_time from users to transfer controller
sauloperez Jul 28, 2017
9a1d6b0
Move #give_time from orgs to transfers controller
sauloperez Aug 16, 2017
808ed91
DRY #new by abstracting ivars into their methods
sauloperez Aug 16, 2017
69982ab
Autofix cops and change style of spec
sauloperez Aug 16, 2017
0aa4073
Simplify #create action
sauloperez Aug 16, 2017
0fa4668
Remove dead #give_time actions
sauloperez Aug 16, 2017
fa3adf4
Abstract transfers controller by passing account
sauloperez Aug 16, 2017
24e5d6a
Use @accountable instead of @user or @organization
sauloperez Aug 16, 2017
96f36b1
Refactor assigns' specs to check HTML instead
sauloperez Aug 17, 2017
b50d662
Replace ivars with locals in views
sauloperez Aug 18, 2017
bf6223b
Extract transfer sources select
sauloperez Aug 19, 2017
a624dbc
Remove differences between new, new_organization
sauloperez Aug 19, 2017
fc902af
Use transfers :new template only
sauloperez Aug 19, 2017
14eaad8
Extract option building into class + polymorphism
sauloperez Aug 24, 2017
be04d97
Extract TransferFactory and decouple view
sauloperez Aug 24, 2017
e97d467
Replace conditional with #find_by_id
sauloperez Sep 2, 2017
5f4d70e
Add specs and docs to TransferSourceOptions
sauloperez Jan 4, 2018
7b51848
Inline AccountOptionTag to avoid wrong abstraction
sauloperez Jan 4, 2018
95230dc
Remove obsolete :give_time action views
sauloperez Jan 4, 2018
b319ed9
Fix doc
sauloperez Jan 4, 2018
b13b38e
Add test and doc to #display_id
sauloperez Jan 5, 2018
b71dfb7
Add specs to TransferFactory
sauloperez Jan 5, 2018
47812a9
Replace give_time link from users#show page
sauloperez Jan 18, 2018
e40a0ff
Replace give_time link from organizations#show
sauloperez Jan 22, 2018
545ea44
Remove intermediate vars
sauloperez Jan 22, 2018
70efd91
Fix some rubocop violations
sauloperez Jan 22, 2018
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 .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ SpecialGlobalVars:
Enabled: false

StringLiterals:
EnforcedStyle: double_quotes
EnforcedStyle: single_quotes

VariableInterpolation:
Enabled: false
Expand Down
24 changes: 0 additions & 24 deletions app/controllers/organizations_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,6 @@ def destroy
redirect_to organizations_path, notice: "deleted"
end

def give_time
@destination = @organization.account.id
@source = find_transfer_source
@offer = find_transfer_offer
@transfer = Transfer.new(source: @source, destination: @destination)
@sources = find_transfer_sources_for_admin
end

def set_current
if current_user
session[:current_organization_id] = @organization.id
Expand All @@ -70,20 +62,4 @@ def organization_params
public_opening_times description address
neighborhood city domain])
end

def find_transfer_offer
current_organization.offers.
find(params[:offer]) if params[:offer].present?
end

def find_transfer_source
current_user.members.
find_by(organization: @organization).account.id
end

def find_transfer_sources_for_admin
return unless admin?
[current_organization.account] +
current_organization.member_accounts.where("members.active is true")
end
end
27 changes: 22 additions & 5 deletions app/controllers/transfers_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,29 @@ def create
transfer_params.merge(source: @source, destination: @account)
)

begin
transfer.save!
rescue ActiveRecord::RecordInvalid
flash[:error] = transfer.errors.full_messages.to_sentence
end
transfer.save!
redirect_to redirect_target
rescue ActiveRecord::RecordInvalid
flash[:error] = transfer.errors.full_messages.to_sentence
end

def new
transfer_factory = TransferFactory.new(
current_organization,
current_user,
params[:offer],
params[:destination_account_id]
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The term destination_account would be better off as payee. Seems more appropriate to this domain.

)

render(
:new,
locals: {
accountable: transfer_factory.accountable,
transfer: transfer_factory.build_transfer,
offer: transfer_factory.offer,
sources: transfer_factory.transfer_sources
}
)
end

def delete_reason
Expand Down
28 changes: 0 additions & 28 deletions app/controllers/users_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -61,18 +61,6 @@ def update
end
end

def give_time
@user = scoped_users.find(params[:id])
@destination = @user.members.
find_by(organization: current_organization).account.id
@source = find_transfer_source
@offer = find_transfer_offer
@transfer = Transfer.new(source: @source,
destination: @destination,
post: @offer)
@sources = find_transfer_sources_for_admin
end

private

def user_params
Expand All @@ -85,22 +73,6 @@ def user_params
params.require(:user).permit *fields_to_permit
end

def find_transfer_offer
current_organization.offers.
find(params[:offer]) if params[:offer].present?
end

def find_transfer_source
current_user.members.
find_by(organization: current_organization).account.id
end

def find_transfer_sources_for_admin
return unless admin?
[current_organization.account] +
current_organization.member_accounts.where("members.active is true")
end

def find_user
if current_user.id == params[:id].to_i
current_user
Expand Down
7 changes: 7 additions & 0 deletions app/helpers/transfers_helper.rb
Original file line number Diff line number Diff line change
@@ -1,2 +1,9 @@
module TransfersHelper
def accountable_path(accountable)
if accountable.is_a?(Organization)
organization_path(accountable)
else
user_path(accountable.user)
end
end
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there any reason why this can't be an AccountableMixin extending Organization and User models? Could we share an interface to expose the path?

Copy link
Collaborator Author

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'd just do whatever it takes to follow Rails convention so that we don't have to define any path helper. But because we made this standout, we can work on it in an upcoming iteration, not here.

end
9 changes: 9 additions & 0 deletions app/models/member.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,15 @@ def display_name_with_uid
"#{user} (#{member_uid})"
end

# Returns the id to be displayed in the :new transfer page
#
# @params _destination_accountable used to keep the same API as
# Organization#display_id
# @return [Integer]
def display_id(_destination_accountable)
member_uid
end

def remove_all_posts_from_index
Post.with_member.where("members.id = ?", self.id).find_each do |post|
post.delete_document
Expand Down
17 changes: 17 additions & 0 deletions app/models/organization.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,23 @@ def to_s
"#{name}"
end

def display_name_with_uid
self
end

# Returns the id to be displayed in the :new transfer page with the given
# destination_accountable
#
# @params destination_accountable [Organization | Object] target of a transfer
# @return [Integer | String]
def display_id(destination_accountable)
if destination_accountable.is_a?(Organization)
account.accountable_id
else
''
end
end

def ensure_reg_number_seq!
update_column(:reg_number_seq, members.maximum(:member_uid))
end
Expand Down
80 changes: 80 additions & 0 deletions app/models/transfer_factory.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
class TransferFactory
Copy link
Collaborator Author

@sauloperez sauloperez Jan 4, 2018

Choose a reason for hiding this comment

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

This is the first try at encapsulating all this transfer building logic. It's more of a patch to let us move code around until we figure out how the data model really works.

def initialize(current_organization, current_user, offer_id, destination_account_id)
@current_organization = current_organization
@current_user = current_user
@offer_id = offer_id
@destination_account_id = destination_account_id
end

# Returns the offer that is the subject of the transfer
#
# @return [Maybe<Offer>]
def offer
current_organization.offers.find_by_id(offer_id)
end

# Returns a new instance of Transfer with the data provided
#
# @return [Transfer]
def build_transfer
transfer = Transfer.new(source: source, destination: destination_account.id)
transfer.post = offer unless for_organization?
transfer
end

def transfer_sources
if admin?
[current_organization.account] +
current_organization.member_accounts.merge(Member.active)
else
[]
end
end

def accountable
@accountable ||= destination_account.accountable
end

private

attr_reader :current_organization, :current_user, :offer_id,
:destination_account_id

# Returns the id of the account that acts as source of the transfer.
# Either the account of the organization or the account of the current user.
#
# @return [Maybe<Integer>]
def source
organization = if accountable.is_a?(Organization)
accountable
else
current_organization
end

current_user.members.find_by(organization: organization).account.id
end

# Checks whether the destination account is an organization
#
# @return [Boolean]
def for_organization?
destination_account.accountable.class == Organization
end

def admin?
current_user.try :manages?, current_organization
end

# TODO: this method implements authorization by scoping the destination
# account in all the accounts of the current organization. If the specified
# destination account does not belong to it, the request will simply faily.
#
# Returns the account the time will be transfered to
#
# @return [Account]
def destination_account
@destination_account ||= current_organization
.all_accounts
.find(destination_account_id)
end
end
43 changes: 43 additions & 0 deletions app/models/transfer_sources_options.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Wraps the passed sources as a collection of HTML <option>'s value and text
# pairs as expected by Rails' #options_for_select.
class TransferSourcesOptions
Copy link
Contributor

Choose a reason for hiding this comment

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

Doc please 😬

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

true

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

done

# Constructor
#
# @param sources [Array<Account>]
# @param destination_accountable [Member | Organization]
def initialize(sources, destination_accountable)
@sources = sources
@destination_accountable = destination_accountable
end

# Returns the collection as an Array containing pairs of <option>'s text and
# value sorted by accountable type and member_uid
#
# @return [Array<String, Integer>]
def to_a
sources
.sort_by { |account| to_accountable_type_and_uid(account) }
.map { |account| to_text_and_value(account) }
end

private

attr_reader :sources, :destination_accountable

def to_accountable_type_and_uid(account)
[account.accountable_type, account.accountable.try(:member_uid)]
end

def to_text_and_value(account)
accountable = account.accountable

[
"#{display_id(accountable)} #{accountable.class} #{accountable}",
account.id
]
end

def display_id(accountable)
accountable.display_id(destination_accountable)
end
end
2 changes: 1 addition & 1 deletion app/views/offers/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<% end %>
<% end %>
<% if current_user and @offer.user != current_user %>
<%= link_to give_time_user_path(@offer.user, offer: @offer.id),
<%= link_to new_transfer_path(id: @offer.user.id, offer: @offer.id),
class: "btn btn-success" do %>
<%= glyph :time %>
<%= t ".give_time_for" %>
Expand Down
63 changes: 0 additions & 63 deletions app/views/organizations/give_time.html.erb

This file was deleted.

2 changes: 1 addition & 1 deletion app/views/organizations/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@
</li>
<% end %>
<li>
<%= link_to give_time_organization_path(@organization) do %>
<%= link_to new_transfer_path(id: @organization, destination_account_id: @organization.account.id) do %>
<%= glyph :time %>
<%= t "global.give_time" %>
<% end %>
Expand Down
6 changes: 6 additions & 0 deletions app/views/transfers/_member_offer.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<% if offer %>
<%= form.input :post, readonly: true %>
<%= form.input :post_id, as: :hidden %>
<% else %>
<%= form.input :post_id, collection: accountable.offers.active %>
<% end %>
3 changes: 3 additions & 0 deletions app/views/transfers/_organization_offer.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<% if offer %>
<%= form.input :post, as: :hidden %>
<% end %>
9 changes: 9 additions & 0 deletions app/views/transfers/_sources.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<div class="form-group">
<%= form.label :source %>
<%= form.select :source,
options_for_select(
TransferSourcesOptions.new(sources, accountable).to_a,
selected: current_user.member(current_organization).account.id
), {}, id: "select2-time", class: "form-control"
%>
</div>
Loading