Psst.. new poll here.
Psst.. new forums here.
Microsoft is blocking us again (TY IP Reputation!) so just use oauth login instead. :)
Paste
Pasted as Ruby by Anonymous ( 16 years ago )
require 'rubygems'
gem 'facets'
require 'facets'
require 'fileutils'
include FileUtils
files = Hash.new { |hash, filename| hash[filename] = "" }
File.read(__FILE__).newlines.inject(nil) do |filename, line|
if line =~ /^# ---- File\: .*? ----$/ then filename = line[/^# ---- File\: (.*?) ----$/, 1]
else files[filename] << "#{line}\n"; end
filename
end
files.each do |filename, content|
next if filename.nil?
mkdir_p File.dirname(filename)
File.open(filename, "w") { |io| io.print content }
end
exit
# ---- File: comments.txt ----
<<TEXT
Ну что, динамические петушки успешно соснули. Даже с такой проверкой типов, как
сейчас, по эксепшенам хуй проссышь, где конкретно упало. Тесты приходится
проходить по полчаса. Представляю, что было бы, если б я вообще типы не
проверял. И даже думать страшно, что было бы, если бы рубиновый stdlib не
содержал бы в себе проверок типов или (что то же самое) был бы
слаботипизированным. Смотрите "TODO" c пометкой "[!]".
Вывод: Ruby для энтерпрайза если и подходит, то только обвешенный тоннами
статикоблядства.
TEXT
# ---- File: issues.rb ----
require 'yaml'
YAML.load <<YAML
# По умолчанию тип равен "Задача".
-
Приоритет: -1
Описание: |
Выполнить все задачи, помеченные в исходнике меткой "XXX".
-
Приоритет: 0
Название: |
Выполнить все задачи, помеченные в исходнике меткой "TODO".
-
Приоритет: 0
Тип: Улучшение
Описание: |
Может, сделать автоматический перевод валют в бинарных операциях?
YAML
# ---- File: lib/deps.rb ----
# Dependencies of the application are described here.
require 'rubygems'
gem 'facets'
# ---- File: lib/every.rb ----
# This file contains what every module requires.
require 'deps'
require 'mathn'
# ---- File: lib/currency.rb ----
require 'every'
require 'facets'
require 'facets/equitable'
require 'csv'
class Currency
# XXX: Check for duplicate entries in currencies collection.
# TODO: Redesign. "to_sign" really disturbs!
@@currencies = []
# +sign+ - value for #sign.
#
# +to_sign+ - value for #to_sign.
#
# +rates+ - Hash of +to_sign+ to coefficient. Example for euro:
# { "toDollar" => 0.75 }
#
def initialize(sign, to_sign, rates)
#
@sign = sign
@to_sign = to_sign
@rates = rates
#
@@currencies << self
end
# Sign denoting this Currency. For example, "$" or "eur"
attr_reader :sign
# Name of function used to convert from other Currency to this one.
# For example, "toDollar", "toEuro".
attr_reader :to_sign
# Coefficient of conversion from this Currency to Currency denoted by
# specified +to_sign+.
def rate(currency_or_currency_to_sign)
to_sign =
case currency = to_sign = currency_or_currency_to_sign
when String then to_sign
when Currency then currency.to_sign
else raise TypeError, currency_or_currency_to_sign.inspect
end
return 1 if to_sign == self.to_sign
@rates[to_sign] or raise RateNotDefined.new(self, Currency.with_to_sign(to_sign))
end
# TODO: [!] Type check. Everywhere. And with standard style for overloaded
# methods.
def ==(other)
raise TypeError, other.inspect unless other.is_a? Currency
return self.sign == other.sign
end
def to_s
"#{sign}"
end
# Returns Currency with specified #sign.
def self.with_sign(sign)
@@currencies.find { |c| c.sign == sign } or raise(NoSuchCurrency.new(:sign, sign))
end
def self.by_sign(sign); with_sign(sign); end
def self.[](sign); with_sign(sign); end
# Returns Currency with specified #to_sign.
def self.with_to_sign(to_sign)
@@currencies.find { |c| c.to_sign == to_sign } or raise(NoSuchCurrency.new(:to_sign, to_sign))
end
def self.by_to_sign(to_sign); with_to_sign(to_sign); end
def self.load(currencies_description)
#
@@currencies.clear
# Parse the description!
begin
# Normalize the description.
currencies_description = currencies_description.gsub(" ", "")
# Parse as CSV.
currencies_description =
begin
CSV.parse(currencies_description)
rescue CSV::IllegalFormatError => e
raise CurrenciesDescriptionSyntaxError.new("???", e.message)
end
# Check if the table is formed well.
raise CurrenciesDescriptionSyntaxError.new(0, "Currencies description must not be empty") if currencies_description.empty?
#
to_signs = currencies_description[0][1..-1]
# Parse currencies.
currencies_description[1..-1].map_with_index do |currency_row, row_no|
line_no = row_no + 1
Currency.new(
#
sign = currency_row[0],
#
current_currency_to_sign = to_signs[row_no],
# Parse rates.
# XXX: This may be done simpler.
to_signs.zip(currency_row[1..-1]).
# Reject not specified rates.
reject { |to_sign, rate_str| rate_str == nil or rate_str.blank? or rate_str == "-" }.
# Parse the rates!
map do |to_sign, rate_str|
rate = rate_str.to_f
raise CurrenciesDescriptionSyntaxError.new(line_no, %Q{Invalid conversion rate for "#{sign}" and "#{to_sign}": #{rate_str}}) if rate <= 0
raise CurrenciesDescriptionSyntaxError.new(line_no, %Q{Conversion rate from "#{sign}" to itself (with "#{to_sign}") must be equal to 1 but it is #{rate}}) if current_currency_to_sign == to_sign and rate != 1
[to_sign, rate]
end.
#
to_h
)
end
end
end
def self.delete_all
@@currencies.clear
end
class CurrenciesDescriptionSyntaxError < Exception
def initialize(line_no, error_description)
@error_description = error_description
@line_no = line_no
end
attr_reader :line_no
attr_reader :error_description
def to_s
"Error in currencies description at line #{line_no}: #{error_description}"
end
end
class RateNotDefined < Exception
def initialize(from_currency, to_currency)
super("Rate of #{from_currency} to #{to_currency} is not defined")
@from_currency = from_currency
@to_currency = to_currency
end
attr_reader :from_currency
attr_reader :to_currency
end
class NoSuchCurrency < Exception
def initialize(property, value)
case property
when :sign then super(%Q{No currency signed as "#{value}"})
when :to_sign then super(%Q{No currency with conversion function "#{value}"})
else raise %Q{Property "#{property}" is not recognized}
end
@property = property
@value = value
end
# By which property the Currency was tried to be found.
attr_reader :property
# Value of the property.
attr_reader :value
end
end
# ---- File: test/currency_test.rb ----
require 'test/unit'
require 'currency'
class CurrencyTest < Test::Unit::TestCase
def test_finding_currency
a = Currency.new("a", "toA", {})
assert_equal a, Currency.with_sign("a")
assert_equal a, Currency.with_to_sign("toA")
assert_raise Currency::NoSuchCurrency do Currency.with_sign "toA"; end
assert_raise Currency::NoSuchCurrency do Currency.with_to_sign "a"; end
end
def test_rate_to_self
assert Currency.new("a", "toA", {}).rate("toA") == 1
end
def test_rate
a = Currency.new("a", "toA", { "toB" => 10 })
b = Currency.new("b", "toB", {})
assert a.rate("toB") == 10
assert_raise Currency::RateNotDefined do b.rate("toA"); end
assert_raise Currency::NoSuchCurrency do b.rate("toC"); end
end
def test_load
assert_nothing_raised do
Currency.load <<-CURRENCY_DESCRIPTION
"", "toDollar", "toEuro", "toFrank"
"$", "1", "0.75", ""
"eur", "1.25", "", ""
"fr", "5", "10", "1"
CURRENCY_DESCRIPTION
end
assert Currency.with_sign("$").rate("toEuro") == 0.75
assert_raise Currency::RateNotDefined do Currency.with_sign("eur").rate("toFrank"); end
end
def test_load_error_rate_sintactically_invalid
begin
Currency.load <<-X
"", "toDollar", "toEuro"
"$", "1", "abc"
"eur", "1.25", ""
X
assert false
rescue Currency::CurrenciesDescriptionSyntaxError => e
assert e.line_no == 1
end
end
def test_load_error_rate_is_not_1
begin
Currency.load <<-X
"", "toDollar", "toEuro"
"$", "5", "0.75"
"eur", "1.25", ""
X
assert false
rescue Currency::CurrenciesDescriptionSyntaxError => e
assert e.line_no == 1
end
end
def teardown
Currency.delete_all
end
end
# ---- File: money_sum.rb ----
require 'every'
require 'currency'
require 'facets'
require 'facets/equitable'
# Not inheritable.
class MoneySum
# :call_seq:
# new(sum, currency)
# new(sum, currency_sign)
def initialize(sum, currency_or_sign)
#
raise TypeError if self.class != MoneySum
currency = to_currency(currency_or_sign)
raise TypeError, currency.inspect unless currency.is_a? Currency
#
@sum = sum
@currency = currency
end
# :call_seq:
# new(sum, currency)
# new(sum, currency_sign)
def to(currency_or_sign)
currency = to_currency(currency_or_sign)
MoneySum.new(self.sum * self.currency.rate(currency), currency)
end
def +(other)
raise TypeError, other.inspect unless other.is_a? MoneySum
raise WrongCurrency.new(other, self.currency) unless other.currency == self.currency
return MoneySum.new(self.sum + other.sum, currency)
end
def -@
MoneySum.new(-sum, currency)
end
def -(other)
raise TypeError, other.inspect unless other.is_a? MoneySum
self + (-other)
end
def ==(other)
raise TypeError, other.inspect unless other.is_a? MoneySum
raise WrongCurrency.new(other, self.currency) unless other.currency == self.currency
return self.sum == other.sum
end
class WrongCurrency < Exception
def initialize(money_sum, required_currency)
super(%Q{#{required_currency} is required but #{money_sum} is got})
@money_sum = money_sum
@required_currency = required_currency
end
attr_reader :money_sum
attr_reader :required_currency
end
protected
attr_reader :currency
attr_reader :sum
private
def to_currency(currency_or_currency_sign)
currency = sign = currency_or_currency_sign
return Currency[sign] if currency_or_currency_sign.is_a? String
return currency
end
end
# ---- File: test/money_sum_test.rb ----
require 'test/unit'
require 'money_sum'
class MoneySumTest < Test::Unit::TestCase
def setup
Currency.load <<-X
"", "toDollar", "toEuro", "toFrank"
"$", "1", "0.75", ""
"eur", "1.25", "", ""
"fr", "5", "10", "1"
X
end
def test_plus
assert MoneySum.new(1, "$") + MoneySum.new(2, "$") == MoneySum.new(3, "$")
assert MoneySum.new(2, "$") - MoneySum.new(1, "$") == MoneySum.new(1, "$")
end
def test_equal
assert MoneySum.new(1, "eur") == MoneySum.new(1, "eur")
end
def test_plus_incorrect_currency
assert_raise MoneySum::WrongCurrency do MoneySum.new(1, "$") + MoneySum.new(2, "eur"); end
end
def test_equal_incorrect_currency
assert_raise MoneySum::WrongCurrency do MoneySum.new(1, "$") == MoneySum.new(2, "eur"); end
end
end
# ---- File: Rakefile ----
require 'rubygems'
require 'rake'
require 'rake/clean'
require 'rake/gempackagetask'
require 'rake/rdoctask'
require 'rake/testtask'
spec = Gem::Specification.new do |s|
s.name = 'Currency Calculator'
s.version = '0.0.1'
s.has_rdoc = true
s.extra_rdoc_files = ['README']
s.summary = 'Currency calculator for 0chan special olympics'
s.description = s.summary
s.files = %w(README) + Dir.glob("{bin,lib}/**/*")
s.require_path = "lib"
end
Rake::GemPackageTask.new(spec) do |p|
p.gem_spec = spec
p.need_tar = false
p.need_zip = false
end
Rake::RDocTask.new do |rdoc|
files =['README', 'lib/**/*.rb']
rdoc.rdoc_files.add(files)
rdoc.main = "README" # page to start on
rdoc.title = "Currency Calculator"
rdoc.rdoc_dir = 'doc/rdoc' # rdoc output folder
rdoc.options << '--line-numbers'
end
Rake::TestTask.new do |t|
t.test_files = FileList['test/**/*.rb']
t.libs = ["lib"]
end
Revise this Paste