Rails gem for using routes inside of the article body - ruby-on-rails

is there any gem allowing to replace string in particular format for links to some controller with parameters?
Example: In the content of article /articles/2/second-article I want to link to a previous article /articles/1/first-article.
Thanks

There is not a "gem" but you can do this easily in your model.
class Article
def next
self.class.where("id > ?", id).first
end
def previous
self.class.where("id < ?", id).last
end
end
Then you can use these in your controller:
#article = Article.find(params[:id])
#next_article = #article.next
#previous_article = #article.previous

Ok,
I have written a helper for that:
module ApplicationHelper
def raw_with_links(text)
text.gsub! /{'(.+?)', (.+?)(,(.+?))*}/ do |full_match|
# The expression returned from this block will be used as the replacement string
# $1 will be the matched content between the ' and ' quotes.
# $2 will be the mathed content in the second group with the route name
#4 will be the matched content in the last group with route parameters, if any
url = Rails.application.routes.url_helpers.send $2, $4
link = "" + $1 + ""
end
raw(text)
end
end
then you can call it in a view like this:
<%= raw_with_links #article.content %>
and here is how to create a link in the text (the route parameter (value 7 in the example bellow) is not mandatory):
class HomeController < ApplicationController
def index
#article.content = "<p>This is the article content with the {'link', other_route_name_path, 7} to the other route.</p>"
end
end
and the result is (considering the route other_route_name is other_route/:id:
<p>This is the article content with the link to the other route.</p>

Related

How do I set the tag owner before any tag is saved with the acts-as-taggable gem?

I am using acts-as-taggable gem https://github.com/mbleigh/acts-as-taggable-on
and jQueryTokenInput plugin to add tags to my image model . Tags are created and added fine so far. I followed the tutorial http://bloginius.com/blog/2013/12/31/how-integrate-acts-as-taggable-on-with-jquery-token-input-with-rails-3/.
Now however, I want to be able to give ownership of the tag to the current_user at the time the tag is created.
As in the gem' s github page, I have tried
#some_user.owned_taggings
#some_user.owned_tags
with no satisfactory results. I proceeded and added a user_id to the tags table. Is there a tagsController associated with the acts-as-taggable-on gem that I can modify with a before_save to set the user_id for the tag ?
Thanks!!
A section of the acts-as-taggable-on README dedicated to ownership, and is useful to work through with the specifics of your models.
But, I don't think the methods provided are correct -- they will apply all owned tags (owned by anyone, that is), to each owner<>item relationship. Here's how I'd do it:
DEFAULT_ACTSASTAGGABLEON_TYPE = :tag
module TagToOwner
extend ActiveSupport::Concern
private
def add_owned_tag(item, owner, tags_to_add, options = {})
own_tag(item, owner, arrayify(tags_to_add), "add", options)
end
def remove_owned_tag(item, owner, tags_to_add, options = {})
own_tag(item, owner, arrayify(tags_to_add), "subtract", options)
end
def own_tag(item, owner, tags_to_add, direction = "add", opts)
tag_type = (options[:tag_type] || DEFAULT_ACTSASTAGGABLEON_TYPE)
owned_tag_list = item.owner_tags_on(owner, tag_type).map(&:name)
if direction == "subtract"
owned_tag_list = owned_tag_list.reject{|n| n.in?(tags_to_add)}
else
owned_tag_list.push(*tags_to_add)
end
owner.tag(item, with: stringify(owned_tag_list), on: tag_type, skip_save: (options[:skip_save] || true))
end
def arrayify(tags_to_add)
return tags_to_add if tags_to_add.is_a?(Array)
tags_to_add.split(",")
end
def stringify(tag_list)
tag_list.inject('') { |memo, tag| memo += (tag + ',') }.chomp(",")
end
end
And:
class MyModelController < ApplicationController
include TagToOwner
# ...
def create
#my_model = MyModel.new(my_model_params)
add_tags
# ...
end
# ...
def update
add_tags
# ...
end
private
def add_tags
return unless params[:tag_list] && "#{params[:tag_list]}".split(",").any?
return unless validate_ownership_logic # <- e.g. `current_user`
add_owned_tag(#my_model, current_user, params[:tag_list])
end
end
Note I have filed an issue against acts-as-taggable-on, and a corresponding pull request, to correct their README.

push pop recenlty viewed session data with ruby on rails

The following code it taken from a spree_recently_viewed gem
Controller
after_action :recently_viewed, only: :show
def recently_viewed
id = #product.id
rvp = (session['recently_viewed_products'] || '').split(', ')
rvp.delete(id)
rvp << id unless rvp.include?(id.to_s)
rvp_max_count = 5
rvp.delete_at(0) if rvp.size > rvp_max_count.to_i
session['recently_viewed_products'] = rvp.join(', ')
end
Helper
module ProductsHelper
def cached_recently_viewed_products_ids
(session['recently_viewed_products'] || '').split(', ')
end
def cached_recently_viewed_products
Product.find_by_array_of_ids(cached_recently_viewed_products_ids)
end
end
Model
class Product < ActiveRecord::Base
def self.find_by_array_of_ids(ids)
products = Product.where('id IN (?)', ids)
ids.map { |id| products.detect { |product| product.id == id.to_i } }.compact
end
end
looking at this line in the controller
rvp.delete_at(0) if rvp.size > rvp_max_count.to_i
it is only replacing the value at index 0. Is there a way I can add push pop style so that when a new record is added value at 0 for example moves to 1 and 1 to 2 and so on and the last one gets poped out. and if already exist moves to index 0.
Use array #unshift method to push into the beginning of an array, and #shift to pop from the beginning of the array.
PS: According the whole controllers and models code. I don't know why both too comlpex... models method ::find.. can be simplified to just Product.where('id IN (?)', ids) or to Product.where(id: ids) (thanx to #bbozo) in the newer version of Rails.

routing and searching a db ruby

I want to retrieve all the tweets that have a certain hashtag in them.
At first I add the hashtags in my 2 tables :
def add_hashtags(tweet)
tweet.content.scan(/(?:\s|^)(?:#(?!(?:\d+|\w+?_|_\w+?)(?:\s|$)))(\w+)(?=\s|$)/){ |tag|
#allhashes = Hashtag.all
#hash = Hashtag.find_by_name(tag[0].strip)
unless #hash
#hashtag = Hashtag.new(name: tag[0].strip)
#hashtag.save
#hashrel = Hashrelation.new(tweet_id: tweet.id, hashtag_id: #hashtag.id)
#hashrel.save
else
#hashrel = Hashrelation.new(tweet_id: tweet.id, hashtag_id: #hash.id)
#hashrel.save
end
}
end
then I want to route to the show method of tweet controller :
get 'tweets/show/(.:format)' => 'tweets#show', as: :hashtag
The links in the hashtags are as follows:
def twitify(tweet = '')
tweet.gsub(/(?:\s|^)(?:#(?!(?:\d+|\w+?_|_\w+?)(?:\s|$)))(\w+)(?=\s|$)/) do |tag|
" " + link_to("#{tag.strip}", hashtag_path(tag.strip), {:name => tag.strip})
end.html_safe
end
And finally the show method of the tweet controller is :
def show
#hashtag = Hashtag.find_by_name(params[:name])
#tweet_ids = Hashrelation.find_by_hashtag_id(#hashtag.id)
#feed_items = Tweet.find_by_id(#tweets_ids.id)
end
When I click on the link I get :
undefined method `id' for nil:NilClass
which means that params[:name] is either nill or it isn't like the one I have in the DB.
Could you guys help me figure this out ?
The link I see that is called is 'http://localhost:3000/tweets/show/.%23dadawea' which means I have extra things why would I ?.
I would do the following
def add_hashtags(tweet)
tweet.content.scan(/(?:\s|^)(?:#(?!(?:\d+|\w+?_|_\w+?)(?:\s|$)))(\w+)(?=\s|$)/).flatten.each do |tag|
hashtag = Hashtag.where(name: tag.strip).first_or_create
Hashrelation.create(tweet_id: tweet.id, hashtag_id: hashtag.id)
end
end
Then change the twitify method to look like
def twitify(tweet = '')
tweet.gsub(/(?:\s|^)(?:#(?!(?:\d+|\w+?_|_\w+?)(?:\s|$)))(\w+)(?=\s|$)/) do |tag|
" " + link_to("#{tag.strip}", hashtag_path(name: tag.strip))
end.html_safe
end
And the show method
def show
#hashtag = Hashtag.find_by_name(params[:name])
#tweet_ids = Hashrelation.where(hashtag_id: #hashtag.id).pluck(:id)
#feed_items = Tweet.where(tweet_id: #tweets_ids)
end
This should be what you are looking for. Now for whats changing:
Removed Duplicate logic in the add_hashtags to use create instead.
twitify method is passing name as an html option not a url option so I fixed that. Right now it thinks you want to set the format to the name of the hashtag and name the link the name of the hashtag**
show method is using find_by which will only return a single result not what you wnat for tweet_ids so i changed it to where clause and just grabbed the ids. Then changes feed_items to search Tweet for all tweet_ids in the Array.
To strip the # just use tag.strip[1..-1]

Colons as divider with Rails Route?

I would like to use colons as divider in my rails route url instead of forward slashes. Is it possible to do the this?
I'm after something like the following
match '*page_path/:title ":" *section_path ":" :section_title' => 'pages#show'
so for the url food/fruit/apples:cooking:pie:apple_pie would return the parameters:
:page_path = "food/fruit"
:title = "apples"
:section_path = "cooking:pie"
:section_title = "apple_pie"
Is this possible in rails?
Here's an approach :
match 'food/fruit/:id' => 'pages#show' # add constraints on id if you need
class Recipe < ActiveRecord::Base
def self.from_param( param )
title, section_path, section_title = extract_from_param( param )
# your find logic here, ex: find_by_title_and_section_path_and_section_title...
end
def to_param
# build your param string here,
# ex: "#{title}:#{section_path}:#{section_title}"
# Beware ! now all your urls relative to this resource
# will use this method instead of #id.
# The generated param should be unique, of course.
end
private
def self.extract_from_param( param )
# extract tokens from params here
end
end
then in your controller:
#recipe = Recipe.from_param( params[:id] )
note that the use of use the built-in to_param method is optionnal.

HTML escaped in Rails 3

I have a method call in my view like this
<%= Navigation.with(params) do |menu|
if current_user && current_user.can_verify?
menu.item("Listings", manage_listings_path())
menu.item("Listing changes", needing_change_approval_manage_listings_path())
menu.item("Flagged Items", flagged_manage_listings_path())
menu.item("Transfers", manage_listing_transfers_path())
menu.item("Reviews", manage_listing_reviews_path())
end
if current_user && current_user.admin?
menu.item("Log", manage_verifications_path())
menu.item("Indexer Compensations", manage_compensations_path())
menu.item("Users", manage_users_path())
end
end%>
that splits out the below string
"<li>Listings</li> <li>Listing changes</li> <li>Flagged Items</li> <li>Transfers</li> <li>Reviews</li> <li>Log</li> <li>Indexer Compensations</li> <li>Users</li>"
I just get this string in my page. I wanted them to be menus nicely styled by CSS. I am just getting the above raw text in my page. How do I convert this string to be treated as HTML by the browser.
Please help
Here is the navigation class
class NavigationMenu < ActionView::Base
def initialize(params)
#params = params
end
def item(title, path, options={})
#items ||= Array.new
unless (route = Rails.application.routes.recognize_path(path,:method => options[:method]|| :get))
raise "Unrecognised path #{path}, are you sure it's in routes.rb?"
end
#items << content_tag(:li, link_to(title,path, :class => (#params[:controller] == route[:controller] && #params[:action] == route[:action])? 'active' : nil))
end
def output
return '' if #items.blank?
content_tag(:ul, #items.join("\n"), :id => 'navigation')
end
end
class Navigation
def self.with(params, &block)
menu = NavigationMenu.new(params)
yield menu
menu.output
end
end
You have to add a call to the raw method:
<%= raw ... %>
This is necessary, because in Rails 3 every string is escaped by default, unless you use the raw method.
It's like an inverse of the h method in Rails 2, where every string is unescaped by default, unless you use the h method.
Example:
This code in Rails 2...
<%= h "String which must be escaped" %>
<%= "String which must be output raw %>
... must be this in Rails 3:
<%= "String which must be escaped" %>
<%= raw "String which must be output raw %>
(Although an additional call to h doesn't do any harm in Rails 3)
You need to append .html_safe to the string - this will stop rails from escaping it when it's time to output text. Probably best to put it in the item method that you call repeatedly.
I recently wrote an article regarding XSS protection in Rails 3 when upgrading from Rails 2:
http://developer.uservoice.com/entries/upgrading-to-rails-3-printing-escaped-strings
The idea is to hook code to printing HTML so that we can determine when we are actually printing something we don't want to:
module ActionView
module Helpers
module TextHelper
def simple_format_with_double_escape_reporting(*args)
HtmlDoubleEscapeReporter.assert_sane(simple_format_without_double_escape_reporting(*args))
end
alias_method_chain :simple_format, :double_escape_reporting
end
module TagHelper
private
def content_tag_string_with_double_escape_reporting(*args)
HtmlDoubleEscapeReporter.assert_sane(content_tag_string_without_double_escape_reporting(*args))
end
alias_method_chain :content_tag_string, :double_escape_reporting
end
module UrlHelper
def link_to_with_double_escape_reporting(*args, &block)
HtmlDoubleEscapeReporter.assert_sane(link_to_without_double_escape_reporting(*args, &block))
end
alias_method_chain :link_to, :double_escape_reporting
end
end
end
Method HtmlDoubleEscapeReporter.assert_sane can be written, for example, like this:
class HtmlDoubleEscapeReporter
def self.assert_sane(str)
if (str.match(/<[a-z]/) || str.match(/&(quot|rarr|larr|amp|#)/)) &&
!str.match(/looks something you do not want to print/
send_problem_report('#{str}' looks something you do not want to print")
end
return str
end
end
Here, 'looks something you do not want to print' is used to prevent the possibility of infinite loops. The line send_problem_report('#{str}' looks something you do not want to print") can be replaced with a call to "debugger" (from ruby-debug gem) so that you are able to check the backtrace and see where the problem is coming from.
Here is the new class. At last... I got that bug.
class NavigationMenu < ActionView::Base
def initialize(params)
#params = params
end
def item(title, path, options={})
#items ||= Array.new
unless (route = Rails.application.routes.recognize_path(path,:method => options[:method]|| :get))
raise "Unrecognised path #{path}, are you sure it's in routes.rb?"
end
#items << content_tag(:li, link_to(title,path, :class => (#params[:controller] == route[:controller] && #params[:action] == route[:action])? 'active' : nil))
end
def output
#items = #items.join("\n").html_safe
return '' if #items.blank?
content_tag(:ul, #items, :id => 'navigation')
end
end
class Navigation
def self.with(params, &block)
menu = NavigationMenu.new(params)
yield menu
menu.output
end
end

Resources