assigning random url to a resource in rails 3 - ruby-on-rails-3

I need to generate a random url to my Topic model(for example) as such:
http://localhost:3000/9ARb123
So how can I do this in rails?
Note: the random string must contain digits, small and capital letters.

Something like this perhaps
#config/routes.rb
match "/:random_id" => "topics#show", :constraints => {:random_id => /([a-zA-Z]|\d){3,6}/}
will match a random string of 3-6 random letters/numbers to the show method of your Topics controller. Make sure to declare other resources above this matcher, as something like "http://localhost:3000/pies" will route to Topics#show instead of Pies#index.
To generate a random url for your Topic you can go something like this:
#app/models/topic.rb
before_create :generate_random_id
def generate_random_id
#generates a random hex string of length 6
random_id = SecureRandom.hex(3)
end

Patricks answer should work - but it only covers routing incoming requests.
If you're still using the standard routes (eg topic_path) to create your links, it will still use the normal routes.
If you run rake routes, you should see the name of the route you created with with the random_id. (You may need to name it using :as => 'random_route')
If you call that instead of the standard topic_path you should get the route you are after

Related

How to send a query to the database using GET through an REST API (Ruby on Rails)

I would like to add a query parameter to a GET request such a way that my REST API returns the query's result instead of the result from default index method.
Is this possible?
Here is my index method:
def index
users = User.all
render(
json: ActiveModel::ArraySerializer.new(
users,
each_serializer: Api::V1::UserSerializer,
root: 'users'
)
)
end
I would like to have an additional method named my_new_index executable by a GET or I would like to have a query submitted as a parameter to the default index method, lets say something like this:
query = "select * from users where name like 'A%' order by name desc"
I'm not sure I understand exactly what you're trying to do but what I would suggest is using the same end point index to return your content filtered.
First I'll start by creating a scope like:
scope :starting_with, ->(letter) { where('users.name like ?', "#{letter}%") if letter }
Then update you index end point to something like:
def index
users = User.starting_with(params[:letter]).all
render(
json: ActiveModel::ArraySerializer.new(
users,
each_serializer: Api::V1::UserSerializer,
root: 'users'
)
)
end
In this quick example the end point receive the params, if it contains a letter params, it will render users filtered by the scope query. If the param is not present, it return all users.
FYI it's just a quick example and not perfect, I'm sure you could find ways improve it.
Hope it helps :)

Rails Order by frequency of a column in another table

I have a table KmRelationship which associates Keywords and Movies
In keyword index I would like to list all keywords that appear most frequently in the KmRelationships table and only take(20)
.order doesn't seem to work no matter how I use it and where I put it and same for sort_by
It sounds relatively straight forward but i just can't seem to get it to work
Any ideas?
Assuming your KmRelationship table has keyword_id:
top_keywords = KmRelationship.select('keyword_id, count(keyword_id) as frequency').
order('frequency desc').
group('keyword_id').
take(20)
This may not look right in your console output, but that's because rails doesn't build out an object attribute for the calculated frequency column.
You can see the results like this:
top_keywords.each {|k| puts "#{k.keyword_id} : #{k.freqency}" }
To put this to good use, you can then map out your actual Keyword objects:
class Keyword < ActiveRecord::Base
# other stuff
def self.most_popular
KmRelationship.
select('keyword_id, count(keyword_id) as frequency').
order('frequency desc').
group('keyword_id').
take(20).
map(&:keyword)
end
end
And call with:
Keyword.most_popular
#posts = Post.select([:id, :title]).order("created_at desc").limit(6)
I have this listed in my controller index method which allows the the order to show the last post with a limit of 6. It might be something similar to what you are trying to do. This code actually reflects a most recent post on my home page.

Detect hash tag and srip the # for clean url

consider the string
$tring = "e.g. i want to #sleep."
Am able to check for hash tags using
echo preg_replace('/(#\w+)/','\1',$tring']);
What i want to do is send the tag without the hash in front i.e. #sleep instead of #sleep
You can modify your regex slightly to make this work properly.
echo preg_replace('/(#)(\w+)/i', '\1\2', $tring);
This creates two groupings, instead of the one that you had. Only the second group, the word, is used in the URL link. Both are used in the display test.
Examples of how this will work
e.g. i want to #sleep. => e.g. i want to #sleep.
this is a #cow => this is a #cow
the #duck is #sleeping => the #duck is #sleeping
#regex are #awesome #fun => #regex are #awesome #fun
As you can see, this handles multiple tags in the string as well.

Reverse pagination with kaminari

I want to create pagination for a messaging system in which the first page shown contains the oldest messages, with subsequent pages showing newer messages.
For example, if normal pagination for {a,b,c,d,e,f,g,h,i} with 3 per page is:
{a,b,c}, {d,e,f}, {g,h,i}
Then reverse pagination would be:
{g,h,i}, {d,e,f}, {a,b,c}
I plan to prepend the pages so the result is the same as normal pagination, only starting from the last page.
Is this possible with kaminari?
Kaminary.paginate_array does not produce query with offset and limit. For optimization reason, you shouldn't use this.
Instead you can do this:
#messages = query_for_message.order('created_at DESC').page(params[:page]).per(3)
Where query_for_message stands for any query which you use to retrieve the records for pagination. For example, it can be all the messages of a particular conversation.
Now in the view file, you just need to display #messages in the reverse order. For example:
<%= render :collection => #messages.reverse, :partial => 'message' %>
<%= paginate #messages %>
There's a good example repo on Github called reverse_kaminari on github. It suggests an implementation along these lines (Source).
class CitiesController < ApplicationController
def index
#cities = prepare_cities City.order('created_at DESC')
end
private
def prepare_cities(scope)
#per_page = City.default_per_page
total_count = scope.count
rest_count = total_count > #per_page ? (total_count % #per_page) : 0
#num_pages = total_count > #per_page ? (total_count / #per_page) : 1
if params[:page]
offset = params[:page].sub(/-.*/, '').to_i
current_page = #num_pages - (offset - 1) / #per_page
scope.page(current_page).per(#per_page).padding(rest_count)
else
scope.page(1).per(#per_page + rest_count)
end
end
end
All credits go to Andrew Djoga. He also hosted the app as a working demo.
One way to solve this problem would be this one:
Reverse pagination with kaminari?
It does not look very clean nor optimal, but it works :)
Yes, but the method I have come up with isn't exactly pretty. Effectively, you have to set your own order:
Message.page(1).per(3).order("created_at DESC").reverse!
The problem with this approach is twofold:
First the reverse! call resolves the scope to an array and does the query, nerfing some of the awesome aspects of kaminari using AR scopes.
Second, as with any reverse pagination your offset is going to move, meaning that between two repeat calls, you could have exactly 3 new messages send and you would get the exact same data back. This problem is inherent with reverse pagination.
An alternative approach would be to interrogate the "last" page number and increment your page number down towards 1.

hyphen in rails 3 route

I would like to do the following
match "company/client-list" => 'companies#list'
however, when I do, my routes table entry doesn't name the route, like so.
/company/client-list(.:format) {:controller=>"companies", :action=>"list"}
as soon as I remove the hyphen in the matched route, it behaves as expected
company_clientlist /company/clientlist(.:format) {:controller=>"companies", :action=>"list"}
Anyone have any ideas how to include hyphens in my matched routes?
And I have the answer:
match 'company/client-list' => 'companies#list', :as => 'client_list'
This makes the following entry in my routes table
client_list /company/client-list(.:format) {:controller=>"companies", :action=>"list"}
Go Team!