Scope or ajax for queries - ruby-on-rails

I am not to sure i understand correctly scope in rails. Here what i am trying to do I have a model call article and called tags.
To start off the application it log you in at Article#index action which show all articles. However what I want to be able to refine my view by clicking on a tags that would dynamically be created. The query i would like would be Article.find(:all, :conditions => ['tags = ?', 'world'])
How can i achieve this? Or should i just use ajax to do this?

I think ajax makes sense in this case.
You could have something like this.
routes.rb
match "/articles/tag" => "articles#tag"
articles_controller.rb
def tag
#particularly_tagged_links = Link.find_all_by_tags(params[:tags])
end
application.js or wherever you are using jquery codes. And trig this function when the user clicks the tag or something.
function taggit(tag) {
$.ajax({
url: "articles/tags?"+tag
});
}

Related

How to organize business logic and js logic, for voting in Rails?

In rails, I want to handle the visual vote icons and the database record upon clicking on a vote button. It seems like it would be a good idea to have both of these in one authoritative location (so that they don't go out of sync), rather than duplicating the control flow logic for the front and back ends in different files.
There is some logic as follows, and I need to handle the front end aspect and the back end aspects.
What's he best way to do this in rails?
There is a poll, and there are different options. Each option is given a scope id.
The following code handles intuitive voting behavior for an option that allows only one option.
model method called by controller
if current_user voted_for? #votable
#votable.unvote_by current_user
#votable.vote_by current_user, scope: params[:vote_type] unless current_user voted_for? #votable, scope: params[:vote_type]
else
#votable.vote_by current_user, scope: params[:vote_type]
end
Now this is fine for the back end, and I need front-end.
asset javascript file
// Detect if record was voted for by current user, obtain #id
// add class 'voted' to child with matching #id
$('#poll .option').on(click, ->
if ($('this').first().class('voted') ) {
$('this').first().removeClass('voted');
} else if ( $('this').siblings('option').first().class('voted') ){
$('this').siblings('option').first().removeClass('voted');
} else {
$('this').first().addClass('voted');
}
Will this work properly with a rails remote: true link?
html.haml
#poll
= link_to (content_tag :div, class: #voted), vote_path(:vote_type)
= link_to "", vote_path(vote_type: "2"), class=
Using acts_as_votable API to conditionally set class in view. Use CSS to style 'voted'
controller
def show
# can be moved to model
if current_user.voted_on? #votable
#voted = 'voted'
else
#voted = ''
end
end
I have not used ajax calls in the above. Do I need to?
The above javascript seems like it would get very messy very quickly, if I use ajax.
It also doesn't prevent multiple voting, or the visual votes going out of sync with what's actually in the database.
Now the above so far duplicates the if/else control flow in the back and front ends.
But is it better to combine them in a js.erb file where it does both?
That's why I was thinking it might be better to combine things into one js.erb that handles both front and back. But that doesn't seem like good design either. Perhaps there is a way using ajax to put in more validations and increase robustness? Anyway, this is all just nice-to-haves. As long as it works, that's good.
It seems having a custom js.erb file is not good for using responds_with... I am confused. on how to proceed.
Sorry for the multiple questions within this question. I am just trying to implement an intuitive voting system, and it's not very easy to get bits and pieces of information from different sources.
Thanks in advance.
This is a very generic question. Aw as for the backend you can use some of the gems to do that as it would make your life easier something like acts_as_votable gem for rails.
Then include it at your ActiveRecord model:
class Record < ActiveRecord::Base
acts_as_votable
end
Now you will be able to use a lot of helper methods like:
#record = Record.create!(attributes)
#record.liked_by #user1
#record.downvote_from #user2
#record.vote_by :voter => #user4, :vote => 'bad'
#record.vote_by :voter => #user5, :vote => 'like'
As for your fronend, you can send some Ajax requests with the like of link_to remote: true or form_for remote:true. Then you can use RJS or even doing that manually on the client side using jQuery and change the state of the divs.
I'll demonstrate with code here:
In Controller you'll have an action something like:
def vote
#record = Record.find(params[:post_id])
#record.liked_by current_user
end
Html
<%= link_to 'like', vote_path(#record), class: 'vote', remote: true %>
JS
$('.vote')
.on('ajax:send', function () { $(this).addClass('loading'); })
.on('ajax:complete', function () { $(this).removeClass('loading'); })
.on('ajax:success', function (data) { $(this).html(data.count); });

Redirecting from Rails new form to edit if field exist already?

I have a requirement when creating a new Foo object and a unique field foo_id exist already, I have to redirect to the edit page for that foo_id.
I'm using jQuery autocomplete with the foo_id textbox already and was wondering what the best way to do this?
My initial thought is to return an extra isExist attribute in the JSON that gets returned to jQuery autocomplete and based onselecting a foo_id, I'll check the isExist attribute and redirect.
Is there a better way to do this? Also, if I need to do a redirect from JavaScript, should I generate the path from Rails?
Based on Vidya's answer and found results from other questions, this is what I did:
in Foo's controller added:
def checkFooExists
#foo = Foo.find_by_foo_id params[:foo_id]
if !#foo.nil?
flash[:notice] = 'You entered an existing Foo ID, so here is the edit page'
flash.keep(:notice)
render :js => "window.location = '" + edit_foo_path(#foo) + "'"
else
render :nothing => true, :status => 409
end
end
in routes change:
#resources :foos
resources :foos do
collection do
get 'checkFooExists'
end
end
in application.js, in the autocomplete event handler for select for textbox add one jQuery get line:
select: function(event, ui) {
$.get("/foos/checkFooExists", { foo_id: ui.item.foo_id });
$('#bar').val(ui.item.bar);
return false;
}
There are a lot of ways to do this, but an easy way would be to send an ajax GET request (on the change event for the autocomplete) with the id of the selected Foo instance to a controller endpoint. Do a lookup with the id. If an instance with the id exists, return a 200; otherwise, return a 404. So you would have success and error handlers respectively to handle those cases.
At this point, you can do many more things. Your REST endpoint could send the result of foo_path(foo) (remember you looked up foo) in the body of the 200 response. You could keep a list on the client side of all the different foo_path(:id)s corresponding to the choices in your autocomplete and pick the right one on success. Either way, you can then set document.location in your JavaScript to the correct path.
And probably a lot of other things others will point out I'm sure.

getScript jQuery path not working / Ruby on Rails

I've got a jQuery function that is called after a doubleclick on a list item.
app/assets/javascripts/tile/tile.js
$('#list > li').dblclick(function(){
// styling
$(this).toggleClass('liked');
// pass ID to controller
var movie_id = $(this).attr("data-id");
$.getScript("/likes.js");
});
Next to applying some new formats to said item my main goal is to make a database entry from my like controller. In this Railscast the index action from their comments controller gets called with this simple line.
$.getScript("/comments.js");
Additionally some JavaScript gets called from a index.js.erb file.
My first problem with understanding the example code from Railscasts is how they define the action. If I wanted to call the action createLike from my likes_controller how would I call it?
Secondly, my attempts so far have all failed because both the JavaScript file doesn't load and the action doesn't get called aswell.
Somehow I sense that I've messed up with the paths. Where should I locate the JavaScript files that should get called with the getScript function?
Files
app/assets/javascripts/likes/index.js.erb
console.log("Test");
app/controllers/likes_controller.rb
class LikesController < ApplicationController
protect_from_forgery
def index
Like.create(:user_id => current_user.id, :item_id => params[:id])
end
end
I believe the execution issue can be solved by moving index.js.erb from
app/assets/javascripts/likes/index.js.erb
to
app/views/likes
This is where Rails looks for templates to render (your script shouldn't be served by the asset pipeline). Rails tackles this through convention - your app automatically routes /likes to the index action.
If you want a more informative route, use the Rails routing guide to generate a new route and match it to the create_likes action in the Likes controller. Then,
$.getScript("/create_likes.js")
will know where to look
You can define action in controller like that:
class LikesController < ApplicationController
# another code
def createLike
# your action code
end
# another code
end
And you can call action like /likes/createLike.
In the folder PATH_TO_APP/app/views/likes create a file createLike.html.erb - there is will be a createLike view
Javascript files must be in the folder /PATH_TO_APP/public/javascripts
And best way to include javascript file is a javascript_include_tag like:
<%= javascript_include_tag "tile/tile.js" %>
tile.js file must be is into the /PATH_TO_APP/public/javascripts/tile directory.
And if you want to get javascript files with jQuery, you must put them in public/javascripts directory and call $.getScript('/javascripts/likes.js'); - there is an example.
P.S. I advise to look at getting started guide
The behavior you're wanting is different than what that specific Railscasts is addressing. It is specifically focused on the retrieving of new comments as they are created, without a page refresh. That is why you are running into issues following this guide.
First you will need to make sure you have a resources :likes in your config/routes.rb. From your code excerpt it looks like you are associating a like with a movie so make sure you make the route nested inside your resources :movies call. In the end your routes should look something like this:
resources :movies do
resources :likes
end
For the controller piece you will need to add a 'create' action to your controller. Assuming that your Movie model has_many :likes this is a simple version of what your action should look like:
def create
movie = Movie.find(params[:movie_id])
movie.likes.create(user_id: current_user.id)
end
You will also need to change your javascript code to make a post instead of a get request. That's because the http method is how Rails differentiates between a create and an index request as they both use the same url path (e.g. /comments.js). You will also need to have the url reflect that it's a nested resource within a movie. Here is modified version of your JS code with that change:
$('#list > li').dblclick(function() {
// Cached jquery this selector.
$this = $(this)
// pass ID to controller
var movie_id = $this.data('id');
$.post('/movies/' + movie_id + '/likes.js', function() {
$this.toggleClass('liked');
});
});
In regards to your .js.erb file, as stated by others, it should be placed in your app/views folder. However, due to your regular JS handling the logic you don't need to have it all.
This is just one strategy but there are quite a few other ways to handle JS interaction with Rails. If you want an example of using a js.erb (js.coffee in this case) view file you can take a look at this implementation. In that case all that is handling the click event is a link_to with the remote: true option which delegates it the jquery-ujs adapter.
Hope that helps!
This might not be close to your answer but I use $.getscript() to load those js/css files that i need once my page has rendered,which in turn improves the performance and reduces the page load time.This is the code I have used in my erb files.My shop_for_free_module.js resides in app/public/javascripts
<script type="text/javascript">
jQuery(document).ready(function($){
//this gives your protocol-http
var protocol = this.location.protocol;
//this gives your domain name-myshopptinsite.com
var host = this.location.host;
var initial_url = protocol + "//" + host;
$.getScript(initial_url + "/javascripts/eshop_js/shop_for_free_module.js");
});
</script>
...hope it helps.
try this out
Question: My first problem with understanding the example code from Railscasts is how they define the action. If I wanted to call the action createLike from my likes_controller how would I call it?
Answer:
class LikesController < ApplicationController
def create_like
Like.create(:user_id => current_user.id, :item_id => params[:id])
end
end
in routes.rb file
get '/create_like.js' => 'likes#create_like'
Question: Secondly, my attempts so far have all failed because both the JavaScript file doesn't load and the action doesn't get called aswell.
Anaswer: move app/assets/javascripts/likes/index.js.erb
code to
app/views/likes/create_like.js.erb
you need to pass item_id to
getScript method
$(document).ready(function() {
$('#list > li').dblclick(function(){
// styling
$(this).toggleClass('liked');
// pass ID to controller
var item_id = $(this).attr("data-id");
$.getScript("/create_like.js?item_id=" + item_id);
});
});

Creating VoteUp and VoteDown Buttons with Acts_as_Votable

I am new to RoR and I am trying to use the acts_as_votable plugin. I can see that there are methods such as
#object.vote :voter => #user, :vote => 'like'
But I don't know how to call this method based upon a button click on a view, which is my ultimate goal. The underlying methods exist, I just need to provide an action by the user.
There are two options. You can call the methods in a controller (or model) or you can do it with AJAX right there on the page. For something like voting, the AJAX method is probably more common, because it doesn't involve reloading the page.
Here's some simple examples of the sort of thing you want to do:
http://wowkhmer.com/2011/09/19/unobtrusive-ajax-with-rails-31/
http://stackoverflow.com/questions/10264453/is-it-possible-to-call-a-rails-helper-method-from-within-javascript
Also, take a look at the button_to docs:
http://api.rubyonrails.org/classes/ActionView/Helpers/UrlHelper.html#method-i-button_to

Displaying Posts by Tag - Ruby on Rails Blog

I have a Rails blog coming along quite nicely. However, I cannot seem to get my tagged articles to show up within their own page (for example, I'd like ONLY the articles tagged Arts & Entertainment to show up when that link is clicked).
I have a column in my scaffold model entitled tags. It takes a string. So
1) How do I go about accessing ONLY a specific tag? I tried something like the following:
def self.sports
find(:all, :tags => 'gainmuscle')
end
to no avail.
2) How do I get them to show up in the view?
Any help would be very much appreciated.
You can probably define a method inside your controller something similar to this :
def self.tagged_with
#articles = Article.tagged(params[:tag]).paginate(default_paginate_options)
end
Here tagged is a namedscope. you can neglect that but the action generates all the articles which are tagged with the tag selected as :param. Tag is an attribute of the model Article in this case.
1) here you could use the scope in the model:
class YourModel < ActiveRecord::Base
scope :by_tag, lambda{|tag| where(:tag => tag)}
...
end
And then in controller:
#collection = YourModel.by_tag("gainmuscle")
2) I would say the best way is to create a partial with html code for 1 single post, and then render it this way:
render :partial => 'post', :collection => #collection
(You can check partials usage here: http://apidock.com/rails/ActionView/Partials)
Just to mention another approach; there are quite a few gems to deal with this exact purpose.
This might be interesting, and personally I can vote for acts_as_taggable_on. This gives you quite a lot of options which might come in handy later on as well.

Resources