diff --git a/.github/workflows/run_specs.yml b/.github/workflows/run_specs.yml
new file mode 100644
index 0000000..79fc95f
--- /dev/null
+++ b/.github/workflows/run_specs.yml
@@ -0,0 +1,45 @@
+env:
+ RUBY_VERSION: 3.0.3
+ POSTGRES_USER: postgres
+ POSTGRES_PASSWORD: postgres
+ POSTGRES_DB: programmingtil_rails_1_test
+# DEVISE_JWT_SECRET_KEY: ${{ secrets.DEVISE_JWT_SECRET_KEY }}
+
+name: Rails Specs
+on: [push,pull_request]
+jobs:
+ rspec-test:
+ name: RSpec
+ runs-on: ubuntu-20.04
+ services:
+ postgres:
+ image: postgres:latest
+ ports:
+ - 5432:5432
+ env:
+ POSTGRES_USER: ${{ env.POSTGRES_USER }}
+ POSTGRES_PASSWORD: ${{ env.POSTGRES_PASSWORD }}
+ steps:
+ - uses: actions/checkout@v1
+ - uses: ruby/setup-ruby@v1
+ with:
+ ruby-version: '3.0.3' # Not needed with a .ruby-version file
+ bundler-cache: true # runs 'bundle install' and caches installed gems automatically
+ - name: Install postgres client
+ run: sudo apt-get install libpq-dev
+ - name: Install dependencies
+ run: |
+ gem install bundler
+ bundler install
+ - name: Create database
+ run: |
+ bundler exec rails db:create RAILS_ENV=test
+ bundler exec rails db:migrate RAILS_ENV=test
+ - name: Run tests
+ run: bundler exec rspec spec/*
+ - name: Upload coverage results
+ uses: actions/upload-artifact@master
+ if: always()
+ with:
+ name: coverage-report
+ path: coverage
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 6de91cd..744e484 100644
--- a/.gitignore
+++ b/.gitignore
@@ -29,3 +29,4 @@
/.idea
.env
public/uploads
+coverage
diff --git a/.rspec b/.rspec
new file mode 100644
index 0000000..c99d2e7
--- /dev/null
+++ b/.rspec
@@ -0,0 +1 @@
+--require spec_helper
diff --git a/Gemfile b/Gemfile
index 2bc3044..1025ac3 100644
--- a/Gemfile
+++ b/Gemfile
@@ -60,6 +60,9 @@ gem 'bootsnap', '>= 1.1.0', require: false
group :development, :test do
# Call 'byebug' anywhere in the code to stop execution and get a debugger console
gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
+ gem 'rspec-rails'
+ gem 'factory_bot_rails'
+ gem 'timecop'
end
group :development do
@@ -76,7 +79,8 @@ group :test do
gem 'capybara', '>= 2.15'
gem 'selenium-webdriver'
# Easy installation and use of chromedriver to run system tests with Chrome
- gem 'chromedriver-helper'
+ # gem 'chromedriver-helper'
+ gem 'simplecov'
end
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
diff --git a/Gemfile.lock b/Gemfile.lock
index d1c8450..eea5a66 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,84 +1,82 @@
GEM
remote: https://rubygems.org/
specs:
- actioncable (7.0.2.3)
- actionpack (= 7.0.2.3)
- activesupport (= 7.0.2.3)
+ actioncable (7.0.4)
+ actionpack (= 7.0.4)
+ activesupport (= 7.0.4)
nio4r (~> 2.0)
websocket-driver (>= 0.6.1)
- actionmailbox (7.0.2.3)
- actionpack (= 7.0.2.3)
- activejob (= 7.0.2.3)
- activerecord (= 7.0.2.3)
- activestorage (= 7.0.2.3)
- activesupport (= 7.0.2.3)
+ actionmailbox (7.0.4)
+ actionpack (= 7.0.4)
+ activejob (= 7.0.4)
+ activerecord (= 7.0.4)
+ activestorage (= 7.0.4)
+ activesupport (= 7.0.4)
mail (>= 2.7.1)
net-imap
net-pop
net-smtp
- actionmailer (7.0.2.3)
- actionpack (= 7.0.2.3)
- actionview (= 7.0.2.3)
- activejob (= 7.0.2.3)
- activesupport (= 7.0.2.3)
+ actionmailer (7.0.4)
+ actionpack (= 7.0.4)
+ actionview (= 7.0.4)
+ activejob (= 7.0.4)
+ activesupport (= 7.0.4)
mail (~> 2.5, >= 2.5.4)
net-imap
net-pop
net-smtp
rails-dom-testing (~> 2.0)
- actionpack (7.0.2.3)
- actionview (= 7.0.2.3)
- activesupport (= 7.0.2.3)
+ actionpack (7.0.4)
+ actionview (= 7.0.4)
+ activesupport (= 7.0.4)
rack (~> 2.0, >= 2.2.0)
rack-test (>= 0.6.3)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.2.0)
- actiontext (7.0.2.3)
- actionpack (= 7.0.2.3)
- activerecord (= 7.0.2.3)
- activestorage (= 7.0.2.3)
- activesupport (= 7.0.2.3)
+ actiontext (7.0.4)
+ actionpack (= 7.0.4)
+ activerecord (= 7.0.4)
+ activestorage (= 7.0.4)
+ activesupport (= 7.0.4)
globalid (>= 0.6.0)
nokogiri (>= 1.8.5)
- actionview (7.0.2.3)
- activesupport (= 7.0.2.3)
+ actionview (7.0.4)
+ activesupport (= 7.0.4)
builder (~> 3.1)
erubi (~> 1.4)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.1, >= 1.2.0)
- activejob (7.0.2.3)
- activesupport (= 7.0.2.3)
+ activejob (7.0.4)
+ activesupport (= 7.0.4)
globalid (>= 0.3.6)
- activemodel (7.0.2.3)
- activesupport (= 7.0.2.3)
- activerecord (7.0.2.3)
- activemodel (= 7.0.2.3)
- activesupport (= 7.0.2.3)
- activestorage (7.0.2.3)
- actionpack (= 7.0.2.3)
- activejob (= 7.0.2.3)
- activerecord (= 7.0.2.3)
- activesupport (= 7.0.2.3)
+ activemodel (7.0.4)
+ activesupport (= 7.0.4)
+ activerecord (7.0.4)
+ activemodel (= 7.0.4)
+ activesupport (= 7.0.4)
+ activestorage (7.0.4)
+ actionpack (= 7.0.4)
+ activejob (= 7.0.4)
+ activerecord (= 7.0.4)
+ activesupport (= 7.0.4)
marcel (~> 1.0)
mini_mime (>= 1.1.0)
- activesupport (7.0.2.3)
+ activesupport (7.0.4)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 1.6, < 2)
minitest (>= 5.1)
tzinfo (~> 2.0)
- addressable (2.8.0)
- public_suffix (>= 2.0.2, < 5.0)
- archive-zip (0.12.0)
- io-like (~> 0.3.0)
+ addressable (2.8.1)
+ public_suffix (>= 2.0.2, < 6.0)
autoprefixer-rails (8.6.5)
execjs
- bcrypt (3.1.17)
+ bcrypt (3.1.18)
bindex (0.8.1)
- bootsnap (1.11.1)
+ bootsnap (1.13.0)
msgpack (~> 1.2)
builder (3.2.4)
byebug (11.1.3)
- capybara (3.36.0)
+ capybara (3.37.1)
addressable
matrix
mini_mime (>= 0.1.3)
@@ -93,9 +91,6 @@ GEM
mime-types (>= 1.16)
ssrf_filter (~> 1.0)
childprocess (4.1.0)
- chromedriver-helper (2.1.1)
- archive-zip (~> 0.10)
- nokogiri (~> 1.8)
coffee-rails (4.2.2)
coffee-script (>= 2.2.0)
railties (>= 4.0.0)
@@ -103,7 +98,7 @@ GEM
coffee-script-source
execjs
coffee-script-source (1.12.2)
- concurrent-ruby (1.1.9)
+ concurrent-ruby (1.1.10)
content_disposition (1.0.0)
crass (1.0.6)
devise (4.8.1)
@@ -112,40 +107,44 @@ GEM
railties (>= 4.1.0)
responders
warden (~> 1.2.3)
- digest (3.1.0)
- dotenv (2.7.6)
- dotenv-rails (2.7.6)
- dotenv (= 2.7.6)
+ diff-lcs (1.5.0)
+ docile (1.4.0)
+ dotenv (2.8.1)
+ dotenv-rails (2.8.1)
+ dotenv (= 2.8.1)
railties (>= 3.2)
- down (5.3.0)
+ down (5.3.1)
addressable (~> 2.8)
- erubi (1.10.0)
+ erubi (1.11.0)
execjs (2.8.1)
- faraday (2.2.0)
- faraday-net_http (~> 2.0)
+ factory_bot (6.2.0)
+ activesupport (>= 5.0.0)
+ factory_bot_rails (6.2.0)
+ factory_bot (~> 6.2.0)
+ railties (>= 5.0.0)
+ faraday (2.5.2)
+ faraday-net_http (>= 2.0, < 3.1)
ruby2_keywords (>= 0.0.4)
- faraday-net_http (2.0.1)
+ faraday-net_http (3.0.0)
ffi (1.15.5)
ffi (1.15.5-x64-mingw32)
globalid (1.0.0)
activesupport (>= 5.0)
hashie (5.0.0)
- i18n (1.10.0)
+ i18n (1.12.0)
concurrent-ruby (~> 1.0)
- io-like (0.3.1)
- io-wait (0.2.1)
jbuilder (2.11.5)
actionview (>= 5.0.0)
activesupport (>= 5.0.0)
- jquery-rails (4.4.0)
+ jquery-rails (4.5.0)
rails-dom-testing (>= 1, < 3)
railties (>= 4.2.0)
thor (>= 0.14, < 2.0)
- jwt (2.3.0)
+ jwt (2.5.0)
listen (3.7.1)
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
- loofah (2.15.0)
+ loofah (2.19.0)
crass (~> 1.0.2)
nokogiri (>= 1.5.9)
mail (2.7.1)
@@ -159,96 +158,106 @@ GEM
mini_magick (4.11.0)
mini_mime (1.1.2)
mini_portile2 (2.8.0)
- minitest (5.15.0)
- msgpack (1.4.5)
- multi_json (1.15.0)
+ minitest (5.16.3)
+ msgpack (1.5.6)
multi_xml (0.6.0)
- net-imap (0.2.3)
- digest
+ net-imap (0.3.0)
net-protocol
- strscan
- net-pop (0.1.1)
- digest
+ net-pop (0.1.2)
net-protocol
+ net-protocol (0.1.3)
timeout
- net-protocol (0.1.2)
- io-wait
- timeout
- net-smtp (0.3.1)
- digest
+ net-smtp (0.3.2)
net-protocol
- timeout
nio4r (2.5.8)
- nokogiri (1.13.3)
+ nokogiri (1.13.8)
mini_portile2 (~> 2.8.0)
racc (~> 1.4)
- nokogiri (1.13.3-x64-mingw32)
+ nokogiri (1.13.8-x64-mingw32)
racc (~> 1.4)
- oauth2 (1.4.9)
+ oauth2 (2.0.9)
faraday (>= 0.17.3, < 3.0)
jwt (>= 1.0, < 3.0)
- multi_json (~> 1.3)
multi_xml (~> 0.5)
- rack (>= 1.2, < 3)
- omniauth (2.0.4)
+ rack (>= 1.2, < 4)
+ snaky_hash (~> 2.0)
+ version_gem (~> 1.1)
+ omniauth (2.1.0)
hashie (>= 3.4.6)
- rack (>= 1.6.2, < 3)
+ rack (>= 2.2.3)
rack-protection
- omniauth-oauth2 (1.7.2)
- oauth2 (~> 1.4)
- omniauth (>= 1.9, < 3)
+ omniauth-oauth2 (1.8.0)
+ oauth2 (>= 1.4, < 3)
+ omniauth (~> 2.0)
omniauth-rails_csrf_protection (1.0.1)
actionpack (>= 4.2)
omniauth (~> 2.0)
orm_adapter (0.5.0)
paranoia (2.6.0)
activerecord (>= 5.1, < 7.1)
- pg (1.3.4)
- pg (1.3.4-x64-mingw32)
- public_suffix (4.0.6)
- puma (4.3.11)
+ pg (1.4.3)
+ pg (1.4.3-x64-mingw32)
+ public_suffix (5.0.0)
+ puma (4.3.12)
nio4r (~> 2.0)
racc (1.6.0)
- rack (2.2.3)
- rack-protection (2.2.0)
+ rack (2.2.4)
+ rack-protection (3.0.1)
rack
- rack-test (1.1.0)
- rack (>= 1.0, < 3)
- rails (7.0.2.3)
- actioncable (= 7.0.2.3)
- actionmailbox (= 7.0.2.3)
- actionmailer (= 7.0.2.3)
- actionpack (= 7.0.2.3)
- actiontext (= 7.0.2.3)
- actionview (= 7.0.2.3)
- activejob (= 7.0.2.3)
- activemodel (= 7.0.2.3)
- activerecord (= 7.0.2.3)
- activestorage (= 7.0.2.3)
- activesupport (= 7.0.2.3)
+ rack-test (2.0.2)
+ rack (>= 1.3)
+ rails (7.0.4)
+ actioncable (= 7.0.4)
+ actionmailbox (= 7.0.4)
+ actionmailer (= 7.0.4)
+ actionpack (= 7.0.4)
+ actiontext (= 7.0.4)
+ actionview (= 7.0.4)
+ activejob (= 7.0.4)
+ activemodel (= 7.0.4)
+ activerecord (= 7.0.4)
+ activestorage (= 7.0.4)
+ activesupport (= 7.0.4)
bundler (>= 1.15.0)
- railties (= 7.0.2.3)
+ railties (= 7.0.4)
rails-dom-testing (2.0.3)
activesupport (>= 4.2.0)
nokogiri (>= 1.6)
- rails-html-sanitizer (1.4.2)
+ rails-html-sanitizer (1.4.3)
loofah (~> 2.3)
- railties (7.0.2.3)
- actionpack (= 7.0.2.3)
- activesupport (= 7.0.2.3)
+ railties (7.0.4)
+ actionpack (= 7.0.4)
+ activesupport (= 7.0.4)
method_source
rake (>= 12.2)
thor (~> 1.0)
zeitwerk (~> 2.5)
rake (13.0.6)
- rb-fsevent (0.11.1)
+ rb-fsevent (0.11.2)
rb-inotify (0.10.1)
ffi (~> 1.0)
- regexp_parser (2.2.1)
+ regexp_parser (2.6.0)
responders (3.0.1)
actionpack (>= 5.0)
railties (>= 5.0)
rexml (3.2.5)
+ rspec-core (3.11.0)
+ rspec-support (~> 3.11.0)
+ rspec-expectations (3.11.1)
+ diff-lcs (>= 1.2.0, < 2.0)
+ rspec-support (~> 3.11.0)
+ rspec-mocks (3.11.1)
+ diff-lcs (>= 1.2.0, < 2.0)
+ rspec-support (~> 3.11.0)
+ rspec-rails (5.1.2)
+ actionpack (>= 5.2)
+ activesupport (>= 5.2)
+ railties (>= 5.2)
+ rspec-core (~> 3.10)
+ rspec-expectations (~> 3.10)
+ rspec-mocks (~> 3.10)
+ rspec-support (~> 3.10)
+ rspec-support (3.11.1)
ruby2_keywords (0.0.5)
rubyzip (2.3.2)
sassc (2.4.0)
@@ -261,42 +270,53 @@ GEM
sprockets (> 3.0)
sprockets-rails
tilt
- selenium-webdriver (4.1.0)
+ selenium-webdriver (4.4.0)
childprocess (>= 0.5, < 5.0)
rexml (~> 3.2, >= 3.2.5)
- rubyzip (>= 1.2.2)
+ rubyzip (>= 1.2.2, < 3.0)
+ websocket (~> 1.0)
semantic-ui-sass (2.4.4.0)
sassc
shrine (3.4.0)
content_disposition (~> 1.0)
down (~> 5.1)
+ simplecov (0.21.2)
+ docile (~> 1.1)
+ simplecov-html (~> 0.11)
+ simplecov_json_formatter (~> 0.1)
+ simplecov-html (0.12.3)
+ simplecov_json_formatter (0.1.4)
+ snaky_hash (2.0.1)
+ hashie
+ version_gem (~> 1.1, >= 1.1.1)
spring (2.1.1)
spring-watcher-listen (2.0.1)
listen (>= 2.7, < 4.0)
spring (>= 1.2, < 3.0)
- sprockets (4.0.3)
+ sprockets (4.1.1)
concurrent-ruby (~> 1.0)
rack (> 1, < 3)
sprockets-rails (3.4.2)
actionpack (>= 5.2)
activesupport (>= 5.2)
sprockets (>= 3.0.0)
- ssrf_filter (1.0.7)
- strscan (3.0.1)
+ ssrf_filter (1.1.1)
thor (1.2.1)
- tilt (2.0.10)
- timeout (0.2.0)
+ tilt (2.0.11)
+ timecop (0.9.5)
+ timeout (0.3.0)
trix-rails (2.4.0)
rails (> 4.1)
turbolinks (5.2.1)
turbolinks-source (~> 5.2)
turbolinks-source (5.2.0)
- tzinfo (2.0.4)
+ tzinfo (2.0.5)
concurrent-ruby (~> 1.0)
- tzinfo-data (1.2022.1)
+ tzinfo-data (1.2022.4)
tzinfo (>= 1.0.0)
uglifier (4.2.0)
execjs (>= 0.3.0, < 3)
+ version_gem (1.1.1)
warden (1.2.9)
rack (>= 2.0.9)
web-console (4.2.0)
@@ -304,12 +324,13 @@ GEM
activemodel (>= 6.0.0)
bindex (>= 0.4.0)
railties (>= 6.0.0)
+ websocket (1.2.9)
websocket-driver (0.7.5)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.5)
xpath (3.2.0)
nokogiri (~> 1.8)
- zeitwerk (2.5.4)
+ zeitwerk (2.6.0)
PLATFORMS
ruby
@@ -322,10 +343,10 @@ DEPENDENCIES
byebug
capybara (>= 2.15)
carrierwave (~> 1.0)
- chromedriver-helper
coffee-rails (~> 4.2)
devise
dotenv-rails
+ factory_bot_rails
jbuilder (~> 2.9)
jquery-rails
listen
@@ -337,12 +358,15 @@ DEPENDENCIES
pg (>= 0.18, < 2.0)
puma (~> 4.3)
rails (~> 7.0.1)
+ rspec-rails
sassc-rails
selenium-webdriver
semantic-ui-sass
shrine
+ simplecov
spring
spring-watcher-listen (~> 2.0.0)
+ timecop
trix-rails
turbolinks (~> 5)
tzinfo-data
diff --git a/app/controllers/rents_controller.rb b/app/controllers/rents_controller.rb
index bd57a49..e4368a5 100644
--- a/app/controllers/rents_controller.rb
+++ b/app/controllers/rents_controller.rb
@@ -101,7 +101,7 @@ def set_rent
# Only allow a list of trusted parameters through.
def rent_params
- params.require(:rent).permit(:item_id, :user_id, :begin, :end, :state)
+ params.require(:rent).permit(:item_id, :user_id, :begin, :end, :state, :number)
end
def rent_filter_params
diff --git a/app/models/rent.rb b/app/models/rent.rb
index 38f882f..62e36e5 100644
--- a/app/models/rent.rb
+++ b/app/models/rent.rb
@@ -3,9 +3,20 @@ class Rent < ApplicationRecord
belongs_to :user
enum state: [:unprocessed, :approved, :rejected, :taken, :returned], _default: :unprocessed
- validates :begin, presence: true, comparison: { greater_than: DateTime.now, message: " később legyen, mint a mostani időpont." }
+ validates :begin, presence: true
+ validate :begin_cant_be_before_the_present
validates :end, presence: true , comparison: { greater_than: :begin, message: " később legyen, mint a kezdete." }
+ validates :number, presence: true, comparison: { greater_than: 0 }
+ validates_with RentNumberValidator
+
+ def begin_cant_be_before_the_present
+ if self.begin < Time.now
+ errors.add(:begin, "később legyen, mint a mostani időpont.")
+ end
+ end
+
+ #
#STATE MACHINE
def approve!
raise Exceptions::ForbiddenTransition unless unprocessed?
@@ -42,5 +53,9 @@ def self.users_filter(params, id)
def item
Item.unscoped { super }
end
+
+ def interval
+ self.begin..self.end
+ end
end
diff --git a/app/validators/rent_number_validator.rb b/app/validators/rent_number_validator.rb
new file mode 100644
index 0000000..543a736
--- /dev/null
+++ b/app/validators/rent_number_validator.rb
@@ -0,0 +1,64 @@
+# Validators are instantiated once, instance variables persist between validations, might cause bugs
+class RentNumberValidator < ActiveModel::Validator
+ def validate(rent)
+ active_rents = active_rents(rent)
+ intervals = intervals(rent, active_rents)
+ sum_items_in_intervals = sum_items_in_intervals(intervals, active_rents)
+
+ sum_items_in_intervals.each do |interval, sum_items|
+ available_item_number = rent.item.number - sum_items
+ if available_item_number < rent.number
+ rent.errors.add :base, "A kiválasztott időszakban (#{(interval)}) csak #{available_item_number} darab #{rent.item.name} elérhető."
+ end
+ end
+ end
+
+ private
+
+ def formatted_interval(interval)
+ "#{formatted_date(interval.begin)} - #{formatted_date(interval.end)}"
+ end
+
+ def formatted_date(date)
+ date.strftime("%Y-%m-%d %H:%M")
+ end
+ def sum_items_in_intervals(intervals, active_rents)
+ result = {}
+ intervals.each do |interval|
+ sum_item_number = 0
+ active_rents.each do |active_rent|
+ if overlaps?(interval, active_rent.interval)
+ sum_item_number += active_rent.number
+ end
+ end
+ result[formatted_interval(interval)] = sum_item_number
+ end
+ result
+ end
+
+ def overlaps?(range1, range2)
+ !(range2.end <= range1.begin || range1.end <= range2.begin)
+ end
+
+ def intervals(rent, active_rents)
+ time_points = active_rents.map { |active_rent| [active_rent.begin, active_rent.end] }.flatten
+ time_points = time_points.select { |time_point| time_point.between?(rent.begin, rent.end) }
+ time_points.push(rent.begin, rent.end)
+ time_points = time_points.uniq.sort
+ intervals = time_points.map.with_index do |time_point, index|
+ next_time_point = time_points[index + 1]
+ next unless next_time_point
+
+ (time_point..next_time_point)
+ end.compact
+ intervals
+ end
+
+ def active_rents(rent)
+ Rent.includes(:item)
+ .where(item: rent.item)
+ .where(state: [:approved, :taken])
+ .where.not('rents.end <= :begin or :end <= rents.begin',
+ { begin: rent.begin, end: rent.end })
+ end
+end
diff --git a/app/views/rents/_form.html.erb b/app/views/rents/_form.html.erb
index 469be30..1e58f22 100644
--- a/app/views/rents/_form.html.erb
+++ b/app/views/rents/_form.html.erb
@@ -24,6 +24,11 @@
<%= form.hidden_field :item_id, :value => @rent.item_id %>
+
+ <%= form.label :number, 'Darabszám' %>
+ <%= form.number_field :number %>
+
+
<%= form.label :begin, 'Kezdete' %>
<%= form.datetime_field :begin %>
diff --git a/app/views/rents/index.html.erb b/app/views/rents/index.html.erb
index 5525317..394c6e2 100644
--- a/app/views/rents/index.html.erb
+++ b/app/views/rents/index.html.erb
@@ -28,6 +28,7 @@
| Név |
Eszköz |
+ Darabszám |
Kezdet |
Vége |
Állapot |
@@ -40,6 +41,7 @@
| <%= rent.user.name %> |
<%= rent.item.name %> |
+ <%= rent.number %> darab |
<%= rent.begin.strftime("%Y.%m.%d. %H:%M") %> |
<%= rent.end.strftime("%Y.%m.%d. %H:%M") %> |
<%= I18n.t :"rent_states.#{rent.state}" %> |
diff --git a/app/views/rents/users_index.html.erb b/app/views/rents/users_index.html.erb
index b15b0ed..5b0422e 100644
--- a/app/views/rents/users_index.html.erb
+++ b/app/views/rents/users_index.html.erb
@@ -27,6 +27,7 @@
| Eszköz |
+ Darabszám |
Kezdet |
Vége |
Állapot |
@@ -37,6 +38,7 @@
<% @rents.each do |rent| %>
| <%= rent.item.name %> |
+ <%= rent.number %> darab |
<%= rent.begin.strftime("%Y.%m.%d. %H:%M") %> |
<%= rent.end.strftime("%Y.%m.%d. %H:%M") %> |
<%= I18n.t :"rent_states.#{rent.state}" %> |
diff --git a/config/application.rb b/config/application.rb
index 5652589..9ee2bd7 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -9,7 +9,8 @@
module Schorpong
class Application < Rails::Application
# Initialize configuration defaults for originally generated Rails version.
- config.load_defaults 5.2
+ # config.load_defaults 5.2
+ config.active_record.legacy_connection_handling = false
config.i18n.default_locale = :hu
# Configuration for the application, engines, and railties goes here.
diff --git a/config/initializers/oauth.rb b/config/initializers/oauth.rb
index 63a6043..89f03a3 100644
--- a/config/initializers/oauth.rb
+++ b/config/initializers/oauth.rb
@@ -41,7 +41,7 @@ def raw_info
session[:access_token] = access_token.token
url = "/api/profile"
params = {:params => { :access_token => access_token.token}}
- @raw_info ||= MultiJson.decode(access_token.client.request(:get, url, params).body)
+ @raw_info ||= JSON.parse(access_token.client.request(:get, url, params).body)
end
end
diff --git a/config/locales/hu.yml b/config/locales/hu.yml
index 4dd77b5..778035e 100644
--- a/config/locales/hu.yml
+++ b/config/locales/hu.yml
@@ -17,6 +17,7 @@ hu:
name: "Név"
number: "Mennyiség"
rent:
+ number: "Darabszám"
begin: "Kezdete"
end: "Vége"
state: "Állapot"
diff --git a/db/migrate/20220922143621_add_number_to_rent.rb b/db/migrate/20220922143621_add_number_to_rent.rb
new file mode 100644
index 0000000..d3ca616
--- /dev/null
+++ b/db/migrate/20220922143621_add_number_to_rent.rb
@@ -0,0 +1,5 @@
+class AddNumberToRent < ActiveRecord::Migration[7.0]
+ def change
+ add_column :rents, :number, :integer
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 566025f..436c966 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema[7.0].define(version: 2022_08_16_134517) do
+ActiveRecord::Schema[7.0].define(version: 2022_09_22_143621) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -115,6 +115,7 @@
t.integer "state"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
+ t.integer "number"
t.index ["item_id"], name: "index_rents_on_item_id"
t.index ["user_id"], name: "index_rents_on_user_id"
end
diff --git a/db/seeds.rb b/db/seeds.rb
index ca38272..cdf364c 100644
--- a/db/seeds.rb
+++ b/db/seeds.rb
@@ -45,10 +45,15 @@
teams.each { |t| Entry.create(team_id: t[:team].id, event_id: event1.id, showed_up: false, comment: t[:comment]) }
-items = [{ name: 'Pohár', number: 3 },
- { name: 'Labda', number: 5 }]
+items = [{ name: 'Pohár', number: 10 },
+ { name: 'Labda', number: 20 }]
items.each { |item| Item.create(item) }
-rents = [{ item_id: 1, user_id: 1, begin: DateTime.new(2022,6,23,4,5,6), end: DateTime.new(2022,6,30,4,5,6)},
- { item_id: 2, user_id: 2, begin: DateTime.new(2022,6,24,4,5,6), end: DateTime.new(2022,6,29,4,5,6)}]
+rents =[ { item_id: 1, user_id: 1, begin: DateTime.now + 3.days, end: DateTime.now + 4.days, number: 10},
+ { item_id: 2, user_id: 2, begin: DateTime.now + 3.days, end: DateTime.now + 4.days, number: 1},
+ { item_id: 2, user_id: 2, begin: DateTime.now + 3.days, end: DateTime.now + 5.days, number: 2},
+ { item_id: 2, user_id: 2, begin: DateTime.now + 3.days, end: DateTime.now + 9.days, number: 3},
+ { item_id: 2, user_id: 2, begin: DateTime.now + 6.days, end: DateTime.now + 7.days, number: 4},
+ { item_id: 2, user_id: 2, begin: DateTime.now + 6.days, end: DateTime.now + 9.days, number: 5},
+ { item_id: 2, user_id: 2, begin: DateTime.now + 9.days, end: DateTime.now + 10.days, number: 6}]
rents.each { |rent| Rent.create(rent) }
diff --git a/spec/factories/items.rb b/spec/factories/items.rb
new file mode 100644
index 0000000..3beed38
--- /dev/null
+++ b/spec/factories/items.rb
@@ -0,0 +1,6 @@
+FactoryBot.define do
+ factory :item do
+ name { 'pohár' }
+ number { 5 }
+ end
+end
diff --git a/spec/factories/users.rb b/spec/factories/users.rb
new file mode 100644
index 0000000..6991a31
--- /dev/null
+++ b/spec/factories/users.rb
@@ -0,0 +1,7 @@
+FactoryBot.define do
+ factory :user do
+ name {'User'}
+ mail { 'user@email.com' }
+ password { 'secret1234' }
+ end
+end
diff --git a/spec/models/rent_spec.rb b/spec/models/rent_spec.rb
new file mode 100644
index 0000000..d50d70b
--- /dev/null
+++ b/spec/models/rent_spec.rb
@@ -0,0 +1,205 @@
+require 'rails_helper'
+
+RSpec.describe Rent, type: :model do
+ let(:user) { create(:user) }
+ let(:item) { create(:item, number: 10) }
+ let(:pre_existing_rent) do
+ Rent.create!(state: :approved, item: item, user: user,
+ begin: DateTime.new(2022, 1, 10),
+ end: DateTime.new(2022, 1, 15),
+ number: 5)
+ end
+ let(:current_rent) do
+ Rent.new(state: :approved, item: item, user: user,
+ begin: begin_date,
+ end: end_date,
+ number: rent_number)
+ end
+
+ subject do
+ pre_existing_rent
+ current_rent
+ end
+
+ before(:each) do
+ Timecop.freeze(DateTime.new(2022, 1, 1))
+ end
+
+ after do
+ Timecop.return
+ end
+
+ context 'when the rent number equals to all available item number' do
+ let(:rent_number) { 10 }
+ context 'when the rent starts before the pre existing rent' do
+ let(:begin_date) { DateTime.new(2022, 1, 5) }
+ context 'and ends before the current rent' do
+ let(:end_date) { DateTime.new(2022, 1, 9) }
+ it { expect(subject).to be_valid }
+ end
+
+ context 'and ends on the current rent beginning date' do
+ let(:end_date) { DateTime.new(2022, 1, 10) }
+ it { expect(subject).to be_valid }
+ end
+
+ context 'and ends after the current_rent is take' do
+ let(:end_date) { DateTime.new(2022, 1, 11) }
+ it {
+ expect(subject).not_to be_valid
+ }
+ end
+ end
+
+ context 'when the rent ends after the pre existing rent' do
+ let(:end_date) { DateTime.new(2022, 1, 20) }
+ context 'and begins before the current rent is back' do
+ let(:begin_date) { DateTime.new(2022, 1, 14) }
+ it { expect(subject).not_to be_valid }
+ end
+
+ context 'and begins whe the current rent is back' do
+ let(:begin_date) { DateTime.new(2022, 1, 15) }
+ it { expect(subject).to be_valid }
+ end
+
+ context 'and begins after the current rent is back' do
+ let(:begin_date) { DateTime.new(2022, 1, 16) }
+ it { expect(subject).to be_valid }
+ end
+ end
+
+ context 'and begins and ends during the current rent' do
+ let(:begin_date) { DateTime.new(2022, 1, 11) }
+ let(:end_date) { DateTime.new(2022, 1, 14) }
+ it { expect(subject).not_to be_valid }
+ end
+
+ context 'and begins and ends at the same time as the current rent' do
+ let(:begin_date) { DateTime.new(2022, 1, 10) }
+ let(:end_date) { DateTime.new(2022, 1, 15) }
+ it { expect(subject).not_to be_valid }
+ end
+ end
+
+ context 'when the rent number is less then all available item number' do
+ let(:rent_number) { 1 }
+ context 'and begins and ends at the same time as the current rent' do
+ let(:begin_date) { DateTime.new(2022, 1, 10) }
+ let(:end_date) { DateTime.new(2022, 1, 15) }
+ it { expect(subject).to be_valid }
+ end
+ end
+
+ context "when a rent overlaps two existing rents but their is enough item" do
+ let(:rent_1) do
+ Rent.new(state: :approved, item: item, user: user,
+ begin: DateTime.new.change(year: 2022, month: 1, day: 5, hour: 12),
+ end: DateTime.new.change(year: 2022, month: 1, day: 10, hour: 12),
+ number: 5)
+ end
+ let(:rent_2) do
+ Rent.new(state: :approved, item: item, user: user,
+ begin: DateTime.new.change(year: 2022, month: 1, day: 10, hour: 14),
+ end: DateTime.new.change(year: 2022, month: 1, day: 15, hour: 12),
+ number: 5)
+ end
+
+ let(:current_rent) do
+ Rent.new(state: :approved, item: item, user: user,
+ begin: DateTime.new.change(year: 2022, month: 1, day: 8, hour: 14),
+ end: DateTime.new.change(year: 2022, month: 1, day: 12, hour: 12),
+ number: 5)
+ end
+ it 'should be valid' do
+ rent_1.save!
+ rent_2.save!
+ expect(current_rent).to be_valid
+ end
+ end
+
+ context "when a rent overlaps two existing rents but their is not enough item" do
+ let(:rent_1) do
+ Rent.new(state: :approved, item: item, user: user,
+ begin: DateTime.new.change(year: 2022, month: 1, day: 5, hour: 12),
+ end: DateTime.new.change(year: 2022, month: 1, day: 10, hour: 12),
+ number: 5)
+ end
+
+ let(:rent_2) do
+ Rent.new(state: :approved, item: item, user: user,
+ begin: DateTime.new.change(year: 2022, month: 1, day: 10, hour: 14),
+ end: DateTime.new.change(year: 2022, month: 1, day: 15, hour: 12),
+ number: 5)
+ end
+
+ let(:current_rent) do
+ Rent.new(state: :approved, item: item, user: user,
+ begin: DateTime.new.change(year: 2022, month: 1, day: 8, hour: 14),
+ end: DateTime.new.change(year: 2022, month: 1, day: 12, hour: 12),
+ number: 10)
+ end
+
+ it 'should be invalid' do
+ rent_1.save!
+ rent_2.save!
+ expect(current_rent).not_to be_valid
+ end
+ end
+
+ context "when a rent overlaps two existing rents ending at the same time but their is enough item" do
+ let(:rent_1) do
+ Rent.new(state: :approved, item: item, user: user,
+ begin: DateTime.new.change(year: 2022, month: 1, day: 5, hour: 12),
+ end: DateTime.new.change(year: 2022, month: 1, day: 10, hour: 12),
+ number: 3)
+ end
+ let(:rent_2) do
+ Rent.new(state: :approved, item: item, user: user,
+ begin: DateTime.new.change(year: 2022, month: 1, day: 7, hour: 12),
+ end: DateTime.new.change(year: 2022, month: 1, day: 10, hour: 12),
+ number: 3)
+ end
+
+ let(:current_rent) do
+ Rent.new(state: :approved, item: item, user: user,
+ begin: DateTime.new.change(year: 2022, month: 1, day: 3, hour: 12),
+ end: DateTime.new.change(year: 2022, month: 1, day: 12, hour: 12),
+ number: 4)
+ end
+ it 'should be valid' do
+ rent_1.save!
+ rent_2.save!
+ expect(current_rent).to be_valid
+ end
+ end
+
+ context "when a rent overlaps two existing rents ending at the same time but their is not enough item" do
+ let(:rent_1) do
+ Rent.new(state: :approved, item: item, user: user,
+ begin: DateTime.new.change(year: 2022, month: 1, day: 5, hour: 12),
+ end: DateTime.new.change(year: 2022, month: 1, day: 10, hour: 12),
+ number: 3)
+ end
+
+ let(:rent_2) do
+ Rent.new(state: :approved, item: item, user: user,
+ begin: DateTime.new.change(year: 2022, month: 1, day: 7, hour: 12),
+ end: DateTime.new.change(year: 2022, month: 1, day: 10, hour: 12),
+ number: 3)
+ end
+
+ let(:current_rent) do
+ Rent.new(state: :approved, item: item, user: user,
+ begin: DateTime.new.change(year: 2022, month: 1, day: 3, hour: 12),
+ end: DateTime.new.change(year: 2022, month: 1, day: 12, hour: 12),
+ number: 5)
+ end
+
+ it 'should not be valid' do
+ rent_1.save!
+ rent_2.save!
+ expect(current_rent).not_to be_valid
+ end
+ end
+end
diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb
new file mode 100644
index 0000000..a5e7636
--- /dev/null
+++ b/spec/rails_helper.rb
@@ -0,0 +1,68 @@
+# This file is copied to spec/ when you run 'rails generate rspec:install'
+require 'simplecov'
+SimpleCov.start 'rails'
+
+require 'spec_helper'
+ENV['RAILS_ENV'] ||= 'test'
+
+require_relative '../config/environment'
+# Prevent database truncation if the environment is production
+abort("The Rails environment is running in production mode!") if Rails.env.production?
+require 'rspec/rails'
+# Add additional requires below this line. Rails is not loaded until this point!
+require_relative './support/factory_bot'
+# Requires supporting ruby files with custom matchers and macros, etc, in
+# spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are
+# run as spec files by default. This means that files in spec/support that end
+# in _spec.rb will both be required and run as specs, causing the specs to be
+# run twice. It is recommended that you do not name files matching this glob to
+# end with _spec.rb. You can configure this pattern with the --pattern
+# option on the command line or in ~/.rspec, .rspec or `.rspec-local`.
+#
+# The following line is provided for convenience purposes. It has the downside
+# of increasing the boot-up time by auto-requiring all files in the support
+# directory. Alternatively, in the individual `*_spec.rb` files, manually
+# require only the support files necessary.
+#
+# Dir[Rails.root.join('spec', 'support', '**', '*.rb')].sort.each { |f| require f }
+
+# Checks for pending migrations and applies them before tests are run.
+# If you are not using ActiveRecord, you can remove these lines.
+begin
+ ActiveRecord::Migration.maintain_test_schema!
+rescue ActiveRecord::PendingMigrationError => e
+ puts e.to_s.strip
+ exit 1
+end
+RSpec.configure do |config|
+ # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
+ config.fixture_path = "#{::Rails.root}/spec/fixtures"
+
+ # If you're not using ActiveRecord, or you'd prefer not to run each of your
+ # examples within a transaction, remove the following line or assign false
+ # instead of true.
+ config.use_transactional_fixtures = true
+
+ # You can uncomment this line to turn off ActiveRecord support entirely.
+ # config.use_active_record = false
+
+ # RSpec Rails can automatically mix in different behaviours to your tests
+ # based on their file location, for example enabling you to call `get` and
+ # `post` in specs under `spec/controllers`.
+ #
+ # You can disable this behaviour by removing the line below, and instead
+ # explicitly tag your specs with their type, e.g.:
+ #
+ # RSpec.describe UsersController, type: :controller do
+ # # ...
+ # end
+ #
+ # The different available types are documented in the features, such as in
+ # https://relishapp.com/rspec/rspec-rails/docs
+ config.infer_spec_type_from_file_location!
+
+ # Filter lines from Rails gems in backtraces.
+ config.filter_rails_from_backtrace!
+ # arbitrary gems may also be filtered via:
+ # config.filter_gems_from_backtrace("gem name")
+end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
new file mode 100644
index 0000000..a0d4080
--- /dev/null
+++ b/spec/spec_helper.rb
@@ -0,0 +1,94 @@
+# This file was generated by the `rails generate rspec:install` command. Conventionally, all
+# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
+# The generated `.rspec` file contains `--require spec_helper` which will cause
+# this file to always be loaded, without a need to explicitly require it in any
+# files.
+#
+# Given that it is always loaded, you are encouraged to keep this file as
+# light-weight as possible. Requiring heavyweight dependencies from this file
+# will add to the boot time of your test suite on EVERY test run, even for an
+# individual file that may not need all of that loaded. Instead, consider making
+# a separate helper file that requires the additional dependencies and performs
+# the additional setup, and require it from the spec files that actually need
+# it.
+#
+# See https://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
+RSpec.configure do |config|
+ # rspec-expectations config goes here. You can use an alternate
+ # assertion/expectation library such as wrong or the stdlib/minitest
+ # assertions if you prefer.
+ config.expect_with :rspec do |expectations|
+ # This option will default to `true` in RSpec 4. It makes the `description`
+ # and `failure_message` of custom matchers include text for helper methods
+ # defined using `chain`, e.g.:
+ # be_bigger_than(2).and_smaller_than(4).description
+ # # => "be bigger than 2 and smaller than 4"
+ # ...rather than:
+ # # => "be bigger than 2"
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
+ end
+
+ # rspec-mocks config goes here. You can use an alternate test double
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
+ config.mock_with :rspec do |mocks|
+ # Prevents you from mocking or stubbing a method that does not exist on
+ # a real object. This is generally recommended, and will default to
+ # `true` in RSpec 4.
+ mocks.verify_partial_doubles = true
+ end
+
+ # This option will default to `:apply_to_host_groups` in RSpec 4 (and will
+ # have no way to turn it off -- the option exists only for backwards
+ # compatibility in RSpec 3). It causes shared context metadata to be
+ # inherited by the metadata hash of host groups and examples, rather than
+ # triggering implicit auto-inclusion in groups with matching metadata.
+ config.shared_context_metadata_behavior = :apply_to_host_groups
+
+# The settings below are suggested to provide a good initial experience
+# with RSpec, but feel free to customize to your heart's content.
+=begin
+ # This allows you to limit a spec run to individual examples or groups
+ # you care about by tagging them with `:focus` metadata. When nothing
+ # is tagged with `:focus`, all examples get run. RSpec also provides
+ # aliases for `it`, `describe`, and `context` that include `:focus`
+ # metadata: `fit`, `fdescribe` and `fcontext`, respectively.
+ config.filter_run_when_matching :focus
+
+ # Allows RSpec to persist some state between runs in order to support
+ # the `--only-failures` and `--next-failure` CLI options. We recommend
+ # you configure your source control system to ignore this file.
+ config.example_status_persistence_file_path = "spec/examples.txt"
+
+ # Limits the available syntax to the non-monkey patched syntax that is
+ # recommended. For more details, see:
+ # https://relishapp.com/rspec/rspec-core/docs/configuration/zero-monkey-patching-mode
+ config.disable_monkey_patching!
+
+ # Many RSpec users commonly either run the entire suite or an individual
+ # file, and it's useful to allow more verbose output when running an
+ # individual spec file.
+ if config.files_to_run.one?
+ # Use the documentation formatter for detailed output,
+ # unless a formatter has already been configured
+ # (e.g. via a command-line flag).
+ config.default_formatter = "doc"
+ end
+
+ # Print the 10 slowest examples and example groups at the
+ # end of the spec run, to help surface which specs are running
+ # particularly slow.
+ config.profile_examples = 10
+
+ # Run specs in random order to surface order dependencies. If you find an
+ # order dependency and want to debug it, you can fix the order by providing
+ # the seed, which is printed after each run.
+ # --seed 1234
+ config.order = :random
+
+ # Seed global randomization in this process using the `--seed` CLI option.
+ # Setting this allows you to use `--seed` to deterministically reproduce
+ # test failures related to randomization by passing the same `--seed` value
+ # as the one that triggered the failure.
+ Kernel.srand config.seed
+=end
+end
diff --git a/spec/support/factory_bot.rb b/spec/support/factory_bot.rb
new file mode 100644
index 0000000..195f20a
--- /dev/null
+++ b/spec/support/factory_bot.rb
@@ -0,0 +1,3 @@
+RSpec.configure do |config|
+ config.include FactoryBot::Syntax::Methods
+end
\ No newline at end of file