Среда, 23 июня 2021 01:15

JSON RESTful API. ATM on Grape & Ruby on Rails

Оцените материал
(3 голосов)

Сегодня в нашей программе - несложный API, построенный на основе Ruby on Rails и микрофреймворка Grape, имитирующий работу некоего банкомата. Это JSON RESTful API, банк которого вы можете "пополнить" виртуальными купюрами, и который сумеет "выдать" вам любую требуемую сумму исходя из имеющихся в наличии банкнот, при этом их количество в недрах банкомата, само собой, уменьшится. Все действия, как видно из скриншота, весьма удобно свершать посредством curl.

 

API ATM on Grape & Ruby on Rails
API ATM on Grape & Ruby on Rails

 

git clone https://github.com/cmirnow/atm-on-grape.git

 

Вы легко освоите описанную механику, посетив и клонировав репозиторий atm-on-grape, также следуя нехитрым указаниям документации. Сейчас же кратко остановлюсь всего на одном.

 

Увидеть на гитхабе.

 

Понятно, что основой логики и наиболее интересным моментом здесь является автоматический расчет требуемой суммы, исходя из числа имеющихся купюр определенных номиналов. Разберем ряд способов реализации этого механизма, часть которых вполне возможно отнести к области динамического программирования.

 

Dev banner 2

 

Два простейших решения выглядят следующим образом:

 

  def subset_sum(inp, s)
        arr = []
        loop.with_index{|_, i| arr << inp.combination(i+1).to_a.find{
    |c| (c.inject(0) {|sum,x| sum + x})  == s}; break if !a.none? || i == inp.count }
    arr
  end

  subset_sum([1,2,3,4,5,6], 15)

 

  def subset_sum(*args)
    args[0].length.downto(1).flat_map do |i|
      args[0].combination(i).to_a
    end.select do |a|
      a.inject(:+) == args[1]
    end
  end
  
  subset_sum([1,2,3,4,5,6], 15).first

 

Это отлично работает, пока первый аргумент вызова метода содержит массив из нескольких элементов (купюр), но вы не представляете, как безбожно способен затянуться процесс расчета, если количество "купюр" некритично больше; скажем, два или три десятка. В этом случае оптимально прибегнуть к такой рекурсии:

 

def subset_sum((first, *rest), target)
  return (first == target ? [first] : nil) if rest.empty?

  rv = subset_sum(rest, target)
  return rv unless rv.nil?
  return nil if first > target
  return [first] if first == target

  rv = subset_sum(rest, target - first)
  rv.nil? ? nil : [first, *rv]
end

print subset_sum([1, 2, 3, 4, 5, 6, 3, 4, 5, 6, 1, 2], 15)

 

Или даже так:

 

def subset_sum(inp, sum)
  candidates = inp.filter { |i| i < sum } # don't do this optimize if inp contain negative numbers
  sum_combi_hash = Hash.new do |hash, key|
    if key > 0
      hash[key] = { candidates[key] => [candidates[key]] }
      if hash[key - 1][sum].nil?
        hash[key - 1].each do |s, c|
          hash[key][s] = c
          new_s = s + candidates[key]
          next unless hash[key][new_s].nil?

          hash[key][new_s] = c + [candidates[key]]
          break if new_s == sum
        end
      else
        hash[key][sum] = hash[key - 1][sum]
      end
      hash[key]
    else
      hash[key] = { candidates.first => [candidates.first] }
    end
  end
  sum_combi_hash[candidates.size - 1][sum]
end

print subset_sum([1, 2, 3, 4, 5, 6, 3, 4, 5, 6, 1, 2], 15)

 

Вы сумеете провести успешные тесты описанной логики (также всего API), воспользовавшись простейшим минитестом:

 

require 'test_helper'
class Banknote::AssetTest < ActiveSupport::TestCase
  include Rack::Test::Methods

  def app
    Rails.application
  end

  test 'GET /api/v1/banknote_asset' do
    get('/api/v1/banknote_asset')
    assert last_response.ok?
    assert_equal 200, last_response.status
    assert_equal 58, last_response.length
  end

  test 'Successful cash withdrawal' do
    patch('/api/v1/banknote_asset?sum=93')
    assert_equal '[1, 2, 5, 10, 25, 50]', last_response.body
  end

  test 'Unable to issue required amount' do
    patch('/api/v1/banknote_asset?sum=94')
    assert_equal 'transaction not allowed', last_response.body
  end
end

 

Таким образом - ценность, на субъективный взгляд автора, представляют не деньги, виртуальные либо же реальные, а - сам по себе процесс познания. Воспользуйтесь моим эмпирическим опытом, если кого-то он заинтересует.

 

Последнее изменениеВоскресенье, 21 января 2024 05:31

Оставить комментарий

Добавьте ваш комментарий

Разработка web-проектов

Poker onRails

Комментарии в блоге