Skip to content

Commit

Permalink
Merge branch 'master' into fix-thread-safety-in-storage
Browse files Browse the repository at this point in the history
  • Loading branch information
morozRed committed Jul 17, 2023
2 parents af465ce + 57dcd8b commit 062958d
Show file tree
Hide file tree
Showing 67 changed files with 70,282 additions and 70 deletions.
30 changes: 28 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:
- uses: actions/cache@v3
with:
path: vendor/bundle
key: ${{ runner.os }}-${{ matrix.ruby }}-${{ matrix.redis-version }}-gems-${{ hashFiles('**/Gemfile.lock') }}
key: ${{ runner.os }}-${{ matrix.ruby }}-${{ matrix.redis-version }}-gems-${{ hashFiles('**/Gemfile.lock') }}-v2
restore-keys: |
${{ runner.os }}-gems-
Expand All @@ -44,5 +44,31 @@ jobs:
- name: Run RSspec
run: bundle exec rspec --format documentation

- name: Run Sorbet Typechecks
run: bundle exec spoom tc
lint:
runs-on: ubuntu-latest
if: "!contains(github.event.head_commit.message, '[skip ci]')"
steps:
- uses: actions/checkout@v3

- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: 3.2

- uses: actions/cache@v3
with:
path: vendor/bundle
key: ${{ runner.os }}-3.2-gems-${{ hashFiles('**/Gemfile.lock') }}-v2
restore-keys: |
${{ runner.os }}-gems-
- name: bundle install
run: |
gem install bundler
bundle config path vendor/bundle
bundle install -j $(getconf _NPROCESSORS_ONLN) --retry 3
- name: Run Rubocop
run: bundle exec rubocop
run: bundle exec rubocop --parallel
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@
/spec/reports/
/tmp/
.ruby-version
.irb_history
1 change: 1 addition & 0 deletions .irbrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
require_relative './lib/money_distributed' # rubocop:disable Packaging/RequireRelativeHardcodingLib
48 changes: 46 additions & 2 deletions .rubocop.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,52 @@
Style/FileName:
inherit_from: .rubocop_todo.yml

inherit_gem:
rubocop-dbl:
- config/dbl.yml

AllCops:
TargetRubyVersion: 3.0
Exclude:
- 'sorbet/**/*'
- 'bin/*'
- 'vendor/**/*'

Layout/LineLength:
Max: 120

Style/IfUnlessModifier:
Enabled: false

Gemspec/RequiredRubyVersion:
Enabled: false

Lint/UnusedMethodArgument:
Enabled: false

Style/AccessModifierDeclarations:
EnforcedStyle: inline

Naming/PredicateName:
Enabled: false

Lint/BooleanSymbol:
Enabled: false

Style/HashSyntax:
Enabled: false

Style/FrozenStringLiteralComment:
Enabled: false

Naming/BlockForwarding:
Enabled: false

Metrics/MethodLength:
Max: 11
CountAsOne: ['array', 'heredoc', 'method_call']

Gemspec/DevelopmentDependencies:
EnforcedStyle: gemspec

# we do not have active support for Time.zone.now
Rails/TimeZone:
Enabled: false
31 changes: 31 additions & 0 deletions .rubocop_todo.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# This configuration was generated by
# `rubocop --auto-gen-config`
# on 2023-07-17 16:35:16 UTC using RuboCop version 1.49.0.
# The point is for the user to remove these configuration records
# one by one as the offenses are removed from the code base.
# Note that changes in the inspected code, or installation of new
# versions of RuboCop, may require this file to be generated again.

# Offense count: 1
# Configuration parameters: CountAsOne.
RSpec/ExampleLength:
Max: 10

# Offense count: 1
RSpec/MultipleExpectations:
Max: 9

# Offense count: 7
# Configuration parameters: EnforcedStyle, IgnoreSharedExamples.
# SupportedStyles: always, named_only
RSpec/NamedSubject:
Exclude:
- 'spec/money/distributed/fetcher/file_spec.rb'
- 'spec/money/distributed/storage_spec.rb'

# Offense count: 2
# Configuration parameters: IgnoreNameless, IgnoreSymbolicNames.
RSpec/VerifiedDoubles:
Exclude:
- 'spec/money/distributed/fetcher/file_spec.rb'
- 'spec/money/distributed/redis_spec.rb'
20 changes: 20 additions & 0 deletions bin/lint
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/usr/bin/env ruby

require 'English'

unless system('bundle exec rubocop --parallel --ignore-unrecognized-cops')
puts $CHILD_STATUS
exit(1)
end

unless system('bundle exec spoom tc')
puts $CHILD_STATUS
exit(1)
end

unless system('bundle exec rspec --format documentation')
puts $CHILD_STATUS
exit(1)
end

puts "\n\n✅ ALL GOOD!"
27 changes: 27 additions & 0 deletions bin/rubocop
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#!/usr/bin/env ruby
# frozen_string_literal: true

#
# This file was generated by Bundler.
#
# The application 'rubocop' is installed as part of a gem, and
# this file is here to facilitate running it.
#

ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)

bundle_binstub = File.expand_path("bundle", __dir__)

if File.file?(bundle_binstub)
if File.read(bundle_binstub, 300).include?("This file was generated by Bundler")
load(bundle_binstub)
else
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
end
end

require "rubygems"
require "bundler/setup"

load Gem.bin_path("rubocop", "rubocop")
27 changes: 27 additions & 0 deletions bin/spoom
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#!/usr/bin/env ruby
# frozen_string_literal: true

#
# This file was generated by Bundler.
#
# The application 'spoom' is installed as part of a gem, and
# this file is here to facilitate running it.
#

ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)

bundle_binstub = File.expand_path("bundle", __dir__)

if File.file?(bundle_binstub)
if File.read(bundle_binstub, 300).include?("This file was generated by Bundler")
load(bundle_binstub)
else
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
end
end

require "rubygems"
require "bundler/setup"

load Gem.bin_path("spoom", "spoom")
27 changes: 27 additions & 0 deletions bin/tapioca
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#!/usr/bin/env ruby
# frozen_string_literal: true

#
# This file was generated by Bundler.
#
# The application 'tapioca' is installed as part of a gem, and
# this file is here to facilitate running it.
#

ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)

bundle_binstub = File.expand_path("bundle", __dir__)

if File.file?(bundle_binstub)
if File.read(bundle_binstub, 300).include?("This file was generated by Bundler")
load(bundle_binstub)
else
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
end
end

require "rubygems"
require "bundler/setup"

load Gem.bin_path("tapioca", "tapioca")
5 changes: 0 additions & 5 deletions lib/money-distributed.rb

This file was deleted.

17 changes: 7 additions & 10 deletions lib/money/distributed/fetcher/base.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# typed: true
# frozen_string_literal: true

require 'money'

class Money
module Distributed
module Fetcher
Expand All @@ -15,24 +14,22 @@ def fetch
rates = exchange_rates
currencies = rates.keys

currencies.each { |cur| add_rate(cur, cur, 1) }
currencies.each { add_rate(_1, _1, 1) }

currencies.combination(2).each do |curr1, curr2|
rate = rates[curr2] / rates[curr1]
add_rate(curr1, curr2, rate)
currencies.combination(2).each do |curr_1, curr_2|
rate = rates[curr_2] / rates[curr_1]
add_rate(curr_1, curr_2, rate)
end
end

private

def add_rate(from_iso, to_iso, rate)
private def add_rate(from_iso, to_iso, rate)
@bank.add_rate(from_iso, to_iso, rate.round(4))
return if from_iso == to_iso

@bank.add_rate(to_iso, from_iso, (1 / rate).round(4))
end

def exchange_rates
private def exchange_rates
raise NotImplementedError
end
end
Expand Down
21 changes: 15 additions & 6 deletions lib/money/distributed/fetcher/file.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# typed: strict
# frozen_string_literal: true

require 'json'
Expand All @@ -9,19 +10,27 @@ module Distributed
module Fetcher
# Fetcher that loads rates from a file
class File
extend T::Sig
include Base

sig do
params(
file_path: T.any(String, Pathname),
bank: T.untyped,
).void
end
def initialize(file_path, bank = nil)
super(bank)
@file_path = file_path
end

private

def exchange_rates
::File.open(@file_path).read.split("\n").each_with_object({}) do |line, h|
code_rate = line.split(' ')
h[code_rate[0]] = BigDecimal(code_rate[1])
sig { returns(T::Hash[String, BigDecimal]) }
private def exchange_rates
::File.read(@file_path).split("\n").each_with_object({}) do |line, h|
code_rate = line.split
currency = T.cast(code_rate[0], String)
rate = T.cast(code_rate[1], T.any(Integer, Float, Rational, BigDecimal, String))
h[currency] = BigDecimal(rate)
end
end
end
Expand Down
22 changes: 17 additions & 5 deletions lib/money/distributed/read_write_lock.rb
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
# typed: strict
# frozen_string_literal: true

require 'concurrent-ruby'

class Money
module Distributed
# ReadWriteLock for `Money::Distributed::Storage` that ensures thread safety
module Cache
module InMemory
class ReadWriteLock
extend T::Sig

sig do
params(
lock: Concurrent::ReentrantReadWriteLock,
_: T.proc.returns(T.untyped),
).returns(T.untyped)
end
def self.read(lock, &_)
lock.acquire_read_lock
begin
Expand All @@ -16,6 +22,12 @@ def self.read(lock, &_)
end
end

sig do
params(
lock: Concurrent::ReentrantReadWriteLock,
_: T.proc.void,
).void
end
def self.write(lock, &_)
lock.acquire_write_lock
begin
Expand Down
Loading

0 comments on commit 062958d

Please sign in to comment.