Skip to content

Yan Matveichuk Test Task #152

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

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
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
1 change: 1 addition & 0 deletions .browserslistrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
defaults
35 changes: 34 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,37 @@ capybara-*.html
/spec/tmp/*
**.orig
rerun.txt
pickle-email-*.html
pickle-email-*.html
# See https://help.github.com/articles/ignoring-files for more about ignoring files.
#
# If you find yourself ignoring temporary files generated by your text editor
# or operating system, you probably want to add a global ignore instead:
# git config --global core.excludesfile '~/.gitignore_global'

# Ignore bundler config.
/.bundle

# Ignore all logfiles and tempfiles.
/log/*
/tmp/*
!/log/.keep
!/tmp/.keep

# Ignore pidfiles, but keep the directory.
/tmp/pids/*
!/tmp/pids/
!/tmp/pids/.keep


/public/assets
.byebug_history

# Ignore master key for decrypting credentials and more.
/config/master.key

/public/packs
/public/packs-test
/node_modules
/yarn-error.log
yarn-debug.log*
.yarn-integrity
1 change: 1 addition & 0 deletions .rspec
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
--require spec_helper
1 change: 1 addition & 0 deletions .ruby-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ruby-2.7.0
28 changes: 28 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
source 'https://rubygems.org'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }

ruby '2.7.0'

gem 'rails', '~> 6.0.3', '>= 6.0.3.6'
gem 'pg', '>= 0.18', '< 2.0'
gem 'puma', '~> 4.1'
gem 'webpacker', '~> 4.0'
gem 'jbuilder', '~> 2.7'
gem 'bootsnap', '>= 1.4.2', require: false
gem 'draper'

group :development do
gem 'listen', '~> 3.2'
end

group :development, :test do
gem 'byebug'
end

group :test do
gem 'database_cleaner-active_record'
gem 'rspec-rails', '~> 5.0.0'
gem 'capybara'
gem "factory_bot_rails"
gem 'faker'
end
6 changes: 6 additions & 0 deletions Rakefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Add your own tasks in files placed in lib/tasks ending in .rake,
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.

require_relative 'config/application'

Rails.application.load_tasks
2 changes: 2 additions & 0 deletions app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
class ApplicationController < ActionController::Base
end
19 changes: 19 additions & 0 deletions app/controllers/charges_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
class ChargesController < ApplicationController
def index
render :index, locals: { view: ChargesViewObject.new(view_object_context) }
end

private

def view_object_context
{
successful_charges: ChargeDecorator.decorate_collection(charges_repo.all_by_status(:successful)),
failed_charges: ChargeDecorator.decorate_collection(charges_repo.all_by_status(:failed)),
disputed_charges: ChargeDecorator.decorate_collection(charges_repo.all_by_status(:disputed))
}
end

def charges_repo
@charges_repo ||= ChargeRepo.new(model: Charge, customer_model: Customer)
end
end
2 changes: 2 additions & 0 deletions app/decorators/application_decorator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
class ApplicationDecorator < Draper::Decorator
end
25 changes: 25 additions & 0 deletions app/decorators/charge_decorator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
class ChargeDecorator < ApplicationDecorator
def customer_name
helpers.content_tag :p do
"Customer name: #{customer.first_name} #{customer.last_name}"
end
end

def amount
helpers.content_tag :p do
"Amount: #{object.amount}"
end
end

def created
helpers.content_tag :p do
"Charge date: #{object.created.strftime('%a %m/%d/%y%l:%M %p')}"
end
end

private

def customer
@customer ||= object.customer
end
end
16 changes: 16 additions & 0 deletions app/javascript/packs/application.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// This file is automatically compiled by Webpack, along with any other files
// present in this directory. You're encouraged to place your actual application logic in
// a relevant structure within app/javascript and only use these pack files to reference
// that code so it'll be compiled.
import '../stylesheets/charges.css'

require("@rails/ujs").start()



// Uncomment to copy all static images under ../images to the output folder and reference
// them with the image_pack_tag helper in views (e.g <%= image_pack_tag 'rails.png' %>)
// or the `imagePath` JavaScript helper below.
//
// const images = require.context('../images', true)
// const imagePath = (name) => images(name, true)
18 changes: 18 additions & 0 deletions app/javascript/stylesheets/charges.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
.charges {
display: flex;
}
.charges_block {
flex: 1;
}

.charges_block__list_failed {
background-color: #FF0000
}

.charges_block__list_disputed {
background-color: #FF5400
}

.charges_block__title {
text-align: center;
}
3 changes: 3 additions & 0 deletions app/models/application_record.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
end
5 changes: 5 additions & 0 deletions app/models/charge.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class Charge < ApplicationRecord
USD_CURRENCY = 'USD'.freeze

belongs_to :customer
end
Empty file added app/models/concerns/.keep
Empty file.
3 changes: 3 additions & 0 deletions app/models/customer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
class Customer < ApplicationRecord
has_many :charges, dependent: :restrict_with_exception
end
7 changes: 7 additions & 0 deletions app/repos/application_repo.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
class ApplicationRepo
attr_reader :model

def initialize(model:)
@model = model
end
end
41 changes: 41 additions & 0 deletions app/repos/charge_repo.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
class ChargeRepo < ApplicationRepo
CHARGE_STATUSES = {
successful: { paid: true, refunded: false },
failed: { paid: false, refunded: true },
disputed: { paid: false, refunded: false }
}.freeze

attr_reader :customer_model

def initialize(model:, customer_model: nil)
@customer_model = customer_model
super(model: model)
end

def create!(customer_id:, amount:, created:, status:)
record = build_default(customer_id: customer_id, amount: amount, created: created)
record.assign_attributes(CHARGE_STATUSES[status])
record.save!
end

def all_by_status(status)
model.includes(customer_name).where(CHARGE_STATUSES[status]).references(customer_name).all
end

private

def customer_name
@_customer_name ||= customer_model.name.downcase
end

def build_default(customer_id:, amount:, created:)
model.new(
paid: true,
currency: Charge::USD_CURRENCY,
refunded: false,
customer_id: customer_id,
amount: amount,
created: created
)
end
end
7 changes: 7 additions & 0 deletions app/view_objects/application_view_object.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
class ApplicationViewObject < BasicObject
attr_reader :context

def initialize(context)
@context = context
end
end
28 changes: 28 additions & 0 deletions app/view_objects/charges_view_object.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
class ChargesViewObject < ApplicationViewObject
def successful_charges
{
charges: context[:successful_charges],
title: 'Successful Charges',
id: 'successful-charges',
list_class: ''
}
end

def failed_charges
{
charges: context[:failed_charges],
title: 'Failed Charges',
id: 'failed-charges',
list_class: 'charges_block__list_failed'
}
end

def disputed_charges
{
charges: context[:disputed_charges],
title: 'Disputed Charges',
id: 'disputed-charges',
list_class: 'charges_block__list_disputed'
}
end
end
13 changes: 13 additions & 0 deletions app/views/charges/_charges.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<div class="charges_block" id=<%= id %>>
<h3 class="charges_block__title"><%= title %></h3>

<ul>
<% charges.each do |charge| %>
<li class=<%= list_class %>>
<%= charge.customer_name %>
<%= charge.amount %>
<%= charge.created %>
</li>
<% end %>
</ul>
</div>
5 changes: 5 additions & 0 deletions app/views/charges/index.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<div class="charges">
<%= render partial: 'charges', locals: view.failed_charges %>
<%= render partial: 'charges', locals: view.disputed_charges %>
<%= render partial: 'charges', locals: view.successful_charges %>
</div>
14 changes: 14 additions & 0 deletions app/views/layouts/application.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<!DOCTYPE html>
<html>
<head>
<title>Source</title>
<%= csrf_meta_tags %>
<%= csp_meta_tag %>

<%= javascript_pack_tag 'application' %>
</head>

<body>
<%= yield %>
</body>
</html>
72 changes: 72 additions & 0 deletions babel.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
module.exports = function(api) {
var validEnv = ['development', 'test', 'production']
var currentEnv = api.env()
var isDevelopmentEnv = api.env('development')
var isProductionEnv = api.env('production')
var isTestEnv = api.env('test')

if (!validEnv.includes(currentEnv)) {
throw new Error(
'Please specify a valid `NODE_ENV` or ' +
'`BABEL_ENV` environment variables. Valid values are "development", ' +
'"test", and "production". Instead, received: ' +
JSON.stringify(currentEnv) +
'.'
)
}

return {
presets: [
isTestEnv && [
'@babel/preset-env',
{
targets: {
node: 'current'
}
}
],
(isProductionEnv || isDevelopmentEnv) && [
'@babel/preset-env',
{
forceAllTransforms: true,
useBuiltIns: 'entry',
corejs: 3,
modules: false,
exclude: ['transform-typeof-symbol']
}
]
].filter(Boolean),
plugins: [
'babel-plugin-macros',
'@babel/plugin-syntax-dynamic-import',
isTestEnv && 'babel-plugin-dynamic-import-node',
'@babel/plugin-transform-destructuring',
[
'@babel/plugin-proposal-class-properties',
{
loose: true
}
],
[
'@babel/plugin-proposal-object-rest-spread',
{
useBuiltIns: true
}
],
[
'@babel/plugin-transform-runtime',
{
helpers: false,
regenerator: true,
corejs: false
}
],
[
'@babel/plugin-transform-regenerator',
{
async: false
}
]
].filter(Boolean)
}
}
4 changes: 4 additions & 0 deletions bin/rails
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/usr/bin/env ruby
APP_PATH = File.expand_path('../config/application', __dir__)
require_relative '../config/boot'
require 'rails/commands'
4 changes: 4 additions & 0 deletions bin/rake
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/usr/bin/env ruby
require_relative '../config/boot'
require 'rake'
Rake.application.run
Loading