Associated databases with elasticsearch - ruby-on-rails

Just installed the elasticsearch gem and been configuring it.
I got the search working on the right model. However on the view I also include data from an associated database model called company and it gives me this error:
undefined method `company' for #<Elasticsearch::Model::Response::Result:0x000001032d67d0>
If I try to remove everything related to that associated database in the view, the search works perfectly. So it's just the problem of figuring this out.
My model for application.rb looks like this:
require 'elasticsearch/model'
class Application < ActiveRecord::Base
belongs_to :company
has_many :answers
include Elasticsearch::Model
include Elasticsearch::Model::Callbacks
....
my class where I display it (searches_controller.rb:
def index
#application = Application.search(params[:query])
end
and finally the index.html.erb file:
<%= form_tag search_startup_path, method: :get do %>
<p>
<%= text_field_tag :query, params[:query] %>
<%= submit_tag "Search", name: nil %>
</p>
<% end %>
<hr>
<% #application.each do |f| %>
<h2><%= f.company.name %> is looking for a <%= f.work_type %></h2>
<%= image_tag f.company.avatar(:medium), alt:"#{f.company.name.pluralize} logo", height:"100", width:"100" %>
<p><%= link_to "Visit #{f.company.name.pluralize} website", f.company.website, rel: "nofollow" %></p>
<p>Company located in: <%= f.company.city %>, <%= f.company.country %></p>
<p>Required to be on location: <%= f.location %> </p>
<% end %>
So viewing the associated company database without elasticsearch works fine. So there is no error there, again I'm just unsure how to enable elasticsearch to view the ass. model

New elastic search library returns only data from elasticsearch and wraps it into Hashie::Mash gem. So you working only with Elasticsearch::Model::Response::Result class. If you want to work with ActiveRecord your index action have to look like this:
def index
#application = Application.search(params[:query]).records
end
records method returns collection of ActiveRecord models, you can find more info on github read me https://github.com/elasticsearch/elasticsearch-rails/tree/master/elasticsearch-model#search-results-as-database-records

Related

Why is post/create method failing with error

I'm new to RoR. Wanted to try if my next web app should be RoR based. Started out following this trail: https://guides.rubyonrails.org/getting_started.html. Worked like a charm in the beginning, but i'm unable to get the darn thing to create new records. Any hint as to what i'm missing is appreciated.
The error i'm getting is this:
D, [2020-12-18T09:59:56.917197 #132399] DEBUG -- : Createevent
F, [2020-12-18T09:59:56.917893 #132399] FATAL -- :
ActionController::ParameterMissing (param is missing or the value is empty: Event):
app/controllers/event_controller.rb:33:in `event_params'
My routing looks like this:
Rails.application.routes.draw do
post 'event/new', to: 'event#create'
resources :event
end
(I'm baffled by the need for specifying the POST above, but without it the create is never fired. ).
The eventcontroller looks like this:
class EventController < ApplicationController
def index
#events = Event.all
end
def show
#event = Event.find(params[:id])
end
def new
logger = Rails.logger
logger.info 'NewEvent'
#event = Event.new
end
def create
logger = Rails.logger
logger.debug 'Createevent'
#event = Event.new(event_params)
logger.debug 'Eventcreated'
if #event.save
redirect_to event_path
else
render :new
end
end
private
def event_params
params.require(:Event).permit(:EventName, :Description, :EventStart, :EventEnd, :Maxparticipants, :Waitlist )
end
end
Index and show works fine.
The new.html.erb looks like this:
<h1>New Event</h1>
dsfsdfds
<%= form_with model: #Event do |form| %>
<div>
<%= form.label :eventname %><br>
<%= form.text_field :EventName %>
<%= #event.errors.full_messages_for(:EventName).each do |message| %>
<div><%= message %></div>
<% end %>
</div>
<div>
<%= form.label :Description %><br>
<%= form.text_field :Description %>
<%= #event.errors.full_messages_for(:description).each do |message| %>
<div><%= message %></div>
<% end %>
</div>
<div>
<%= form.label :EventStart %><br>
<%= form.text_field :EventStart %>
<%= #event.errors.full_messages_for(:eventstart).each do |message| %>
<div><%= message %></div>
<% end %>
</div>
<div>
<%= form.label :Eventend %><br>
<%= form.text_field :Eventend %>
<%= #event.errors.full_messages_for(:eventend).each do |message| %>
<div><%= message %></div>
<% end %>
</div>
<div>
<%= form.label :Maxparticipants %><br>
<%= form.text_field :Maxparticipants %>
<%= #event.errors.full_messages_for(:Maxparticipants).each do |message| %>
<div><%= message %></div>
<% end %>
</div>
<div>
<%= form.label :Waitlist %><br>
<%= form.text_field :Waitlist %>
<%= #event.errors.full_messages_for(:waitlist).each do |message| %>
<div><%= message %></div>
<% end %>
</div>
<div>
<%= form.submit %>
</div>
<%= submit_tag "Create" %>
<% end %>
Routes:
Prefix Verb URI Pattern Controller#Action
event_new POST /event/new(.:format) event#create
event_index GET /event(.:format) event#index
POST /event(.:format) event#create
new_event GET /event/new(.:format) event#new
edit_event GET /event/:id/edit(.:format) event#edit
event GET /event/:id(.:format) event#show
PATCH /event/:id(.:format) event#update
PUT /event/:id(.:format) event#update
DELETE /event/:id(.:format) event#destroy
Things created using:
bin/rails generate model Event EventName:string Description:string EventStart:datetime EventEnd:datetime Maxparticipants:integer WaitList:integer
bin/rails generate controller Event index
Version:
About your application's environment
Rails version 6.0.3.4
Ruby version ruby 2.7.2p137 (2020-10-01 revision 5445e04352) [x86_64-linux]
RubyGems version 3.1.4
Rack version 2.2.3
Thanks to Hackman & nathanvda for clarifying. I scratched everything and started over and i finally got it working. Still way too much woodo and black magic for my taste though. The error message part got me baffled for three consecutive hours.
As stated i followed the guide and therefore ended up using (in new)
<%= form_with model: #event do |form| %>
<% if #event.errors.any? %>
<h2>Errors</h2>
<ul>
<% #event.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
<% end %>
...
Which works (well, sort of). The record gets saved if ok, the validations rules gets fired
class Event < ApplicationRecord
validates :eventname, presence: true
validates :description, presence: true, length: { minimum: 20 }
end
and if violated no record gets written to the database, but no error messages either. Nil. Nothing. After poking around on the internet i ended up changing to
<%= form_for #event do |form| %>
and then error reporting works. Only problem with this solution is that the use of form_for is discouraged as it is being rendered obsolete.
Final version ended up being:
<%= form_with model: #event, local: true do |form| %>
Which does the trick.
Next step in my evaluation will be the use of natural keys as the use of surrogate keys is not an option for some of the data structures needed in this project. (during my poking around i got the impression that natural keys are some kind of a sore tooth in RoR, but time will show.
To start out, your resources in the routes should be pluralized. So resources :event should be resources :events
Also the controller name should be pluralized. So EventController would become EventsController.
Now the needed routes should work fine and you can get rid of the specified POST in your routes.rb
Now inside your controller you have the event_params method. There it is preferred to downcase/snake_case the names like this:
def event_params
params.require(:event).permit(:event_name #etc)
end
If your column names in DB are EventName etc, I would advice to rename them.
Last thing: In your form you got #Event with uppercase while in the controller#new action you defined #event with lowercase. Use lowercase everywhere.
So if you had started as follows:
bin/rails generate model Event event_name:string description:string event\-start:datetime event_end:datetime max_participants:integer wait_list:integer
bin/rails generate controller events index
Then the generated code would work a lot better.
A few tips to clarify:
in ruby we only write classes with a capital, for variables we use snake case (everything lowercase and words connected with underscores). So by extension when generating a model all attributes should be snake cased
a controller in general uses the plural form, since it "controls" all the events (not just one).

How do I implement associations in searchkick

First, the example I read in the docs shows to declare the associated model as singular, :address, but if I do I get the error Association named 'address' was not found on User; If I change it to plural :addresses, then the next problem I have is the association doesn't work in views undefined method `country' for ...
Why am I declaring the association as plural and how can I make the association available in the view
User.rb:
class User < ActiveRecord::Base
searchkick word_middle: ['full_name', 'description', 'interests']
has_many :addresses
scope :search_import, -> { includes(:addresses) }
search.html.erb:
<% #users.each do |u| %>
<li>
<%= link_to "#{u.first_name} #{u.middle_name} #{u.last_name}", page_path(name: u.name) %>
<% #ua=u.addresses.where("current=?", true) %>
<% if #ua.country=="US" %>
<%= #ua.city %>, <%= #ua.state %> <%= ISO3166::Country.find_country_by_alpha2(#ua.country) %>
<% else %>
<%= #ua.city %>, <%= ISO3166::Country.find_country_by_alpha2(#ua.country) %>
<% end %>
</li>
<% end %>
</ul>
In controller, do this: #user = User.includes(:addresses).where(your_query) to make the association readily available in view.
And yes has_many associations are bound to be plural: "User has_one
:life" and "User has_many :passions"; does it make sense?
And finally your error: where returns an array because you queried: "bring me all records which fulfill this condition". find, on the other hand, will bring back 1 specific record as it expects a unique attribute or brings back first record that matches that attribute.
What you need to do:
You should either do this (if you are dead-sure that you will get 1
such record or you need only one of that type):
<% #ua=u.addresses.where("current=?", true).first %>
OR
If you need to go through all the resultant array then:
<% #ua=u.addresses.where("current=?", true) %>
<% #ua.each do |ua| %>
# Your code for each ua instead of #ua.
<% end %>
Happy Learning :)

In Rails, how can I create a button that saves an entry from an external api into my database?

I have an Rails api that consumes an external design search api using the HTTParty gem. With the data that is returned to my view I'd like to be able to save selected entries to my database. Kind of like a bookmark function. Is there anyway of having a button next to each search result item that would achieve this? Any help would really be appreciated. My current code is below.
designs controller:
class DesignsController < ApplicationController
def index
#search = Api.new.find(params[:q])['results']
end
end
Class method:
class Api
include HTTParty
base_uri "http://search.example.com/searchv2"
attr_accessor :name, :limit, :offset
# Find a particular design, based on its name
def find(name)
self.class.get("/designs", query: { q: name }).parsed_response
end
end
View:
<h1>Design Search</h1>
<%= form_tag(designs_path, method: :get) do %>
<%= label_tag(:q, "Search for:") %>
<%= text_field_tag(:q) %>
<%= submit_tag("Search") %>
<% end %>
<h2>Search results:</h2>
<% #search.each do |design| %>
<h3><%= design['name'] %></h3>
<h5><%= image_tag design['thumbnail_url'] %></h5>
<% end %>
Design model:
class Design < ApplicationRecord
end
app/views/designs/index.html.erb:
...
<% #search.each do |design| %>
<h3><%= design['name'] %></h3>
<h5><%= image_tag design['thumbnail_url'] %></h5>
<%= button_to 'Save',
designs_path(
design: design.slice('name', 'thumbnail_url') # etc...
),
method: :post,
data: { disable_with: 'Loading...' }
%>
<% end %>
app/config/routes.rb:
resources :designs, only: [:index, :create]
app/controllers/designs_controller.rb:
class DesignsController < ApplicationController
# ...
def create
#design = Design.new(design_params)
#design.save!
# just an example...
redirect_to designs_path, success: 'Design has been saved!'
end
private
def design_params
params.require(:design).permit(:name, :thumbnail_url) # etc...
end
end
TODOs:
your Design model needs to have attributes that you want to be saved (i.e: :name? and :thumbnail_url?). My assumption was that you already have created/migrated these attributes; if not yet, then please do add them.
because there's still a vulnerability in my code above in that any user can modify the <button>'s <form action='...'> (i.e. they can modify the "name" of the design as they wish when it gets Saved), then my solution above is still not the best solution, though for simplicity and that assuming that this vulnerability is not a problem for you, then the above code already is sufficient.

Routing Error uninitialized constant GradesController

I decided to start a Ruby on Rails project without scaffolding because I actually wanted to learn in the process. I have searched this site but cannot seem to find the answer to my question so I will ask here. I started a Rails project where the user enters their grades. Unfortunately, on the new grade page when the user hits Create Grade I get the error in the subject line. Here is my code for the form that I use in the new page under the grade controller.
<%= form_with(model: grade, local: true) do |f| %>
<% if grade.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(grade.errors.count, "error") %> prohibited this grade
from being saved:</h2>
<ul>
<% grade.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :assignment %>
<%= f.text_field :assignment %>
</div>
<div class="field">
<%= f.label :score %>
<%= f.text_field :score %>
<div class="actions">
<%= f.submit %>
</div>
This is my routes page:
Rails.application.routes.draw do
resources :grades
root 'grade#index'
get 'grade/index'
get 'grade/show'
get 'grade/new'
get 'grade/edit'
get 'grade/create'
get 'grade/update'
get 'grade/destroy'
# For details on the DSL available within this file, see
http://guides.rubyonrails.org/routing.html
end
If more code is needed to answer the question please let me know.
Based on the title of your question, Rails is looking for a file called app/controllers/grades_controller.rb file that defines the GradesController class.
Create the following file, and you should get to the next step
# app/controllers/grades_controller.rb
class GradesController < ApplicationController
def new
#grade = Grade.new
end
def create
# logic for persisting the grade object goes here
end
# other controller methods, here
end
In the form for a new grade, use the instance variable (the one with the # symbol) you defined in the GradesController#new method:
<%= form_with(model: #grade, local: true) do |f| %>
In your routes, this is all you should need:
Rails.application.routes.draw do
resources :grades
root 'grades#index' # not 'grade#index'
end
Controllers are plural, check the name of the controller file to ensure it's plural then check the controller class name change both from GradeController to GradesController.

check box not showing check Rails

I have a parent model that accepts child attributes.
class User < ActiveRecord::Base
accepts_nested_attributes_for :spec
attr_accessible :name, :spec_attributes
In the view I have a form that gets information for 3 models. I use a generic form_tag.
<% form_tag(action) do %>
.
.
.
<% fields_for "user[spec_attributes]" do |spec_form|%>
<%= spec_form.check_box :alert_greeting %>
<%= spec_form.label :alert_greeting, "Email me when new greetings are posted" %>
<% end %>
<% end %>
In the Controller
#user = User.find(session[:user_id])
if #user.update_attributes(params[:user])
do something.
end
The database is getting updated and the all seems to be working.
However when I go back to the form to edit again, even though the value for the checkbox is showing 1 the check box is not checked.
Any ideas as to how to show the checkbox as being checked when it is supposed to be?
Thanks a lot in advance.
You need to reference the specific spec record that is within the user you're calling. Try changing
<% fields_for "user[spec_attributes]" do |spec_form|%>
to
<% fields_for #user.spec do |spec_form|%>
You'll need to make sure that you have a non-nil spec object built for the user (but not necessarily saved) in your edit controller action.
You can do attributes and nested attributes using two fields_for calls like this:
<%= form_tag(action) do %>
... other form tags ...
<%= fields_for :user, #user do |f| %>
<%= f.text_field :first_name %>
<%= f.text_field :last_name %>
<%= f.fields_for :spec do |s| %>
<%= s.check_box :alert_greeting %>
<%= s.label :alert_greeting, "Email me when new greetings are posted" %>
<% end %>
<% end %>
<% end %>

Resources