I'm writing a minimal rails app as a way to learn a bit more about rails.
The app is going to track stuff (books to start with). So I need a "Location" to identify where a given item is.
create_table "locations", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8", force: :cascade do |t|
t.bigint "located_at"
t.integer "sort"
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["name"], name: "index_locations_on_name", unique: true
end
"sort" is irrelevant for this question.
In the form to create a new location the user gets to enter a name (say "X") and optionally say where "X" itself is located (say "Y").
So to create the location in the controller I will do something like
#location = Location.new(location_params)
But I need to turn "Y" into a location.id for "Y". In addition, if "Y" doesn't exist, I need to raise some kind of error.
What I have now is a virtual variable located_at_text and in the controller I do this:
modified_location_params = location_params
located_at_text = modified_location_params["located_at_text"]
located = nil
located = Location.find_by_name(located_at_text) unless located_at_text.nil? or located_at_text.strip.empty?
modified_location_params["located_at"] = located.nil? ? 0 : located.id
#location = Location.new(modified_location_params)
(I don't know why I can't fiddle with location_params ... but that would be a different question ... I'll worry about that once I know where I'm best off putting my code. Also my app does't mind a 0 for location.id).
Various tutorials suggest that some of this logic should be in the model, but various examples also do similar work in the controller.
Which is the "rails way"?
I made some assumptions because not all of the example code made sense to me, but the basic outline would be:
Use ActiveRecord callbacks instead of checking input in the controller
Make sure relationships are correctly set so you can build objects from them
Use first_or_create to clean up the parent lookup/creation
In my experience, manipulating input params is a code smell. Sometimes you have to do it, but usually it's telling you something is incorrectly designed.
Some of the param manipulation you can accomplish with ActiveRecord callbacks. One note, it looks like located_at_text is not part of a model, you will need to check this in the controller or add an attr_reader to the model (but I don't think you should, this is another code smell).
class Location < ApplicationRecord
validates :name, presence: true
end
Set up the relationships in the model. This will let you build out the related records.
class Location < ApplicationRecord
belongs_to :located_at
validates :name, presence: true
end
class LocatedAt < ApplicationRecord
has_many :locations
end
Normally, this setup would carry the ID for LocatedAt in the url (e.g. .../#{located_at_id}/location/new), but this isn't required if you are going to explicitly pass a located_at id which it looks like you are.
This is outside the scope of your question, but if you are breaking from the url pattern where parent ids are passed in the url, it would be very useful for your users to give them some kind of auto-complete search or a UI element that lets them know explicitly that they are creating a new LocatedAt record. It will also make your life easier because you can pass in the id resulting from the search or a trigger param to create the new record first.
Finally, use first_or_create to check that located_at exists (and if not create it). This assumes that there is only one field on LocatedAt and it is called name.
Note: I changed the model from Location since it didn't make sense to me that Location was going to look for itself in the example.
#located_at = LocatedAt
.where(name: params[:form_name][:located_at_text])
.first_or_create
#location = #located_at.locations.new(location_params)
I want to create an object from data API. At first, the user will have to enter his immaticulation number. Once registration typed him return the following data in the view show
vehicles_controller:
#vehicle = Vehicle.new
#vehicles = []
vehicle_number = params['immatricule'].capitalize
vehicles.each do |vehicule|
data_api = CarRegistrationFrance.Lookup(#{"vehicle_number"},"username","password")
Api_data response:
=> {"Description"=>"RENAULT CLIO IV", "RegistrationYear"=>"2017", "CarMake"=>{"CurrentTextValue"=>"RENAULT"}, "CarModel"=>{"CurrentTextValue"=>"CLIO IV"}, "EngineSize"=>{"CurrentTextValue"=>"4"}, "FuelType"=>{"CurrentTextValue"=>"DIESEL"}, "MakeDescription"=>{"CurrentTextValue"=>"RENAULT"}, "ModelDescription"=>{"CurrentTextValue"=>"CLIO IV"}, "Immobiliser"=>{"CurrentTextValue"=>""}, "IndicativeValue"=>{"CurrentTextValue"=>0}, "DriverSide"=>{"CurrentTextValue"=>""}, "BodyStyle"=>{"CurrentTextValue"=>"BERLINE 5 PORTES"}, "RegistrationDate"=>"2017-10-30","ExtendedData"=>{"anneeSortie"=>"2017", "boiteDeVitesse"=>"", "carburantVersion"=>"D", "carrosserieVersion"=>"", "classeSra"=>"K", "libVersion"=>"1.5 DCI 90 EDITION ONE EDC", "libelleModele"=>"CLIO IV", "marque"=>"RE", "modele"=>"88", "produit"=>"", "puissance"=>"4", "version"=>"", "cleCarrosserie"=>"", "groupeSra"=>"30", "nbPlace"=>"5", "datePremiereMiseCirculation"=>"30102017", "questionBatterie"=>"", "electrique"=>"", "genre"=>"", "typeVehicule"=>"", "numSerieMoteur"=>"VF15RBJ0D58888591", "valeurANeufSRA"=>"", "niveauRisqueVol"=>"", "protectionConstructeur"=>"", "puissanceDyn"=>"", "segmentVeh"=>""}}
#vehicles << data_api
vehicle.description => data_api["Description"]
vehicle.annee => data_api["RegistrationYear"]
def vehicle_params
params.require(:voiture).permit(:immatricule, :description, :annee)
end
Once the user enters his registration numbers I want to join
data_api ["Description"] << vehicle ["description"]
attach the data to the attribute of the vehicle object
data_api ["RegistrationYear"] << vehicle ["year"]
/user[:id]/vehicle/new its an example
<%= form_tag("/vehicle", method: "post") do %>
<%= label_tag(:immatricule, "") %>
<%= text_field_tag(:immatricule) %>
<%= submit_tag("create") %>
/user[:id]/vehicle/show it an example
<%= #vehicle.api_data["Description"] %>
<%= #vehicle.api_data["RegistrationYear"] %>
enter your immatricule number
post immatricule of vehicle user to api_data
create objet vehicle from response api_data
render show view
I've tried several operations but I can not find a solution.
So this sort of feels like you coming from a background without being CRUD (Create, Read, Update, Delete) aware ... based on your not listing the controllers etc that you are working to achieve this stuff (crucial to any response to answer your question).
Based on the status of the question 2/22 # 06:18am -600 here is some quick notes ...
You are going to want to do this walk-thru for getting started - RailsGuides.
Second, I think from your URL you listed - you have a nested route & are using a matching nested model. The answer changes which controller/action/routing I am suggesting - so make sure to clarify if you aren't before you try to understand this answer ...
All of these are guess work as there are details of your implementation we are missing (route.rb file, if this is a script you as admin are calling or each user is running the update themselves, if this stuff works at all).
Once finished the question you meant to ask should be ...
"Which controller & controller action should I use to update the registration information?" (Commonly shown as controller#action)
The CRUD answer is the update (rails calls it Edit to display view & update to execute).
The reason your question throwing people off is that the Rails Way (CRUD) already has basic code for a controller and action for that controller action to update the field. You shouldn't have to create an object yourself - all data objects that are permanent should be Rails model objects.
In your case Vehicle model in the app/models/vehicle.rb would be my guess. You might choose to nest the route for that in the config/routes.rb file as ...
Rails.application.routes.draw do
resources :users do
resources :vehicle
...
Which would give you a route/url of "/{:user_id}/{:vehicle_id}/{controller action}".
This assumes ...
A user coming to your application on the web or network
They sign up or in
Display the info from the API (I'm not sure if your are looking to feed this info back to that API & that is why you need the object - but assuming you are not)
After the user#show controller pulls vehicle model pulls the info from the API
Your controller will redirect them to the CRUD read action (Rails calls this Show). It's also already coded in the controller action for update.
In your case this would likely be ... User#show, which would have accepts_nested_attributes_for :RegistrationYear and maybe the :Description in the User model, referring to the fields in the Vehicle model you already have working.
For the other case ... you are simply wrapping another API which is getting updated and your rails application isn't holding anything ...
You need a user entity or authentication ... maybe you are getting an omni_auth or other security login token from the API you referenced ... so your steps ...
Either login in handled or you create/ login the user
Your User#Show should either have a link to the Vehicle#Update or the User#Show view should include that form with field with the user & vehicle id's ... which are filled in already as the the controller executed that API query pull all the information & have the two fields to be updated.
The submit button should in a normal rails app be a link to another controller#action which would just execute the API's update interface instead of stashing the info into your own db ... though you could easily put that API interface call in the controller#action responsible for showing the form.
I suggest you do a standard object file if you are just wrapping the API with your own rails app ...
# depending on where you put this you might need to load it yourself
app/lib/other_api_connection.rb
# rails prefers this format as part of ruby & it's class autoloading
class OtherAPI < CarRegistrationFrance
attr_accessor :registration, :description
def initialize(search_immat, user, pass, api_url = {whatever currently})
...
#user = user
#pass = pass
#record = look_up
#api_url = api_url
end
def look_up(search_immat)
...
Lookup("#{search_immat}", #user, #pass)
end
def update_api
begin
... {you haven't shown us the update API url} ...
rescue {some error from api}
return "{some error message}"
end
end
end
So ... you have the API handler object now ...
For use you call it in the controller#action ... lets say User#Show, which submit redirects to User#Update or Vehicle#Update
class Vehicle < ActionController::Base
...
def update
...
# normal stuff probably needs to be deleted if you aren't saving to your application db THEN updating API (aka just wrapping an API - which I'm assuming you are doing since you haven't said otherwise & it's less work).
# also you could pass the object as a parameter in the submit link - but that's not really CRUD per say
#user = {whatever user is}
api = OtherAPI.new(user, pass)
# you might have to permit this as parameter to pull it out
#response_code = api.update_api(:RegistrationYear, :Description) # or whole vehicle record if the API requires
if #response_code == {whatever valid code is from that API documentation}
format.html { redirect_to #vehicle, notice: 'Vehicle was successfully updated.' }
else
format.html { render :edit }
format.json { render json: #vehicle.errors, status: :unprocessable_entity }
end
... rest of controller actions
end
Note - I suggested making an API interface class/object because you can then call the code from either controller or action & only have to make the changes in one place if the api url is changed etc ... but you might have to include OtherAPI at the top of each controller code.
hello Thanks for your help
i using devise to authentificate user
my shemas database:
create_table "users", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.float "longitude"
t.float "latitude"
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.inet "current_sign_in_ip"
t.inet "last_sign_in_ip"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "username"
t.string "avatar_file_name"
t.string "avatar_content_type"
t.bigint "avatar_file_size"
t.datetime "avatar_updated_at"
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end
create_table "vehicles", force: :cascade do |t|
t.string "immatricule"
t.string "description"
t.string "marque"
t.string "date_circulation"
t.string "fuel_type"
t.bigint "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["user_id"], name: "index_vehicles_on_user_id"
end
add_foreign_key "vehicles", "users"
my model users:
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable, :trackable
has_many :vehicles
my model vehicles:
class Vehicle < ApplicationRecord
belongs_to :user
end
i need this routes:
user_vehicles GET /users/:user_id/vehicles(.:format) vehicles#index
POST /users/:user_id/vehicles(.:format) vehicles#create
new_user_vehicle GET /users/:user_id/vehicles/new(.:format) vehicles#new
edit_user_vehicle GET /users/:user_id/vehicles/:id/edit(.:format) vehicles#edit
user_vehicle GET /users/:user_id/vehicles/:id(.:format) vehicles#show
PATCH /users/:user_id/vehicles/:id(.:format) vehicles#update
PUT /users/:user_id/vehicles/:id(.:format) vehicles#update
DELETE /users/:user_id/vehicles/:id(.:format) vehicles#destroy
but i'm using devise
in my controller i need to build
vehicles with response api i need to save data in my database
from number immatricule
in form new vehicles users enter number_immatricule
"immatricule" = form new vehicles[:immatricule] users
form get data_api
def build_vehicle_user_from_api_data
"description" == data_api["description"]
"marque" == data_api["CarMake"]
"date_circulation" == data_api["RegistrationDate"]
"fuel_type" == data_api["fuel_type"]
end
I'm looking for how to do that in my controller users or vehicles
I'm having some troubles with a project I'm working on. Be warned I consider myself very much a beginner/novice at all this still :)
To keep things short and sweet, I'm using Rails & active admin to build up an admin interface where i can perform CRUD operations on my database models, which is all working great. However I recently decided I wanted to add another field to one of my models, a "description" field, so generated a migration, ran rake db:migrate and updated my list of allowed params in my controller & active admin resource.
My problem is data is not saved for this new "description" field - wether its via creating a new entry or updating an existing one. I can see the output in the terminal confirms it is being filtered out by strong params; returning Unpermitted parameter: :Description However i am under the impression i have set up my strong params correctly, so I'm unsure if i have set up my permit params properly or what else i can do.
Using Rails 5.1.0 & will post code below.
class CellsController < InheritedResources::Base
def index
end
private
def cell_params
params.require(:cell).permit(:name, :description)
end
end
#database schema for my cell model
create_table "cells", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.text "Description"
end
#Active Admin resource
ActiveAdmin.register Cell do
permit_params :name, :description
end
Again, greatly appreciate any help as I'm sure I've overlooked something, happy to provide any other information that is required :)
Thankyou!
To me it looks like the description param is not accepted because the model only has a Description column (with a capitalised D). To fix that, either change each params.permit(:description) to params.permit(:Description) or just rename the column inside a new migration:
def change
rename_column :cells, :Description, :description
end
I recommend renaming the column as it will avoid any trouble with the column in the future.
We inherited a rails project (the whole thing is based on elasticsearch). Thus, the application lists all the documents meeting the provided search criteria by a user. Imagine a facet or so, once you've selected i.e. a specific range of dates it gives you the documents that were created at that time.
We have been currently working on expanding its functionality accordingly:
Our priority is to utilize the elastic query such that all
matched documents by that query could be multiply altered (in
our case, we've been trying to hide all the documents). Then we want to send the query to appropriate rake task that would take care of it.
After the button responsible for mass hiding is clicked the controller's create method gets called, thereby creating a mass_hiding record in database (for convenience we keep a track of hidings which would allow us to revert the mass action afterwards).
def create
mh = current_user.mass_hidings.build(params[:mass_hiding])
mh.save!
mass_hide(mh.query_params)
redirect_to search_documents_path(mass_hide.search_parameters)
end
def mass_hide(query)
search = factic.create_restrictions_search(MultiJson.load(query.to_json))
Resque.enqueue(Document::Jobs::HideDocuments, nil, search.to_scrollable.build_query)
end
However, the line 3 of the create method mass_hide(mh.query_params) triggers the following exception once it gets executed:
ArgumentError in Admin::MassHidingsController#create
wrong number of arguments (0 for 1)
Full trace can be found here.
Parameters:
{"utf8"=>"✓",
"authenticity_token"=>"6mZvRcp4HJuoBWMRToA2gPec9Wv8T82hiTJQ/STf1j/sDhQ+16mBW3QkRmhqlJIHKR0kvX/kqwQh205hp6RuDg==",
"mass_hiding"=>{"serialized_query_params"=>"{}",
"description"=>"test"},
"commit"=>"Hide documents"}
Further, here is the schema representation of mass_hiding as well as the appropriate model:
schema.rb
create_table "mass_hidings", force: :cascade do |t|
t.integer "user_id"
t.text "serialized_query_params"
t.string "description"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
mass_hiding.rb
class MassHiding < ActiveRecord::Base
attr_accessible :serialized_query_params, :description
belongs_to :user
def query_params=(params)
self.serialized_query_params = Oj.dump(params)
end
def query_params
Oj.load(self.serialized_query_params).with_indifferent_access
end
def self.find_or_initialize_by_user_and_query_params(user, query_params)
self.find_or_initialize_by_user_id_and_serialized_query_params(user.id, Oj.dump(query_params))
end
end
Thank you for your help in advance.
The problem is that you have defined this helper function:
def mass_hide(query)
But you are calling it with zero arguments.
First you call it with one argument, when you say mass_hide(mh.query_params). That's fine.
But then on the next line you also say mass_hide.search_parameters. To Ruby that means mass_hide().search_parameters. Perhaps you meant to write mh.search_parameters? I'm not sure. But mass_hide is the method that wants 1 param, and that's the callsite where you pass it 0.
I hope that helps!
I have a rails app where users can talk to each other via chat. If they send over links in the chat (if you send anything at the moment in the chat it gets saved as message.body) to each other I would like to somehow distinguish it from plain text to be able to show the links on an index page later. How could I do that? At the moment 2 users have one common conversation. And messages belong to conversation.
Should I try to save them as different message attribute like message.link? If yes, then how can I distinguish message.link from message.body when creating the message. Or can I just simply use some helper method and showing only those message.body-s where the body itself is a link. And what if the half of the message is plain text and the other half is a link?
message schema
create_table "messages", force: :cascade do |t|
t.text "body"
t.integer "conversation_id"
t.integer "user_id"
t.string "message_attachment"
end
You can use Rinku.
1. Add it to your gemfile
gem 'rinku'
2. Create a method in your ApplicationHelper name it find_links
def find_links(message_body)
Rinku.auto_link(message_body, mode=:all, link_attr=nil, skip_tags=nil).html_safe
end
3. In your view pass the message body to the find_links method.
= find_links(message.body)