auctify 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +229 -0
- data/Rakefile +34 -0
- data/app/assets/config/auctify_manifest.js +1 -0
- data/app/assets/javascripts/folio/console/main_app.coffee +1 -0
- data/app/assets/stylesheets/auctify/application.css +15 -0
- data/app/assets/stylesheets/auctify/bidder_registrations.css +4 -0
- data/app/assets/stylesheets/auctify/bids.css +4 -0
- data/app/assets/stylesheets/auctify/sales.css +4 -0
- data/app/assets/stylesheets/auctify/sales_packs.css +4 -0
- data/app/assets/stylesheets/folio/console/_main_app.sass +1 -0
- data/app/assets/stylesheets/scaffold.css +80 -0
- data/app/cells/folio/console/atoms/previews/main_app.coffee +1 -0
- data/app/concerns/auctify/behavior/base.rb +20 -0
- data/app/concerns/auctify/behavior/buyer.rb +16 -0
- data/app/concerns/auctify/behavior/item.rb +27 -0
- data/app/concerns/auctify/behavior/seller.rb +36 -0
- data/app/concerns/auctify/sale/auction_callbacks.rb +44 -0
- data/app/controllers/auctify/api/v1/auctions_controller.rb +61 -0
- data/app/controllers/auctify/api/v1/base_controller.rb +22 -0
- data/app/controllers/auctify/api/v1/console/bids_controller.rb +37 -0
- data/app/controllers/auctify/application_controller.rb +8 -0
- data/app/controllers/auctify/bidder_registrations_controller.rb +65 -0
- data/app/controllers/auctify/bids_controller.rb +64 -0
- data/app/controllers/auctify/sales_controller.rb +75 -0
- data/app/controllers/auctify/sales_packs_controller.rb +75 -0
- data/app/helpers/auctify/application_helper.rb +10 -0
- data/app/helpers/auctify/bidder_registrations_helper.rb +6 -0
- data/app/helpers/auctify/bids_helper.rb +6 -0
- data/app/helpers/auctify/sales_helper.rb +15 -0
- data/app/helpers/auctify/sales_packs_helper.rb +6 -0
- data/app/jobs/auctify/application_job.rb +6 -0
- data/app/jobs/auctify/bidding_closer_job.rb +23 -0
- data/app/jobs/auctify/bidding_is_close_to_end_notifier_job.rb +29 -0
- data/app/mailers/auctify/application_mailer.rb +8 -0
- data/app/models/auctify/application_record.rb +14 -0
- data/app/models/auctify/behaviors.rb +27 -0
- data/app/models/auctify/bid.rb +72 -0
- data/app/models/auctify/bidder_registration.rb +84 -0
- data/app/models/auctify/sale/auction.rb +404 -0
- data/app/models/auctify/sale/base.rb +210 -0
- data/app/models/auctify/sale/retail.rb +97 -0
- data/app/models/auctify/sales_pack.rb +101 -0
- data/app/serializers/auctify/sale/auction_serializer.rb +46 -0
- data/app/services/auctify/bids_appender.rb +241 -0
- data/app/services/auctify/service_base.rb +78 -0
- data/app/types/auctify/minimal_bids_ladder_type.rb +77 -0
- data/app/views/auctify/api/v1/console/bids/index.html.erb +1 -0
- data/app/views/auctify/api/v1/console/bids/show.html.erb +1 -0
- data/app/views/auctify/bidder_registrations/_form.html.erb +27 -0
- data/app/views/auctify/bidder_registrations/edit.html.erb +6 -0
- data/app/views/auctify/bidder_registrations/index.html.erb +33 -0
- data/app/views/auctify/bidder_registrations/new.html.erb +5 -0
- data/app/views/auctify/bidder_registrations/show.html.erb +24 -0
- data/app/views/auctify/bids/_form.html.erb +32 -0
- data/app/views/auctify/bids/edit.html.erb +6 -0
- data/app/views/auctify/bids/index.html.erb +31 -0
- data/app/views/auctify/bids/new.html.erb +5 -0
- data/app/views/auctify/bids/show.html.erb +19 -0
- data/app/views/auctify/sales/_form.html.erb +32 -0
- data/app/views/auctify/sales/edit.html.erb +6 -0
- data/app/views/auctify/sales/index.html.erb +36 -0
- data/app/views/auctify/sales/new.html.erb +5 -0
- data/app/views/auctify/sales/show.html.erb +19 -0
- data/app/views/auctify/sales_packs/_form.html.erb +57 -0
- data/app/views/auctify/sales_packs/edit.html.erb +6 -0
- data/app/views/auctify/sales_packs/index.html.erb +40 -0
- data/app/views/auctify/sales_packs/new.html.erb +5 -0
- data/app/views/auctify/sales_packs/show.html.erb +44 -0
- data/app/views/layouts/auctify/application.html.erb +15 -0
- data/config/locales/auctify.cs.yml +121 -0
- data/config/locales/default.cs.yml +220 -0
- data/config/locales/default.en.yml +215 -0
- data/config/routes.rb +23 -0
- data/db/migrate/20210212163000_create_auctify_sales.rb +16 -0
- data/db/migrate/20210216151350_add_type_to_auctify_sales.rb +7 -0
- data/db/migrate/20210217101246_add_state_to_auctify_sales.rb +7 -0
- data/db/migrate/20210217110819_add_prices_to_auctify_sales.rb +9 -0
- data/db/migrate/20210222103031_add_published_at_to_auctify_sale.rb +7 -0
- data/db/migrate/20210223090240_create_auctify_bidder_registrations.rb +15 -0
- data/db/migrate/20210223152139_create_auctify_bids.rb +13 -0
- data/db/migrate/20210225103227_add_bid_steps_ladder_to_auctify_sales.rb +7 -0
- data/db/migrate/20210412114319_add_reserve_price_to_auctify_sales.rb +7 -0
- data/db/migrate/20210416140603_create_auctify_sales_packs.rb +17 -0
- data/db/migrate/20210416141111_add_pack_reference_to_auctify_sales.rb +7 -0
- data/db/migrate/20210419075003_add_ends_at_to_auctify_sales.rb +7 -0
- data/db/migrate/20210419082800_add_position_to_auctify_sales.rb +8 -0
- data/db/migrate/20210419083321_add_indexes.rb +12 -0
- data/db/migrate/20210421093652_add_number_to_auctify_sales.rb +7 -0
- data/db/migrate/20210423071326_removing_polymorphic_assoc_sale_to_item.rb +7 -0
- data/db/migrate/20210423071534_add_currently_ends_at_to_auctify_sales.rb +7 -0
- data/db/migrate/20210427121353_change_column_published_on_auctify_sale.rb +15 -0
- data/db/migrate/20210427122315_add_featured_to_auctify_sales.rb +7 -0
- data/db/migrate/20210428045651_add_sales_bool_indices.rb +9 -0
- data/db/migrate/20210428064615_allow_seller_to_be_nil.rb +13 -0
- data/db/migrate/20210429051508_add_sales_pack_start_and_end.rb +15 -0
- data/db/migrate/20210430080419_add_sales_slug.rb +11 -0
- data/db/migrate/20210503095704_add_cancelled_to_auctify_bids.rb +7 -0
- data/db/migrate/20210504072041_add_commission_and_contract_to_auctify_sales.rb +8 -0
- data/db/migrate/20210506064100_add_winner_to_auctify_sales.rb +7 -0
- data/db/migrate/20210506072438_add_bids_count_to_auctify_sales.rb +7 -0
- data/db/migrate/20210507085845_rename_bids_count_to_applied_bids_count.rb +7 -0
- data/db/migrate/20210510063146_add_dont_confirm_bids_to_registrations.rb +7 -0
- data/db/migrate/20210511081556_add_index_on_cancelled_on.rb +7 -0
- data/db/migrate/20210512162942_add_sold_at_to_auctify_sales.rb +7 -0
- data/db/migrate/20210607113440_add_commission_in_percent_to_sales_packs.rb +7 -0
- data/db/migrate/20210617062509_add_index_to_auctify_sales_currently_ends_at.rb +7 -0
- data/db/migrate/20210625125732_add_current_winner_to_auctify_sales.rb +7 -0
- data/lib/auctify.rb +7 -0
- data/lib/auctify/configuration.rb +43 -0
- data/lib/auctify/engine.rb +25 -0
- data/lib/auctify/version.rb +5 -0
- data/lib/tasks/auctify_tasks.rake +23 -0
- metadata +442 -0
@@ -0,0 +1,241 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Auctify
|
4
|
+
class BidsAppender < ServiceBase
|
5
|
+
attr_reader :auction, :bid
|
6
|
+
|
7
|
+
def initialize(auction:, bid: nil)
|
8
|
+
super()
|
9
|
+
|
10
|
+
@auction = auction
|
11
|
+
@bid = bid
|
12
|
+
end
|
13
|
+
|
14
|
+
def build_result
|
15
|
+
self.current_price = auction.current_price
|
16
|
+
@updated_win_bid = nil
|
17
|
+
|
18
|
+
if bid
|
19
|
+
set_price_for_limit_bid
|
20
|
+
|
21
|
+
if approved_bid?
|
22
|
+
solve_winner(winning_bid, bid)
|
23
|
+
append_bids!
|
24
|
+
update_auction! unless failed?
|
25
|
+
else
|
26
|
+
fail!
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
@result = result_struct
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
attr_accessor :current_price
|
35
|
+
|
36
|
+
def result_struct
|
37
|
+
OpenStruct.new(
|
38
|
+
current_price: current_price,
|
39
|
+
current_minimal_bid: new_current_minimal_bid,
|
40
|
+
winning_bid: winning_bid,
|
41
|
+
winner: winner,
|
42
|
+
won_price: (overcame_reserve_price? ? current_price : nil)
|
43
|
+
)
|
44
|
+
end
|
45
|
+
|
46
|
+
def set_price_for_limit_bid
|
47
|
+
return unless bid.price.blank? && bid.with_limit?
|
48
|
+
|
49
|
+
if changing_own_limit?
|
50
|
+
bid.price = winning_bid.price
|
51
|
+
elsif bid.max_price <= new_current_minimal_bid
|
52
|
+
bid.price = bid.max_price
|
53
|
+
else
|
54
|
+
bid.price = new_current_minimal_bid
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def approved_bid?
|
59
|
+
@approved_bid ||= begin
|
60
|
+
check_bidder
|
61
|
+
changing_own_limit? ? check_max_price_increasing : check_price_minimum
|
62
|
+
check_same_bidder
|
63
|
+
check_auction_state
|
64
|
+
|
65
|
+
errors.add_from_hash(bid.errors.to_hash)
|
66
|
+
|
67
|
+
errors.empty?
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def append_bids!
|
72
|
+
fail! unless bid.save
|
73
|
+
|
74
|
+
# saving last, so it will still be winning, even if both have same price
|
75
|
+
if @updated_win_bid.present?
|
76
|
+
fail! unless @updated_win_bid.save
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def update_auction!
|
81
|
+
@winning_bid = nil
|
82
|
+
@bids = bids.reload
|
83
|
+
|
84
|
+
|
85
|
+
fail! unless auction.succesfull_bid!(price: new_current_price,
|
86
|
+
winner: winner,
|
87
|
+
time: bid.reload.created_at)
|
88
|
+
self.current_price = auction.current_price
|
89
|
+
end
|
90
|
+
|
91
|
+
def new_current_price
|
92
|
+
winning_bid.price
|
93
|
+
end
|
94
|
+
|
95
|
+
def new_current_minimal_bid
|
96
|
+
return current_price if first_bid?
|
97
|
+
|
98
|
+
increase_price(current_price)
|
99
|
+
end
|
100
|
+
|
101
|
+
def winning_bid
|
102
|
+
@winning_bid ||= bids.first
|
103
|
+
end
|
104
|
+
|
105
|
+
def winner
|
106
|
+
(overcame_reserve_price? ? winning_bid.bidder : nil)
|
107
|
+
end
|
108
|
+
|
109
|
+
def overcame_reserve_price?
|
110
|
+
return false if winning_bid.blank?
|
111
|
+
return false if auction.reserve_price.present? && auction.current_price < auction.reserve_price
|
112
|
+
|
113
|
+
true
|
114
|
+
end
|
115
|
+
|
116
|
+
def first_bid?
|
117
|
+
bids.empty?
|
118
|
+
end
|
119
|
+
|
120
|
+
def bids
|
121
|
+
@bids ||= auction.ordered_applied_bids
|
122
|
+
end
|
123
|
+
|
124
|
+
def check_bidder
|
125
|
+
unless bid.registration&.auction == auction
|
126
|
+
bid.errors.add(:auction, :bidder_is_not_registered_for_this_auction)
|
127
|
+
end
|
128
|
+
unless auction.bidding_allowed_for?(bid.bidder)
|
129
|
+
bid.errors.add(:bidder, :you_are_not_allowed_to_bid)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def check_max_price_increasing
|
134
|
+
bid.errors.add(:bidder, :you_can_only_increase_your_max_price) if bid.max_price.present? && (bid.max_price.to_i <= winning_bid.max_price.to_i)
|
135
|
+
end
|
136
|
+
|
137
|
+
def check_price_minimum
|
138
|
+
if bid.price < new_current_minimal_bid
|
139
|
+
att = bid.with_limit? ? :max_price : :price
|
140
|
+
bid.errors.add(att,
|
141
|
+
:price_is_bellow_minimal_bid,
|
142
|
+
minimal_bid: ActionController::Base.helpers.number_to_currency(new_current_minimal_bid, precision: 0))
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def check_same_bidder
|
147
|
+
return if overbidding_yourself_allowed? || changing_own_limit?
|
148
|
+
|
149
|
+
if winning_bid.present? && same_bidder?(winning_bid, bid)
|
150
|
+
bid.errors.add(:bidder, :you_cannot_overbid_yourself)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def check_auction_state
|
155
|
+
# comparing time with seconds precision, use `.to_i`
|
156
|
+
return if auction.in_sale? && bid.created_at.to_i <= auction.currently_ends_at.to_i
|
157
|
+
|
158
|
+
bid.errors.add(:auction, :auction_is_not_accepting_bids_now)
|
159
|
+
end
|
160
|
+
|
161
|
+
def solve_winner(winning_bid, new_bid)
|
162
|
+
return if winning_bid.blank? || changing_own_limit?
|
163
|
+
|
164
|
+
solve_limits_fight(winning_bid, new_bid) if new_bid.with_limit? && winning_bid.with_limit?
|
165
|
+
increase_bid_price(winning_bid, new_bid) if new_bid.with_limit? && !winning_bid.with_limit?
|
166
|
+
duplicate_increased_win_bid(winning_bid, new_bid) if !new_bid.with_limit? && winning_bid.with_limit?
|
167
|
+
# do nothing, all is solved already if !bid.with_limit? && !winning_bid.with_limit?
|
168
|
+
end
|
169
|
+
|
170
|
+
def solve_limits_fight(winning_bid, new_bid)
|
171
|
+
if winning_bid.max_price < new_bid.max_price
|
172
|
+
update_winning_bid_to(winning_bid.max_price)
|
173
|
+
new_bid.price = [new_bid.price, increase_price_to(overcome: winning_bid.max_price, ceil: new_bid.max_price)].max
|
174
|
+
else
|
175
|
+
new_bid.price = new_bid.max_price
|
176
|
+
update_winning_bid_to(increase_price_to(overcome: new_bid.max_price, ceil: winning_bid.max_price))
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
def increase_bid_price(winning_bid, new_bid)
|
181
|
+
if winning_bid.price < new_bid.max_price
|
182
|
+
new_bid.price = [new_bid.price, increase_price_to(overcome: winning_bid.price, ceil: new_bid.max_price)].max
|
183
|
+
else
|
184
|
+
new_bid.price = new_bid.max_price
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
def duplicate_increased_win_bid(winning_bid, new_bid)
|
189
|
+
if winning_bid.max_price < new_bid.price
|
190
|
+
update_winning_bid_to(winning_bid.max_price)
|
191
|
+
elsif overbidding_yourself_allowed? && same_bidder?(winning_bid, new_bid)
|
192
|
+
update_winning_bid_to(new_bid.price, force_if_equal: true)
|
193
|
+
else
|
194
|
+
update_winning_bid_to(increase_price_to(overcome: new_bid.price, ceil: winning_bid.max_price))
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
def update_winning_bid_to(price, force_if_equal: false)
|
199
|
+
return if price < winning_bid.price
|
200
|
+
return if price == winning_bid.price && !force_if_equal
|
201
|
+
|
202
|
+
@updated_win_bid = winning_bid.dup
|
203
|
+
@updated_win_bid.price = [price, winning_bid.max_price].min
|
204
|
+
end
|
205
|
+
|
206
|
+
def increase_price(price)
|
207
|
+
return price + 1 if bid_steps_ladder.blank?
|
208
|
+
|
209
|
+
_range, increase_step = bid_steps_ladder.detect { |range, step| range.cover?(price) }
|
210
|
+
price + increase_step
|
211
|
+
end
|
212
|
+
|
213
|
+
def increase_price_to(overcome:, ceil:)
|
214
|
+
return ceil if overcome == ceil
|
215
|
+
raise ":ceil is lower than :overcome" if ceil < overcome
|
216
|
+
|
217
|
+
running_price = current_price
|
218
|
+
while running_price <= overcome
|
219
|
+
running_price = increase_price(running_price)
|
220
|
+
end
|
221
|
+
|
222
|
+
[running_price, ceil].min
|
223
|
+
end
|
224
|
+
|
225
|
+
def bid_steps_ladder
|
226
|
+
@bid_steps_ladder ||= auction.bid_steps_ladder
|
227
|
+
end
|
228
|
+
|
229
|
+
def changing_own_limit?
|
230
|
+
bid.with_limit? && winning_bid.present? && (winning_bid.bidder == bid.bidder)
|
231
|
+
end
|
232
|
+
|
233
|
+
def overbidding_yourself_allowed?
|
234
|
+
Auctify.configuration.restrict_overbidding_yourself_to_max_price_increasing == false
|
235
|
+
end
|
236
|
+
|
237
|
+
def same_bidder?(winning_bid, new_bid)
|
238
|
+
winning_bid.bidder == new_bid.bidder
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Base (surprise!) for Auctify services.
|
4
|
+
# Ascendants shoudl implement own methods `initialize` and `build_result`(which assigns to `@result`).
|
5
|
+
# In succesfull flow, call `srv = SomeService.call(*args)`
|
6
|
+
# will return itself with result accessible by `srv.result`, errors by`srv.errors` and `srv.success? => true`
|
7
|
+
# In failed call (calling `fail!` when building result),
|
8
|
+
# there can be still some result, even errors and `srv.success? => false` (or `srv.failed? => true`)
|
9
|
+
# Failing and errors are independent!
|
10
|
+
# Errors are like ActiveRecord errors, use `errors.add(:key, "message)`
|
11
|
+
# You can pass some information through `srv.flashes` hash
|
12
|
+
|
13
|
+
module Auctify
|
14
|
+
class ServiceErrors < Hash
|
15
|
+
def add(key, value, _opts = {})
|
16
|
+
self[key] ||= []
|
17
|
+
self[key] << value
|
18
|
+
self[key].uniq!
|
19
|
+
end
|
20
|
+
|
21
|
+
def add_from_hash(errors_hash)
|
22
|
+
errors_hash.each do |key, values|
|
23
|
+
values.each { |value| add key, value }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def full_messages
|
28
|
+
f_msgs = []
|
29
|
+
each_one { |field, message| f_msgs << "#{field}: #{message}" }
|
30
|
+
f_msgs
|
31
|
+
end
|
32
|
+
|
33
|
+
def each_one
|
34
|
+
each_pair do |field, messages|
|
35
|
+
messages.each { |message| yield field, message }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class ServiceBase
|
41
|
+
attr_reader :result, :errors, :flashes
|
42
|
+
|
43
|
+
def self.call(*args, **keyword_args)
|
44
|
+
if args.blank?
|
45
|
+
new(**keyword_args).call
|
46
|
+
elsif keyword_args.blank?
|
47
|
+
new(*args).call
|
48
|
+
else
|
49
|
+
new(*args, **keyword_args).call
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def call
|
54
|
+
build_result
|
55
|
+
self # always returnning service itself, to get to `errors`, `result`
|
56
|
+
end
|
57
|
+
|
58
|
+
def initialize
|
59
|
+
@result = nil
|
60
|
+
@failed = false
|
61
|
+
@flashes = {}
|
62
|
+
@errors = ServiceErrors.new
|
63
|
+
end
|
64
|
+
|
65
|
+
def fail!
|
66
|
+
@failed = true
|
67
|
+
end
|
68
|
+
|
69
|
+
def success?
|
70
|
+
!failure?
|
71
|
+
end
|
72
|
+
|
73
|
+
def failure?
|
74
|
+
@failed
|
75
|
+
end
|
76
|
+
alias failed? failure?
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Auctify
|
4
|
+
# in ruby/rails we expecting default format as hash with ranges { (0...1_000) => 200, (1_000..) => 500 }
|
5
|
+
# but acceppting (in setter) also hash with numbers or strings
|
6
|
+
# in DB we store JSON with minimal prices casted to nice strings : { "0" => 200, "1_000" => 500 }
|
7
|
+
class MinimalBidsLadderType < ActiveRecord::Type::Value
|
8
|
+
def type
|
9
|
+
:jsonb
|
10
|
+
end
|
11
|
+
|
12
|
+
def cast(value) # setter
|
13
|
+
# hash is expected as value
|
14
|
+
value = JSON.parse(value) if value.is_a?(String)
|
15
|
+
if value.blank?
|
16
|
+
{}
|
17
|
+
else
|
18
|
+
case value.keys.first
|
19
|
+
when Range # { (0...1_000) => 200, (1_000..) => 500 }
|
20
|
+
cast_from_ranges(value)
|
21
|
+
when String # { "0" => 200, "1_000" => 500 }
|
22
|
+
cast_from_min_strings(value)
|
23
|
+
when Numeric # { 0 => 200, 1000 => 500 }
|
24
|
+
cast_from_min_numbers(value)
|
25
|
+
else
|
26
|
+
raise "Uncovered ladder key type `#{value.keys.first}`"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def deserialize(db_value) # getter, from db data to ruby raw object
|
32
|
+
return {} if db_value.blank?
|
33
|
+
|
34
|
+
cast(ActiveSupport::JSON.decode(db_value))
|
35
|
+
end
|
36
|
+
|
37
|
+
def serialize(value) # modifier to store in db
|
38
|
+
return nil if value.nil? # so You are able to do `KLASS.where(bid_steps_ladder: nil)`
|
39
|
+
|
40
|
+
# value should be Hash with ranges as keys
|
41
|
+
result = {}
|
42
|
+
|
43
|
+
value.each_pair do |k_range, v|
|
44
|
+
min_price_string = ActiveSupport::NumberHelper.number_to_delimited(k_range.first, delimiter: "_")
|
45
|
+
result[min_price_string] = v
|
46
|
+
end
|
47
|
+
|
48
|
+
ActiveSupport::JSON.encode(result)
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
def cast_from_ranges(hash)
|
53
|
+
hash # desired format
|
54
|
+
end
|
55
|
+
|
56
|
+
def cast_from_min_strings(hash)
|
57
|
+
result = {}
|
58
|
+
hash.each_pair { |k, v| result[k.to_i] = v }
|
59
|
+
|
60
|
+
cast_from_min_numbers(result)
|
61
|
+
end
|
62
|
+
|
63
|
+
def cast_from_min_numbers(hash)
|
64
|
+
previous_step = nil
|
65
|
+
|
66
|
+
result = hash.to_a.sort.each_with_object({}) do |step, rslt|
|
67
|
+
unless previous_step.nil?
|
68
|
+
rslt[Range.new(previous_step.first, step.first, true)] = previous_step.last
|
69
|
+
end
|
70
|
+
previous_step = step
|
71
|
+
end
|
72
|
+
result[Range.new(previous_step.first, nil, false)] = previous_step.last
|
73
|
+
|
74
|
+
result
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
<p>Bids.count: <%= @bids.count %> </p>
|
@@ -0,0 +1 @@
|
|
1
|
+
<p>BID: <%= @bid.to_json %></p>
|
@@ -0,0 +1,27 @@
|
|
1
|
+
<%= form_with(model: bidder_registration, local: true) do |form| %>
|
2
|
+
<% if bidder_registration.errors.any? %>
|
3
|
+
<div id="error_explanation">
|
4
|
+
<h2><%= pluralize(bidder_registration.errors.count, "error") %> prohibited this bidder_registration from being saved:</h2>
|
5
|
+
|
6
|
+
<ul>
|
7
|
+
<% bidder_registration.errors.full_messages.each do |message| %>
|
8
|
+
<li><%= message %></li>
|
9
|
+
<% end %>
|
10
|
+
</ul>
|
11
|
+
</div>
|
12
|
+
<% end %>
|
13
|
+
|
14
|
+
<div class="field">
|
15
|
+
<%= form.label :bidder_id %>
|
16
|
+
<%= form.text_field :bidder_id %>
|
17
|
+
</div>
|
18
|
+
|
19
|
+
<div class="field">
|
20
|
+
<%= form.label :auction_id %>
|
21
|
+
<%= form.text_field :auction_id %>
|
22
|
+
</div>
|
23
|
+
|
24
|
+
<div class="actions">
|
25
|
+
<%= form.submit %>
|
26
|
+
</div>
|
27
|
+
<% end %>
|
@@ -0,0 +1,33 @@
|
|
1
|
+
<p id="notice"><%= notice %></p>
|
2
|
+
|
3
|
+
<h1>Bidder Registrations</h1>
|
4
|
+
|
5
|
+
<table>
|
6
|
+
<thead>
|
7
|
+
<tr>
|
8
|
+
<th>Bidder</th>
|
9
|
+
<th>Auction</th>
|
10
|
+
<th>Aasm state</th>
|
11
|
+
<th>Handled at</th>
|
12
|
+
<th colspan="3"></th>
|
13
|
+
</tr>
|
14
|
+
</thead>
|
15
|
+
|
16
|
+
<tbody>
|
17
|
+
<% @bidder_registrations.each do |bidder_registration| %>
|
18
|
+
<tr>
|
19
|
+
<td><%= bidder_registration.bidder_id %></td>
|
20
|
+
<td><%= bidder_registration.auction_id %></td>
|
21
|
+
<td><%= bidder_registration.aasm_state %></td>
|
22
|
+
<td><%= bidder_registration.handled_at %></td>
|
23
|
+
<td><%= link_to 'Show', bidder_registration %></td>
|
24
|
+
<td><%= link_to 'Edit', edit_auctify_bidder_registration_path(bidder_registration) %></td>
|
25
|
+
<td><%= link_to 'Destroy', bidder_registration, method: :delete, data: { confirm: 'Are you sure?' } %></td>
|
26
|
+
</tr>
|
27
|
+
<% end %>
|
28
|
+
</tbody>
|
29
|
+
</table>
|
30
|
+
|
31
|
+
<br>
|
32
|
+
|
33
|
+
<%= link_to 'New Bidder Registration', new_auctify_bidder_registration_path %>
|