Skip to content

Commit

Permalink
Add seed data (#35)
Browse files Browse the repository at this point in the history
  • Loading branch information
ricardopacheco authored Apr 3, 2024
1 parent 8aa4a37 commit ae0d578
Show file tree
Hide file tree
Showing 12 changed files with 283 additions and 18 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
## [Unreleased]

## [0.8.5] - 2024-04-03

### Added

- Seed data for rapid develpment

## [0.8.4] - 2024-03-15

- Adjusting dependencies so that they are automatically loaded by the external Gemfile.
Expand Down
2 changes: 1 addition & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ source "https://rubygems.org"
gemspec

group :development, :test do
gem "faker", "3.2.3"
gem "faker", "3.3.1"
gem "pry", "0.14.2"
gem "rspec", "3.13.0"
gem "standard", "1.35.1"
Expand Down
14 changes: 7 additions & 7 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
auction_fun_core (0.8.4)
auction_fun_core (0.8.5)
activesupport (= 7.1.3.2)
bcrypt (= 3.1.20)
dotenv (= 3.1.0)
Expand All @@ -13,8 +13,8 @@ PATH
idlemailer (= 2.2.0)
money (= 6.19.0)
pg (= 1.5.6)
phonelib (= 0.8.7)
rake (= 13.1.0)
phonelib (= 0.8.8)
rake (= 13.2.0)
rom (= 5.3.0)
rom-sql (= 3.6.2)
sidekiq (= 7.2.2)
Expand Down Expand Up @@ -106,7 +106,7 @@ GEM
dry-initializer (~> 3.0)
dry-schema (>= 1.12, < 2)
zeitwerk (~> 2.6)
faker (3.2.3)
faker (3.3.1)
i18n (>= 1.8.11, < 2)
i18n (1.14.1)
concurrent-ruby (~> 1.0)
Expand Down Expand Up @@ -141,14 +141,14 @@ GEM
ast (~> 2.4.1)
racc
pg (1.5.6)
phonelib (0.8.7)
phonelib (0.8.8)
pry (0.14.2)
coderay (~> 1.1)
method_source (~> 1.0)
racc (1.7.3)
rack (3.0.9.1)
rainbow (3.1.1)
rake (13.1.0)
rake (13.2.0)
redis-client (0.20.0)
connection_pool
regexp_parser (2.9.0)
Expand Down Expand Up @@ -256,7 +256,7 @@ PLATFORMS
DEPENDENCIES
auction_fun_core!
database_cleaner-sequel (= 2.0.2)
faker (= 3.2.3)
faker (= 3.3.1)
pry (= 0.14.2)
rom-factory (= 0.12.0)
rspec (= 3.13.0)
Expand Down
4 changes: 2 additions & 2 deletions auction_fun_core.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ Gem::Specification.new do |spec|
spec.add_dependency "idlemailer", "2.2.0"
spec.add_dependency "money", "6.19.0"
spec.add_dependency "pg", "1.5.6"
spec.add_dependency "phonelib", "0.8.7"
spec.add_dependency "rake", "13.1.0"
spec.add_dependency "phonelib", "0.8.8"
spec.add_dependency "rake", "13.2.0"
spec.add_dependency "rom", "5.3.0"
spec.add_dependency "rom-sql", "3.6.2"
spec.add_dependency "sidekiq", "7.2.2"
Expand Down
115 changes: 114 additions & 1 deletion db/seeds.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,116 @@
# frozen_string_literal: true

# This file should contain all the record creation needed to seed the development database with its default values.
require "pry"
require "faker"

I18n.enforce_available_locales = false
Faker::Config.locale = "pt-BR"

# Constants
STOPWATCH_OPTIONS = [15, 30, 45, 60].freeze

# Start application
AuctionFunCore::Application.start(:core)

# Instantiate repos
auction_repository = AuctionFunCore::Repos::AuctionContext::AuctionRepository.new
staff_repository = AuctionFunCore::Repos::StaffContext::StaffRepository.new

# Create root staff. Create as a regular user using the normal flow and after that
# just change the type directly in the db.

root_staff_attributes = {
kind: "root", name: "Root Bot", email: "[email protected]",
phone: Faker::PhoneNumber.unique.cell_phone_in_e164,
password: "password", password_confirmation: "password"
}

AuctionFunCore::Operations::StaffContext::RegistrationOperation.call(root_staff_attributes) do |result|
result.failure { |failure| raise "Error to create root staff: #{failure}" }
result.success { |root| @root = root }
end
staff_repository.update(@root.id, kind: "root")

# Add common staff

common_staff_attributes = {
name: "Staff Bot", email: "[email protected]",
phone: Faker::PhoneNumber.unique.cell_phone_in_e164,
password: "password", password_confirmation: "password"
}

AuctionFunCore::Operations::StaffContext::RegistrationOperation.call(common_staff_attributes) do |result|
result.failure { |failure| raise "Error to create common staff: #{failure}" }
result.success { |staff| @staff = staff }
end

# Create some standard auctions
(1..15).each do |i|
attributes = {
staff_id: @staff.id, title: Faker::Commerce.product_name, description: Faker::Lorem.paragraph_by_chars,
kind: "standard", started_at: i.hour.from_now, finished_at: i.day.from_now,
initial_bid_cents: (i * 100), minimal_bid_cents: (i * 100)
}
AuctionFunCore::Operations::AuctionContext::CreateOperation.call(attributes) do |result|
result.failure { |failure| raise "Error to create standard auction: #{failure}" }
result.success { |auction| puts "Create standard auction with: #{auction.to_h}" }
end
end

# Create some penny auctions
(1..10).each do |i|
stopwatch = STOPWATCH_OPTIONS.sample
started_at = i.hour.from_now
finished_at = started_at + stopwatch.seconds

attributes = {
staff_id: @staff.id, title: Faker::Commerce.product_name, description: Faker::Lorem.paragraph_by_chars,
kind: "penny", started_at: started_at, finished_at: finished_at, stopwatch: stopwatch
}
AuctionFunCore::Operations::AuctionContext::CreateOperation.call(attributes) do |result|
result.failure { |failure| raise "Error to create penny auction: #{failure}" }
result.success { |auction| puts "Create penny auction with: #{auction.to_h}" }
end
end

# Create some closed auctions
(1..3).each do |i|
attributes = {
staff_id: @staff.id, title: Faker::Commerce.product_name, kind: "closed",
started_at: i.hour.from_now, finished_at: i.day.from_now.end_of_day,
initial_bid_cents: (i * 1000)
}
AuctionFunCore::Operations::AuctionContext::CreateOperation.call(attributes) do |result|
result.failure { |failure| raise "Error to create closed auction: #{failure}" }
result.success { |auction| puts "Create closed auction with: #{auction.to_h}" }
end
end

# Add some users
100.times do
attributes = {
name: Faker::Name.name, email: Faker::Internet.unique.email,
phone: Faker::PhoneNumber.unique.cell_phone_in_e164, password: "password",
password_confirmation: "password"
}
AuctionFunCore::Operations::UserContext::RegistrationOperation.call(attributes) do |result|
result.failure { |failure| raise "Error to create user: #{failure}" }
result.success { |user| puts "Create user with: #{user.to_h}" }
end
end

# Create some bids
auction_repository.all.each do |auction|
next if auction.id.even?

bid_params = {
auction_id: auction.id,
user_id: rand(2..100),
value_cents: auction.minimal_bid_cents + (auction.minimal_bid_cents * 0.10)
}

"AuctionFunCore::Operations::BidContext::CreateBid#{auction.kind.capitalize}Operation".constantize.call(bid_params) do |result|
result.failure { |failure| raise "Error to create bid: #{failure}" }
result.success { |bid| puts "Create bid with: #{bid.to_h}" }
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ def self.call(auction_id, &block)
Dry::Matcher::ResultMatcher.call(operation, &block)
end

# @todo Add more actions
# Generate auction statistics and save in JSONB (statistics field)
# Send email to winner with payment info.
# Send email for bidders with user bid stats
# It only performs the basic processing of completing an auction.
# It just changes the status at the database level and triggers the finished event.
# @param auction_id [Integer] Auction ID
# @return [Dry::Monads::Result::Success, Dry::Monads::Result::Failure]
def call(auction_id)
yield validate(auction_id: auction_id)

Expand Down
76 changes: 76 additions & 0 deletions lib/auction_fun_core/relations/auctions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,82 @@ class Auctions < ROM::Relation[:sql]

struct_namespace Entities
auto_struct(true)

def all(page = 1, per_page = 10, options = {bidders_count: 3})
offset = ((page - 1) * per_page)

read(all_auctions_with_bid_info(per_page, offset, options))
end

def info(auction_id, options = {bidders_count: 3})
raise "Invalid argument" unless auction_id.is_a?(Integer)

read(auction_with_bid_info(auction_id, options))
end

private

def auction_with_bid_info(auction_id, options = {bidders_count: 3})
"SELECT a.id, a.title, a.description, a.kind, a.status, a.started_at, a.finished_at, a.stopwatch, a.initial_bid_cents,
(SELECT COUNT(*) FROM (SELECT * FROM bids WHERE bids.auction_id = #{auction_id}) dt) AS total_bids,
CASE
WHEN a.kind = 'standard' THEN
json_build_object(
'current', a.minimal_bid_cents,
'minimal', a.minimal_bid_cents,
'bidders', COALESCE(
json_agg(json_build_object('id', bi.id, 'user_id', users.id, 'name', users.name, 'value', bi.value_cents, 'date', bi.created_at) ORDER BY value_cents DESC)
FILTER (where bi.id IS NOT NULL AND users.id IS NOT NULL), '[]'::json
)
)
WHEN a.kind = 'penny' THEN
json_build_object(
'value', a.initial_bid_cents,
'bidders', COALESCE(
json_agg(json_build_object('id', bi.id, 'user_id', users.id, 'name', users.name, 'value', bi.value_cents, 'date', bi.created_at) ORDER BY value_cents DESC)
FILTER (where bi.id IS NOT NULL AND users.id IS NOT NULL), '[]'::json
)
)
WHEN a.kind = 'closed' THEN
json_build_object('minimal', (a.initial_bid_cents + (a.initial_bid_cents * 0.10))::int)
END as bids
FROM auctions as a
LEFT JOIN LATERAL (SELECT * FROM bids WHERE auction_id = a.id ORDER BY value_cents DESC LIMIT #{options[:bidders_count]}) as bi ON a.id = bi.auction_id AND a.id = #{auction_id}
LEFT JOIN users ON bi.user_id = users.id AND bi.auction_id = a.id
WHERE a.id = #{auction_id}
GROUP BY a.id"
end

def all_auctions_with_bid_info(per_page, offset, options = {bidders_count: 3})
"SELECT a.id, a.title, a.description, a.kind, a.status, a.started_at, a.finished_at, a.stopwatch, a.initial_bid_cents,
(SELECT COUNT(*) FROM (SELECT * FROM bids WHERE bids.auction_id = a.id) dt) AS total_bids,
CASE
WHEN a.kind = 'standard' THEN
json_build_object(
'current', a.minimal_bid_cents,
'minimal', a.minimal_bid_cents,
'bidders', COALESCE(
json_agg(json_build_object('id', bi.id, 'user_id', users.id, 'name', users.name, 'value', bi.value_cents, 'date', bi.created_at) ORDER BY value_cents DESC)
FILTER (where bi.id IS NOT NULL AND users.id IS NOT NULL), '[]'::json
)
)
WHEN a.kind = 'penny' THEN
json_build_object(
'value', a.initial_bid_cents,
'bidders', COALESCE(
json_agg(json_build_object('id', bi.id, 'user_id', users.id, 'name', users.name, 'value', bi.value_cents, 'date', bi.created_at) ORDER BY value_cents DESC)
FILTER (where bi.id IS NOT NULL AND users.id IS NOT NULL), '[]'::json
)
)
WHEN a.kind = 'closed' THEN
json_build_object('minimal', (a.initial_bid_cents + (a.initial_bid_cents * 0.10))::int)
END as bids
FROM auctions as a
LEFT JOIN LATERAL (SELECT * FROM bids WHERE auction_id = a.id ORDER BY value_cents DESC LIMIT #{options[:bidders_count]}) as bi ON a.id = bi.auction_id
LEFT JOIN users ON bi.user_id = users.id AND bi.auction_id = a.id
GROUP BY a.id
LIMIT #{per_page} OFFSET #{offset}"
end
end
end
end
2 changes: 1 addition & 1 deletion lib/auction_fun_core/relations/bids.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
module AuctionFunCore
module Relations
# SQL relation for bids
# @see https://rom-rb.org/5.2/learn/sql/relations/
# @see https://rom-rb.org/5.0/learn/sql/relations/
class Bids < ROM::Relation[:sql]
use :pagination, per_page: 3

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ class AuctionRepository < ROM::Repository[:auctions]
struct_namespace Entities
commands :create, update: :by_pk, delete: :by_pk

# Returns all auctions in the database.
# @return [Array<ROM::Struct::Auction>, []]
def all
auctions.to_a
end

# Returns the total number of auctions in database.
# @return [Integer]
def count
Expand Down
2 changes: 1 addition & 1 deletion lib/auction_fun_core/version.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# frozen_string_literal: true

module AuctionFunCore
VERSION = "0.8.4"
VERSION = "0.8.5"

# Required class module is a gem dependency
class Version; end
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# frozen_string_literal: true

require "spec_helper"

RSpec.describe AuctionFunCore::Repos::AuctionContext::AuctionRepository, type: :repo do
subject(:repo) { described_class.new }

describe "#all" do
let!(:auction) { Factory[:auction, :default_standard] }

it "expect return all auctions" do
expect(repo.all.size).to eq(1)
expect(repo.all.first.id).to eq(auction.id)
end
end

describe "#count" do
context "when has not auction on repository" do
it "expect return zero" do
expect(repo.count).to be_zero
end
end

context "when has auctions on repository" do
let!(:auction) { Factory[:auction, :default_standard] }

it "expect return total" do
expect(repo.count).to eq(1)
end
end
end

describe "#by_id(id)" do
context "when id is founded on repository" do
let!(:auction) { Factory[:auction, :default_standard] }

it "expect return rom object" do
expect(repo.by_id(auction.id)).to be_a(AuctionFunCore::Entities::Auction)
end
end

context "when id is not found on repository" do
it "expect return nil" do
expect(repo.by_id(nil)).to be_nil
end
end
end

describe "#by_id!(id)" do
context "when id is founded on repository" do
let!(:auction) { Factory[:auction, :default_standard] }

it "expect return rom object" do
expect(repo.by_id(auction.id)).to be_a(AuctionFunCore::Entities::Auction)
end
end

context "when id is not found on repository" do
it "expect raise exception" do
expect { repo.by_id!(nil) }.to raise_error(ROM::TupleCountMismatchError)
end
end
end
end
Loading

0 comments on commit ae0d578

Please sign in to comment.