Using coffescript in rails views doesn't work properly - ruby-on-rails

I'm trying to use coffescript as views in Rails 3.2.11
I have create.js.coffee with the following lines:
is_valid = <%=#model.valid?%>
if is_valid
res = confirm("Are you sure you want to continue?")
if(res)
<%=#model.activate%>
window.location.href = "/blabla/models"
else
return
else
$('.form .field_with_errors').removeClass('field_with_errors')
jw_funcs.respond_with_error(<%=#response_invalid%>)
The problem is that the line of code <%=#model.activate%>
is executed every time. I think it depends on the fact that the erb engine runs independently from the coffee engine; If so, how can I do this ?

You really weren't expecting this coffee code to call your model method from the client's browser, were you?
Wrap #model.activate into its own controller action, which will be called by clients if the confirmation is given. Something like this:
res = confirm("Are you sure you want to continue?")
if(res)
$.ajax('/models/1234/activate', ...)
else
return

Related

Rails - can I call a controller method from my ActionCable coffeescript?

I'm building a quiz app that leads all participants synchroneously through multiple stages:
Phase 0: Quiz has not started
Phase 1: Current question is being displayed
Phase 3: Result for current question (correct answer vs given answer, statistics etc.) is being displayed
Phase 4: Result for entire quiz is being displayed
If a participant loses his connection and reconnects, I want them to be able to immediately pick up where they left off.
I always save the current phase to the db. So I'm thinking of calling a method show_current_phase(curent_user) in the connected method of my ActionCable channel. Since I feel like it would make sense to place this method in the corresponding controller for my custom QuizSession model, I'd like to know:
Is it possible to call a controller method in my coffeescript (frontend) and have it return an object?
Or how do I update the DOM tree of only the user who reconnected without broadcasting to all the other participants?
You can just turning your .coffee file into an .haml or .erb template.
.erb example with js
# myscript.js
var myPath = '/some_path';
Will become
# myscript.js.erb
var myPath = <%= some_controller_path %> ;
.haml example with coffee
In case you are using haml, the process is not really different.
# myscript.coffee
myPath = '/some_path'
Will become
# myscript.coffee.haml
myPath = '#{ some_controller_path }'
Obviously, you can create .js.haml and .coffee.erb files as well.
In your case, you could have something like this in your .coffee.haml:
myFunction = ->
res = $.ajax(
url: '#{method_controller_path}'
method: 'GET'
data:
variableParam: '#{#my_controller_var}'
anotherParam: 'anotherParam'
dataType: 'json'
success: (res) ->
res
)
doSomething res

Nokogiri Timeout::Error when scraping own site

Nokogiri works fine for me in the console, but if I put it anywhere... Model, View, or Controller, it times out.
I'd like to use it 1 of 2 ways...
Controller
def show
#design = Design.find(params[:id])
doc = Nokogiri::HTML(open(design_url(#design)))
images = doc.css('.well img') ? doc.css('.well img').map{ |i| i['src'] } : []
end
or...
Model
def first_image
doc = Nokogiri::HTML(open("http://localhost:3000/blog/#{self.id}"))
image = doc.css('.well img')[0] ? doc.css('.well img')[0]['src'] : nil
self.update_attribute(:photo_url, image)
end
Both result in a timeout, though they work perfectly in the console.
When you run your Nokogiri code from the console, you're referencing your development server at localhost:3000. Thus, there are two instances running: one making the call (your console) and one answering the call (your server)
When you run it from within your app, you are referencing the app itself, which is causing an infinite loop since there is no available resource to respond to your call (that resource is the one making the call!). So you would need to be running multiple instances with something like Unicorn (or simply another localhost instance at a different port), and you would need at least one of those instances to be free to answer the Nokogiri request.
If you plan to run this in production, just know that this setup will require an available resource to answer the Nokogiri request, so you're essentially tying up 2 instances with each call. So if you have 4 instances and all 4 happen to make the call at the same time, your whole application is screwed. You'll probably experience pretty severe degradation with only 1 or 2 calls at a time as well...
Im not sure what default value of timeout.
But you can specify some timeout value like below.
require 'net/http'
http = Net::HTTP.new('localhost')
http.open_timeout = 100
http.read_timeout = 100
Nokogiri.parse(http.get("/blog/#{self.id}").body)
Finally you can find what is the problem as you can control timeout value.
So, with tyler's advice I dug into what I was doing a bit more. Because of the disconnect that ckeditor has with the images, due to carrierwave and S3, I can't get any info direct from the uploader (at least it seems that way to me).
Instead, I'm sticking with nokogiri, and it's working wonderfully. I realized what I was actually doing with the open() command, and it was completely unnecessary. Nokogiri parses HTML. I can give it HTML in for form of #design.content! Duh, on my part.
So, this is how I'm scraping my own site, to get the images associated with a blog entry:
designs_controller.rb
def create
params[:design][:photo_url] = Nokogiri::HTML(params[:design][:content]).css('img').map{ |i| i['src']}[0]
#design = Design.new(params[:design])
if #design.save
flash[:success] = "Design created"
redirect_to designs_url
else
render 'designs/new'
end
end
def show
#design = Design.find(params[:id])
#categories = #design.categories
#tags = #categories.map {|c| c.name}
#related = Design.joins(:categories).where('categories.name' => #tags).reject {|d| d.id == #design.id}.uniq
set_meta_tags og: {
title: #design.name,
type: 'article',
url: design_url(#design),
image: Nokogiri::HTML(#design.content).css('img').map{ |i| i['src']},
article: {
published_time: #design.published_at.to_datetime,
modified_time: #design.updated_at.to_datetime,
author: 'Alphabetic Design',
section: 'Designs',
tag: #tags
}
}
end
The Update action has the same code for Nokogiri as the Create action.
Seems kind of obvious now that I'm looking at it, lol. I dwelled on this for longer than I'd like to admit...

Rails code segment explained

I am working on an app which has the below code:
def app
#app ||= begin
if !::File.exist? options[:config]
abort "configuration #{options[:config]} not found"
end
app, myoptions = Rack::Builder.parse_file(self.options[:config], opt_parser)
self.myoptions.merge! myoptions
app
end
end
I am struggling to get my head around several parts of it..
#app||= begin...end
Does this mean that if #app does not exist the block is run?
app ,options = rack::builder
What does the comma do to it?
Please help
Your first assumptions was correct, it does say that if #app is nil, set it to whatever is returned in the block delimited with begin, end.
Regarding the comma, it works like this:
avar, bvar = "atest", "btest"
If you look at the source for Rack:Builder.parse_file then you will notice the last line
return app, options
So it is returning two values.
Hope that helps
#Craig-Taub ansewered the question,
I just want to add some notes:
Ruby commands are expressions which means they return value and you can assign them to variables.
You can read more about expressions and statements on Wikipedia and PragProg.
Second is that when you return more than one value in a code block, Ruby will wrap it into a simple array and return it to the caller.
That's why it works like that.

URL manipulations in views

I have a code which generates a URL which is like this: #{URI.escape p.url(checkout_path)}
Now I need to check a condition where, if #{URI.escape p.url(checkout_path)} generates a URL = "http://mywebsite.com" then append /trunk to the end so it must be "http://mywebsite.com/trunk" else if it already has /trunk appended from before then it should be "http://mywebsite.com".
Thus finally if http://mywebsite.com then http://mywebsite.com/trunk
elsif
http://mywebsite.com/trunk then http://mywebsite.com
But I want to know how to do it using #{URI.escape p.url(checkout_path)}
I would throw this in a helper method somewhere, but you could effectively do something like this:
URI.escape(p.url(checkout_path)) =~ /\/trunk$/ ? URI.escape(p.url(checkout_path)).gsub('/trunk', '') : "#{URI.escape(p.url(checkout_path))}/trunk"

Rendering HTML files in Grails

I've looked around but could not find a way of simply including or rendering *.html files in Grails. My application needs to g.render or <g:render> templates which are delivered as html files. For this, as we know, html files have to be converted to _foo.gsp files in order to get rendered. I am totally surprised as to why isn't there a direct support for html or is there one??
Thanks!
One obvious option is to simply rename your HTML files from foo.html to _foo.gsp and then use <render template="foo">. However this is so obvious that I'm sure you've already thought of it.
If you simply want to render a HTML file from within a controller you can use the text parameter of the render controller method
def htmlContent = new File('/bar/foo.html').text
render text: htmlContent, contentType:"text/html", encoding:"UTF-8"
If you want to do the same thing from within a .gsp, you could write a tag. Something like the following (untested) should work:
import org.springframework.web.context.request.RequestContextHolder
class HtmlTagLib {
static namespace = 'html'
def render = {attrs ->
def filePath = attrs.file
if (!file) {
throwTagError("'file' attribute must be provided")
}
def htmlContent = new File(filePath).text
out << htmlContent
}
}
You can call this tag from a GSP using
<html:render file="/bar/foo.html"/>
What is it you are trying to accomplish?
Render html from a controller?
In that case, all you should have to do is redirect the user to file from your control.
redirect(uri:"/html/my.html")
Use html-files instead of gsp template-files?
Thing is, Grails is a "Convention over Configuration"-platform and that means you will have to do some things "the Grails way". The files needs the _ and the .gsp but the name can be whatever you like even if it's easier when you use the same name as the controller. What you gain from doing that is the knowledge that every developer that knows grails and comes into your project will understand how things are tied together and that will help them get started quickly.
Little bit fixed the Don's example, works fine for me
import org.apache.commons.io.IOUtils
class HtmlTagLib {
static namespace = 'html'
def render = {attrs ->
def filePath = attrs.file
if (!filePath) {
throwTagError("'file' attribute must be provided")
}
IOUtils.copy(request.servletContext.getResourceAsStream(filePath), out);
}
}
I wanted to write static html/ajax pages hosted in grails app (v2.4.4), but use the controller for the url rewrite. I was able to accomplish this by moving the file to web-app/ (for ease of reference), and simply use the render() method with 'file' and 'contentType' params, such as:
// My controller action
def tmp() {
render file: 'web-app/tmp.html', contentType: 'text/html'
}
Note: I only tried this using run-app, and haven't packaged a war and deployed to tomcat, yet.
Doc: http://grails.github.io/grails-doc/2.4.4/ref/Controllers/render.html
Closure include = { attr ->
out << Holders.getServletContext().getResource(attr.file).getContent();
}
It is relative to the web-app folder of your grails main application
I was able to use #momo's approach to allow inclusion of external files for grails rendering plugin, where network paths would get botched in higher environments - mine ended up like:
def includeFile = { attr ->
URL resource = Holders.getServletContext().getResource(attr.file)
InputStream content = resource.getContent()
String text = content?.text
log.info "includeFile($attr) - resource: $resource, content.text.size(): ${text?.size()}"
out << text
}

Resources