Rails 3 Searching HTML Content with Sunspot - ruby-on-rails-3

I'm storing some HTML content in my database and I would like to be able to perform a search using Sunspot while omitting HTML from the hit output and if possible the search itself.
My model:
class Article < ActiveRecord::Base
attr_accessible :caption
searchable do
text :content, :stored => true
end
end
Search action:
def find
#search = Article.search do
fulltext params[:search] do
highlight :name
end
end
end
Template:
- #search.each_hit_with_result do |hit, article|
- unless hit.highlight(:content).nil?
%p= hit.highlight(:content).format { |word| "<span class=\"highlight\">#{strip_tags(word)}</span>"}.html_safe
Some sample content that is being search might be something like:
<h1>Hello world</h1>
<p> Search for me me!</p>
Link
Notice that I'm marking the output as html_safe? This is because I would like the wrap the search text that gets hit with a highlight span, however besides that everything else I want to be stripped completely from the returned text that gets hit. Is this even possible?

What ended up working for me was stripping the content that gets indexed by solr. To do so I had to make the following change inside of my model:
include ActionView::Helpers::SanitizeHelper
searchable do
text :content, :stored => true do
strip_tags(content)
end
end
Adding those changes and running rake sunspot:solr:reindex worked like a charm!

Related

Display only one field from an embedded document with MongoID

I'm a real beginner with MongoDB and MongoID.
I created two scaffolds
class Objet
include Mongoid::Document
field :nom, type: String
embeds_one :coordonnee
end
And
class Coordonnee
include Mongoid::Document
field :adresse1, type: String
field :adresse2, type: String
field :code_postal, type: String
field :ville, type: String
embedded_in :objet
end
That's what I get when creating a new Objet :
Now, I'm trying to show only the field adresse1 for this document, but it doesn't work. I can display only the whole embedded document doing this :
When I do :
<%= #objet.coordonnees.adresse1 %>
I get this error :
undefined method `adresse1' for #<Hash:0x2b2b1f0>
How can I do that ?
EDIT
Doing that, I can display all the elements "Adresse1, adresse2, ville, code_postal" :
Controller
def show
#objet = Objet.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #objet }
end
end
View
<%= #objet.nom %>
<% #objet.coordonnee.each do |t| %>
<%= t[1] %>
<% end %>
But my question is : How to display ONLY one of them ? Such as ville, or code_postal or adresse1... ?
What was your code that works for the full document? It was dropped from your post.
In the mongo Shell, you could do this with dot notation db.collection.find({},{'coordonnees.adresse1':1,'_id':0}) You need to specify the '_id':0 because _id is always returned by default.
The other answer will not work because adresse1 is a subdocument. You must include the reference to coordonnees.
Not hugely familiar with MongoID, but assuming you can make calls straight to mongo, there is a second implicit parameter to all find-like statements called a projection that specifies what exactly you would like to return.
For instance, showing only adresse1 for all items in your collection:
db.collection.find({},{"coordonnees.adresse1": 1, "_id":0})
should return only the adresse1 parameter. I wasn't quite able to tell exactly what context you're displaying the objects in, but regardless of context, api calls to mongo should be fairly straightforward to make. Let me know if I've misinterpreted this question though.
In your posted example, you should change your find function to something like the following:
Objet.find({params[:id]}, {:fields => [coordonnees.adresse1]})
Hope that helps.
I found the solution to my problem.
To display only one element of the hash, I can do :
<%= #objet.coordonnees['adresse1'] %>
I am not sure if you are using embeds_one or embeds_many as you are using singular and plural forms of the relation name interchangeably in your question.
If it is a embeds_one the problem is that you should not iterate on #objet.coordonnee as it is a single document. Your view code should look like:
<%= #objet.nom %>
<%= #objet.coordonnee.address1 %>
If it is a embeds_many, your relation name should be plural, then you should be able to use t.address1 in your view.
# model Objet
embeds_many :coordonnees
# view
<%= #objet.nom %>
<% #objet.coordonnees.each do |t| %>
<%= t.address1 %>
<% end %>

how to append/replace alt/title attribute in all image tags?

say I have an action template like this
# home/index.html.erb
<%= img_tag "logo.gif" %>
if I want to add alt/title attribute to it, I can just do
# home/index.html.erb
<%= img_tag "logo.gif", alt: "alt!!", title: "title!!" %>
but I have 1000 image tags and I don't want to change every each one of them.
I then thought of using rack middleware and modify image tags before outputting from server.
http://railscasts.com/episodes/151-rack-middleware?view=asciicast
doc = Nokogiri.HTML(#response.body)
doc.search("img").each do |tag|
[:alt, :title].each{|attribute| tag[attribute] = "changed!!" }
end
but when I follow the railscast episode, it appends entire body on the top of the original rather than replacing it.
Am I doing it wrong in the rack, or is there a smarter way to do this?
Updated answer:
# /config/initializers/image_tag_helper.rb
module ActionView
module Helpers
module AssetTagHelper
def image_tag(source, options={})
options[:src] = path_to_image(source)
options[:alt] = "Default Alt" unless options.has_key?(:alt)
options[:title] = "Default Title" unless options.has_key?(:title)
tag(:img, options)
end
end
end
end
This overrides the image_tag helper method to set default alt and title attributes.

rspec rendered view fails to include variable data

I have a failing rspec view test but the code works - I probably have a variable incorrectly setup but can't figure out what it is.
When I display the contents of #incident_report (pp #incident_report) in my spec, it properly displays the record created by FactoryGirl.
When I display the actual rendered content (puts rendered), it shows the values from the the record I created with FactoryGirl...
But the "rendered.should contain(work_order)" spec fails with:
1) incident_reports/show.html displays the work order number on the incident
Failure/Error: rendered.should contain(work_order)
expected the following element's content to include "54785":
and none of the data is displayed, only the HTML template
spec/views/incident_report/show.html.haml_spec.rb code
require 'spec_helper'
describe "incident_reports/show.html" do
before(:each) do
#incident_report = Factory(:incident_report)
end
it "displays the work order number on the incident" do
work_order = #incident_report.work_order
pp #incident_report #displays an incident_report, id => 1
assign(:incident_report, #incident_report)
render
puts rendered #this DOES have the content from #incident_report
rendered.should contain("Work Order:")
rendered.should contain(work_order)
end
end
show.html.haml code
%h1 Display Incident Report
.navigation
= link_to 'Edit', edit_incident_report_path(#incident_report)
|
\#{link_to 'Back', incident_reports_path}
= render 'form'
.navigation
= link_to 'Edit', edit_incident_report_path(#incident_report)
|
\#{link_to 'Back', incident_reports_path}
Gotta be something really simple I'm overlooking.
Turns out it's because I was using simple_form and when simple_form displays for a "show" action, it puts the field values into the html as a 'value="54785"' attribute. If you display it in a browser, the labels and values all show up correctly, but rspec can't see them.
I had to add
rendered.should have_tag 'input', :with => { :value => "54765", :name => 'incident_report[work_order]' }
to my example to get it to work.
Seems like there should be a better solution but at least now I can continue testing.

Check if an attribute is validated

My problem is as follows:
I've got a form view, which needs to display success and failure icons after submit.
Before submit it just needs to show the form without the success and failure icons.
We can do this in several ways when this is the form:
<%= form_for #resource do |f| %>
<div class='<%= set_class #resource, :name %>'>
Name: <%= f.text_field :name %>
</div>
<% end %>
Check if the request is a POST:
def set_class( record, attribute )
if request.post?
if record.errors[attribute].any?
return "FAILED"
else
return "SUCCESS"
end
end
# If not submitted, we don't want a class
end
Set a flag after validation ( We can replace request.post? in above solution with record.tried_to_validate ):
class MyModel < ActiveRecord::Base
after_validation :set_tried_to_validate
attr_accessor :validated
def set_validated
#tried_to_validate = true
end
end
But I don't really like these solutions..
Isn't there an inside Rails method to check if the validation process is done?
You can first test for validity..
#form.valid?
Which will generate errors stored in 'errors' on your #form. To see if errors exist on a specific field,
#form.errors[:some_field]
On your form, you can simply do:
<% if #form.errors[:some_field].empty? %>
Valid
<% end %>
As long as some fields generate errors, the whole form will be !valid?, so you'll revert to showing the form again (:new), and you can should 'Valid' or checkmark.
I think you are looking for something like client side validations, if want the validation to show inline on the form. http://railscasts.com/episodes/263-client-side-validations
EDIT
If you want to capture the 3 stages, you can save in your db. New, Validate, Finished and just use callbacks to save each stage and set the default to new. (You will have the change the data type of the validated attribute to string)
after_validation update attribute to "validate"
after_save update attribute to "Finished"
Then you can use an if elsif else conditions to check for the value of that attribute and render the tick and cross. Obviously, this isn't pretty and you should just use valid? and the errors? helpers.

Rails 3 Rendering Binary Content

I need to render binary content(images) on web page. I'm saving images in the database with datatype binary. Now I need to iterate available images from the database and render on webpage.
Please check the below code that I'm doing. Icon is the image column name in material.
// iterating all materials
<% #materials.each do |material| %>
// for each material
<span><%= image_tag(material.icon) %></span>
<% end %>
Any help would be greatly appreciated..
You need to add an action to your controller along these lines (cribbed from here):
def image
#material = Material.find(params[:id])
send_data #material.icon, :type => 'image/png',:disposition => 'inline'
end
Then call the path to that action in your image_tag. You obviously need to make sure the :type field has the right MIME type, add a route, etc.