Reputation: 63
Architecture:
I have two Models: Port and App
belongs_to :port,
class_name: 'Port',
foreign_key: :port_id,
required: true
What I need:
The Port are already existing in a DB. In the create-form from the app I have list with all available ports: <%= f.association :port, collection: Port.available_ports, include_blank: false %>
.
With this approach, the value of a select Item is equal to the number of the port. But what I need is, that the value isn't the number but the id of the port.
What I tried:
I tried to solve my problem by adding following to the f.association: value_method: Port.available_ports
. But that didn't work: no implicit conversion of Array into String
.
My Question:
Am I on the right track or should I approach my problem in a different way? If so, what would you recommend me to do?
EDIT:
AppController
class JBossAppsController < ApplicationController
before_action :load_app,
only: %i[ show destroy ]
def index
@apps = JBossApp.all
end
def show
end
def new
@app = JBossApp.new
end
def create
@app = JBossApp.new(app_params)
if @app.save!
redirect_to root_path
else
render 'j_boss_apps/new'
end
end
def destroy
end
protected
def load_app
@app = JBossApp.find_by(params[:id])
end
private
def app_params
params.require(:j_boss_app).permit(
params.require(:j_boss_app).permit(
:id, :group_id, :port_id, :environment, :status, :fault_count, :request_count, :response_count
)
)
end
end
Port
class Port < ApplicationRecord
# Helper
def self.available_ports
unavailable_ports = Port.order(number: :asc).pluck(:number)
first_port = 8080
last_port = 65080
step = 100
all_ports = (first_port..last_port).step(step).collect { |n| n }
all_ports - unavailable_ports
end
# Relations
has_many :apps,
class_name: 'JBossApp',
foreign_key: :port_id,
dependent: :destroy
has_one :int_app, ->{
where(port_id: id, environment: :int)
}
has_one :tst_app, ->{
where(port_id: id, environment: :tst)
}
has_one :prd_app, ->{
where(port_id: id, environment: :prd)
}
has_one :dmz_tst_app, ->{
where(port_id: id, environment: :dmz_tst)
}
has_one :dmz_prd_app, ->{
where(port_id: id, environment: :dmz_prd)
}
# Validations
validates :number,
numericality: {
greater_than: 0,
only_integer: true
},
presence: true
end
Upvotes: 1
Views: 176
Reputation: 3083
As per my understanding to the Question, you would EITHER need insert the Port number entered by the user to the Ports
table and associate it to the app while saving it OR seed all the Ports to Ports
table beforehand and then find the ports which do not have their corresponding/associated apps. For that you can change your available_ports
method as follows:
def self.available_ports
Port.includes(:apps).where(apps: {id: nil}).order(ports:{number: :asc})
# Or similar results can be achieved by the following in Rails 5
# Port.left_outer_joins(:apps).where(apps: {id: nil}).order(ports:{number: :asc})
end
Now you can use your normal form helper method collection_select
as:
<%= f.collection_select :port_id, Port.available_ports, :id, :number, {prompt: "Select a port"}, {class: "form-control"} %>
Please let me know if you need further help with any of the proposed solutions.
Upvotes: 2
Reputation: 869
def self.available_ports
unavailable_ports = Port.order(number: :asc).pluck(:number)
first_port = 8080
last_port = 65080
step = 100
all_ports = (first_port..last_port).step(step).collect { |n| n }
all_ports = all_ports - unavailable_ports
all_ports = Port.where(number: all_ports)
end
in views
<%= f.collection_select :port_id, Port.available_ports, :id, :number, {prompt: "Select a port"}, {class: "form-control"} %>
Upvotes: 1