Truncate text and link to Read more - ruby-on-rails

Come across a little stumbling block when linking to a post within my app. I am truncating the text of a post and providing a 'Read More' link. There are 2 areas where posts are viewed, one for everyone ( public) and also one for the admin user to edit/delete posts.
So in my public view I am doing this
<% #toppost.each do |t| %>
<div class="post-item">
<h2><%= t.title %></h2>
<ul id="newsHome">
<li><%= date_output(t.published_on) %></li>
<li><%= t.department.name %></li>
<li>by Admin</li>
</ul>
<% if t.comments.length > 350 %>
<p class="post-description"><%= truncate(t.comments, length: 350).html_safe %>
<%= link_to '...Read more', t %></p>
<% else %>
<p class="post-description"><%= t.comments.html_safe %></p>
<% end %>
</div>
<% end %>
However when clicking read more it takes me to the url
/posts/:id
which is actually the place where an admin user views posts, so at the moment a user will get redirected back to the home page as the posts controller has
before_filter :authenticate_user!
The place where all posts are viewed publicly are on specific news pages , for example
localhost:3000/tynewyddnews
localhost:3000/woodside
localhost:3000/sandpiper
localhost:3000/outreach
My question is how to link to that post at its location in the public part of the site.
Index action where top_posts method used(see below for method)
def index
#title = 'Home'
#toppost = Post.top_posts
end
top_posts method
def self.top_posts
#Array with each of the 4 departments - first record
top_posts = [
self.tynewydd_posts.first,
self.woodside_posts.first,
self.sandpiper_posts.first,
self.outreach_posts.first
]
#remove entry if nil
top_posts.delete_if {|x| x==nil}
return top_posts
end
Controller
def tynewyddnews
#title = 'Ty Newydd News'
tynewyddpost = Post.tynewydd_posts.reverse
tynewyddpost.pop
#tynewyddpost = tynewyddpost
#tynewyddpostlatest = Post.tynewydd_posts.first
end
Model
scope :tynewydd_posts, :include => :department, :conditions => {"departments.name" => "Ty Newydd"}, :order => "posts.published_on DESC"
There are another 3 scopes for the other departments, all the same looking for the condition department name
Hopefully ive added enough info, anything else needed please ask
EDIT
Been thinking this through and not sure if im on right track, but for each post i need it to link to its appropriate news page in the public pages controller.
tynewyddnews
sandpipernews
outreachnews
Woodsidenews
So in my link_to i need to pass a route to the appropriate action depending upon the type of post.. so how to give each post a type and then link to that?
Thanks

Wow, ok, where do I begin.
You need a route that points to that controller action, for example
`get '/tynewyddnews' => 'news#tynewyddnews', :as => 'public_news' # gives you the route public_news_path
in your view
= link_to 'Read More', public_news_path
So ya, that should do it. BTW you can also use the .truncate() method. You pass in the length as an argument, and it adds the ... for you

Related

How to use a button to post in rails?

So I'm trying to create a new "transaction" each time I click the button "Purchase", but doesn't seem to work. I get the error "undefined method `[]' for nil:NilClass".
<% #comics.each do |comic|%>
<ul>
<li><%= comic.title %> </li>
<li><%= comic.author %> </li>
<li><%= comic.year %> </li>
<li><%= comic.publisher %> </li>
<% if user_signed_in? %>
<%= button_to 'Purchase', {:controller => "transactions", :action => "create", :seller_id => comic.user_id, :buyer_id=> current_user.id, :comic_id => comic.id} , {:method=>:post} %>
<% end %>
</ul>
This is what you can find in the transactions controller:
def create
#my_transaction = Transaction.new(
buyer_id: params[:transaction][:buyer_id],
seller_id: params[:transaction][:seller_id],
comic_id: params[:transaction][:comic_id]
)
#my_transaction.save
redirect_to "/transactions/"
end
Do you have any idea why this might be happening?
Thanks!
There are a couple of ways you can debug this:
Look in the console logs to see what is being posted in the params hash.
add a puts statement at the top of the create statement to view what is in the params variable e.g.
controller
def create
puts params.inspect
end
I suspect you'll find that the params hash does not have a transaction key and the create method should be
def create
#my_transaction = Transaction.new(
buyer_id: params[:buyer_id],
seller_id: params[:seller_id],
comic_id: params[:comic_id]
)
end
The params[:transaction] is nil, you can see the sent parameters in the log (tail -f log/development.log if the server doesn't log). In your case you access the required data like params[:comic_id]
A few tips:
Never trust the input coming from the client:
:buyer_id=> current_user.id here an attacker could send any ID since the button_to helper will create a html form which is easily accessible using the devtool. Instead check it on the server side. Same goes for the seller_id, you can just fetch the related comic comic = Comic.find params[:comic_id].
You might want to consider an another API approach like POST /comics/1/buy this is a bit more restfull, and you could use the built in path helpers for that url like buy_comic_path(comic)

How to add checkbox values (client ids) to a User attribute (User.clients)

I've been struggling to get this to work following a tutorial. I've got Users that have profiles, and a Client model that, through a 'many to many' join table, establishes the relationships between the user and the clients.
What I'm trying to do is create a list of check boxes generated from the list of clients in the DB that you can tick on or off, and then when you submit it, the user will have the relationship to those clients through the join table.
It's sort of working with static data as you can see below:
/profiles/show.html.erb
<% #clients.all.each do |client| %>
<li>
<%= check_box_tag "user[client_ids][]", client.id %>
<%= client.client_name %>
</li>
<% end %>
<%= link_to 'Add Clients', '../assign_clients/' + #profile.user.id.to_s , class: 'btn btn-default' %>
Routes
get 'assign_clients/:id', to: 'users#assign_clients'
And finally in my users_controller.erb
def assign_clients
#user = User.find(params[:id])
#user.client_ids = [1,2]
redirect_to :back, alert: 'Assigned Users'
end
Obviously it's just using hard coded values of 1 and 2. What I'm not sure how to do is wrap the checkboxes in the correct form tag/simple_form (which I am using), and then with the 'submit' button, have that do the 'assign_clients' action that passes through the values.
Thank you for any help.
What I'm not sure how to do is wrap the checkboxes in the correct form
tag/simple_form (which I am using), and then with the 'submit' button,
have that do the 'assign_clients' action that passes through the
values.
In order to create a form that will trigger the assign_clients method a route needs to be setup in your routes.rb file like the following:
resources :users do
patch 'assign_clients', to: 'users#assign_clients', as: 'assign_clients'
end
This sets up a route for a user that you can use the http patch method with (ie. UPDATE). The plan is to pass the client_ids to the users controller as params from the form. I gave it a path name so that we can reference it in the form as user_assign_clients_path(:user_id)
Now that we have the route set up...using the default rails form tags you can structure the form along the lines of this:
<%= form_for #user, url: user_assign_clients_path(#user) do |f| %>
<% #clients.each do |client| %>
<li>
<%= check_box_tag "user[client_ids][]", client.id, #user.clients.include?(client) %>
<%= client.client_name %>
</li>
<% end %>
<%= f.submit "Add Clients", class: "btn btn-default" %>
<% end %>
This will create a form allowing you to post the selected clients as an array of ids to the assign_clients method.
Finally, the assign_clients method can then retrieve the client_ids from the params hash (via params[:user][:client_ids] most likely) and update the user instance (retrieved using user_id from params hash also). You will probably have to add client_ids: [] to the end of your strong parameters list for user to whitelist it - but this essentially should behave like a typical update method.
def assign_clients
#user = User.find(params[:user_id])
#user.update(user_params)
redirect_to wherever_path
end
def user_params
params.require(:user).permit(
client_ids: []
)
end
You need to understand several basic concepts, let me explain to you:
on: member routing - in order to solve your issue directly, your route should be something like:
resources :users do
post '/assign_clients/:client_id', on: :member
end
so that other than user_id, the :client_id can be also passed in as a parameter. For the details, you can read about rails guides on routing.
For the checkbox way, you need nested_attributes - http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html. So that you can achieve what u need with a simple update call on users.
You can also implement a customised logic, with the client_ids passed in as parameters. In order to learn how forms & parameters work in rails, you can build a form, submit it, and see how it goes in the rails server log.
If anything is unclear, simply comment below & I'll try to update.

Want to display last 3 posts on the homepage

So i have rails app where all posts are displayed on /posts, which is where I want them. There, I have 10 posts per page. But, along with this page - i would like to take the last three posts and display them in a div on the root page.
Not sure where to start.
Thanks
Try this:
%div
-Post.last(3).each do |p|
%h1= p.title
%p= p.author
%p= p.content
Post.last(3) returns the last 3 posts that you're looking for. Hope this helps.
p.s. you may want to refactor this by moving the Post.last(3) into a variable in your controller like #latest_posts = Post.last(3)and iterate over that.
The last finder method will return the results in ascending order. If you want to return the results ordered by created_at in descending order, here's how I would approach it (unit test included).
app/models/post.rb
class Post < ActiveRecord::Base
def self.recent(max = 3)
limit(max).order(created_at: :desc)
end
end
spec/models/post_spec.rb
RSpec.describe Post, type: :model do
describe ".recent" do
it "returns the most recent" do
first_post = Post.create(created_at: 3.days.ago)
second_post = Post.create(created_at: 2.days.ago)
third_post = Post.create(created_at: 1.day.ago)
result = Post.recent(2)
expect(result).to eq([third_post, second_post])
end
end
end
In your controller(s):
#recent_posts = Post.recent
In your view:
<div id="recent-posts">
<ul>
<% #recent_posts.each do |post| %>
<li><%= post.title %></li>
<% end %>
</ul>
</div>
If you want to reuse the view code, put it into a partial and render that in your view(s).

Rails: Having a button in a view call a method in a controller, route not working

Hi I'm making a rails app that uses Zendesk API calls. I have a controller that uses two classes I defined
class TicketsController < ApplicationController
require 'ticket_fields'
require 'ticket_search'
def getTickets
#search_fields = SearchFields.new(client)
#tickets = TicketSearch.new(client)
end
def search_tickets
#ddcustomvalues = [params[:customer_company_id], params[:study_id], params[:type_id], params[:system_id]]
#tickets.search_tickets(ddcustomvalues)
end
end
One class SearchFields uses the api to load values I want to filter tickets by into arrays. My view then uses these values to populate drop down lists.
The other class TicketSearch looks like this.
class TicketSearch
attr_reader :tickets, :text
def initialize(client)
#text = "query"
#tickets = Array.new
client.tickets.all do |resource|
#tickets << resource
end
end
def search_tickets(custom_search_fields)
querystring = "type:ticket+tags:"
custom_search_fields.each_with_index do |field, index|
unless field == ""
if index ==0
querystring += "#{field}"
else
querystring += " #{field}"
end
end
end
#text = querystring
end
end
What I want to happen in my view is when a button is pressed it changes the value of #text to the querystring generated by the drop down list options that were selected. I'm currently doing this for testing to see if my querystring is correct and the button works. What I eventually want it to do is send the querystring to the ZenDesk Server and returns the tickets I filtered for. the #tickets array would then be replaced with the filtered tickets the server returned. Currently my button code looks like this.
<%= button_to 'Search', :action => 'search_tickets' %>
with all the route code I've tried I either get an error upon starting the page. Or when I press the button nothing happens and the #text being displayed in my view remains "query". Can someone help explain what I need to do I don't quite understand how routes work.
==================================================================================
Hey so I made the changes you suggested and did some reading up on AJAX and js and I think I'm almost at the answer my view now looks like this
<div id="test" >
<%= render partial: 'text', locals: { text: #tickets.text} %>
<div id="test" >
and I created a partial _text file that looks like this
<p> Query: <%=text%> </p>
and a js file search_tickets.js.erb
$("#test").html("<%= escape_javascript(render partial: 'text', locals: { text: #tickets.text } ) %>");
any idea what may be going wrong everything loads up okay but the text remains the same in the partial i set up when i hit the button still
the console outputs this after the button is hit
ActionController::RoutingError (No route matches [POST] "/tickets/search_tickets"):
so I guess it may actually be a routing error my route looks like this
resources :tickets do
collection do
put :search_tickets
end
end
and the form tag calling the path looks like this
<%= form_tag search_tickets_tickets_path, remote: :true do %>
<table>
<tr>
<td align = "left" valign="middle"> <font size = 4> Customer Company </font> </td>
<td align = "left" valign="middle">
<%= select_tag "customer_company_id", options_for_select(#search_fields.customer_companies), :prompt => "Select One" %>
</td>
</tr>
......
<tr>
<td> </td>
<td align = "left" valign="middle">
<%= submit_tag "Search" %>
</td>
</tr>
</table>
<% end %>
==================================================================================
(Update)
I think I fixed my last problem by changing my form tag to this
<%= form_tag search_tickets_tickets_path(#tickets), method: :put, remote: :true do%>
however now I get this error from the terminal after I hit the button
NoMethodError (undefined method search_ticket' for nil:NilClass):
app/controllers/tickets_controller.rb:15:insearch_tickets'
how would I pass #tickets as a parameter through my route because clearly its not accessible by search_tickets right now as its giving a nil class error.
Variables
when a button is pressed it changes the value of #text to the querystring generated
It looks to me like you're confused with the stateless nature of Rails - in that, just because a view has been rendered doesn't mean the values / variables are still available for use.
It was mentioned in the comments that it seems you're basing a lot on experience with other frameworks / programming patterns. The best way to describe your solution is that Rails has to "refresh" all your variables / values each time it processes a request; consequently meaning that if you send a button request - you'll have to perform the request as if it were the first one
Ajax
The bottom line is that you need to use an ajax request to pull this off.
To do this, you'll be be best creating a form (not just a button_to), as this will give you the ability to send as many params as you want. You should use form_tag:
#config/routes.rb
resources :tickets do
collection do
get :search_tickets
end
end
#view
<%= form_tag tickets_search_tickets_path, remote: :true do %>
... #-> fields for your params
<%= submit_tag "Search" %>
<% end %>
This will give you the ability to define the following in your controller:
#app/controllers/tickets_controller.rb
Class TicketsController < ApplicationController
def search_tickets
#ddcustomvalues = [params[:customer_company_id], params[:study_id], params[:type_id], params[:system_id]]
#tickets.search_tickets(ddcustomvalues)
respond_to do |format|
format.js #-> loads /views/tickets/search_tickets.js.erb
format.html
end
end
end
#app/views/tickets/tickets_search.js.erb
//JS here to manipulate your original page
Requests
The bottom line here is that if you want to "manipulate" your view without refreshing, unlike "native" application frameworks, where you can rely on a persistent state, with Rails, you basically have to construct the request from scratch (IE passing all the params required for the method to run)

Render a rails partial based on the id of an action

I'm building a small ecommerce site that sells a variety of mens and womens clothing. i would like to render a partial based on which taxonomy the user is in. For example, if the user is at mysite.com/t/women/pants I would like to render _women.html.erb, or, if the user is at mysite.com/t/men/shirts I would like to render _men.html.erb.
I have a Taxonomy model that has_many taxons, and the Taxon model has_many products.
In taxons_controller.rb I have:
def show
#taxon = Taxon.find_by_permalink(params[:id])
return unless #taxon
#taxonomy = Spree::Taxonomy.all
#taxon_title = Spree::Taxon.all
#searcher = Spree::Config.searcher_class.new(params.merge(:taxon => #taxon.id))
#searcher.current_user = try_spree_current_user
#searcher.current_currency = current_currency
#products = #searcher.retrieve_products
respond_with(#taxon)
end
And in taxons#show I have: (which I know is wrong)
<% #taxon_title.each do |title| %>
<% #taxonomy.each do |taxonomy| %>
<% if title.name == taxonomy.name %>
<%= render "spree/shared/#{title.name.downcase}" %>
<% end %>
<% end %>
<% end %>
When I go to mysite.com/t/women/long-sleeve the rails debugger displays :
controller: spree/taxons
action: show
id: women/long-sleeve
How do I access the id of the action im inside, so that in the controller/view I can do something like:
'if id equals 'women' render "spree/shared/#{title.name.downcase}"'
where title is the name of the taxonomy?
I imagine I need to find(params[:something] in the show action of the controller, but I'm a little unclear about params.
*
*
*
#beck03076 That's a great trick. Thank you very much. But it's still not working.
In my controller I put:
#taxon_id = Spree::Taxon.find(params[:id])
Then in the action I put:
render 'spree/shared/women' if #taxon_id == params[:id]
And when I load the page it says 'the page you were looking for doesn't exist'. My partial is in the correct directory. Is my syntax correct?
My params are:
{"controller"=>"spree/taxons", "action"=>"show", "id"=>"women/long-sleeve"}
Thanks again for your help!
Whenever you are unclear about params, just put the lines below in the action and execute the action.
p "****************************"
p params
p "****************************"
Now, goto the terminal in which you started your server.
Locate those two "*******" and everything thats in between them are params.
params is basically a ruby hash.
example:
params look like this, {:controller => "hello",:action => "bye", :id => 7, :others => "OK"}
In your controller to access the id, use params[:id].(=7)
to access others, use params[:others].(="OK")

Resources