How can I register a service worker in Ruby on Rails? - ruby-on-rails

I'm trying register a service worker in Ruby On Rails to implement a push notification GCM. But nothing happens. Please, see my code below:
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/assets/service-worker.js')
.then(initialiseState);
} else {
window.Demo.debug.log('Service workers aren\'t supported in this browser.');
This part works fine. But, when I call navigator.serviceWorker.ready.then(function(serviceWorkerRegistration), nothing happens.
I was searching on search engine and there are few things to help me.
Can anybody help me ?

Check out the serviceworker-rails gem.
As Jeff mentioned, the path on which the service worker must be "in scope". This gem allows you to proxy service worker requests at the root (or any other path) to a resource in the Rails asset pipeline.
For example, if your serviceworker script entry point is in app/assets/javascripts/nested/directory/serviceworker.js in your Rails project, you can configure your application to render the script from /serviceworker.js with some simple configuration setup:
# config/application.rb
config.serviceworker.routes.draw do
match "/serviceworker.js" => "nested/directory/serviceworker.js"
end

The service worker script, service-worker.js in your case, needs to be served from either the same-directory URL path as the page that's registering it, or a sub-path.
You're using the path /assets/service-worker.js, so unless the registering page is also served from /assets/, it's going to fail. If you put a .catch(function(error) { console.warn(error); }) at the end of your register() (which returns a Promise, you would see the error reported there.
The easiest thing to do is make sure that service-worker.js is served out of the same directory as the web page, and then call register('service-worker.js'), which uses a same-directory relative path.

Related

Apartment gem - Adding current database to all server logs

I am using the Apartment gem to switch the tenant (database) being used for a multi tenancy Rails application.
In my server logs I would like to output the current tenant (database) being used for every single line in the log file.
When I do rails s the server never actually starts with the code below that is in the initializers directory. The server just hangs... so odd. No error message and no running server. If I take out #{Apartment::Tenant.current} below everything is fine... but... I really want to know the current tenant (database) in my log files.
/initializers/log_formatting.rb:
class ActiveSupport::Logger::SimpleFormatter
def call(severity, time, progname, msg)
"#{Apartment::Tenant.current} #{msg.strip} (pid:#{$$})\n"
end
end
Any ideas on how to get the current tenant (database) being used output to every line of my log file?
Thank you!
I would suggest you to use log_tags.
From the rails documentation :
config.log_tags accepts a list of: methods that the request object responds to, a Proc that accepts the request object, or something that responds to to_s. This makes it easy to tag log lines with debug information like subdomain and request id - both very helpful in debugging multi-user production applications.
You can add this configuration in application.rb or production.rb whichever fits your need.
For ex: config.log_tags = [ :subdomain, :request_id, lambda { |request| request.headers["tenant_name"] } ]
Note: In case you are adding this for development environment and you are running on your_subdomain.localhost:3000 then subdomain won't be present as localhost doesn't support subdomains. There are some workarounds like modifying /etc/hosts file but i won't recommend it. The more cleaner solution is to use your_subdomain.lvh.me:3000

Actionmailer URL adds subdir twice

I have a pretty standard rails 5.1 app which is mounted under a subdir under our main domain:
https://www.company.com/subir/
This is done in routes.rb
scope :subdir
the other routes
end
I use NGINX in our DMZ to pass incoming requests to my application server:
...
server_name www.company.com;
location /subdir {
proxy_pass https://my-app-server;
}
...
on my app-server is the pretty common combo Nginx/Puma installed and almost everything works fine, except the urls which are in the emails I send via actionmailer.
In my view I have a link:
link_to 'approve customer', admin_customer_url(#customer)
This creates the following:
https://www.company.com/**subdir/subdir**/admin/customer/:id
On my local machine these links are generated correctly in my emails, but not in staging environment on my app-server.
I dumped the request object in a view to see if my nginx setup is crazy but there is nothing obviously crazy...
Any ideas?
Check if you have config.action_mailer.default_url_options set in your environment file.
http://api.rubyonrails.org/classes/ActionMailer/Base.html
For the records: in deploy/staging.rb I still exported ENV['RAILS_RELATIVE_URL_ROOT']. This added the subdir twice to app.base_path and this was causing my issue.

How do I prevent my routes.rb from intercepting requests coming to /blog?

My /blog directory is just a bunch of static HTML files. That's good.
When I go to localhost/blog it works fine - it renders the index.html for my middleman generated blog. Great.
But when I click on any of the posts, it gives me a routing error:
No route matches [GET] "/blog/2015/03/11/hello_world"
I am pretty sure the reason this is happening is because of one of these rules in my routes.rb:
get '/:friendly_id', to: 'posts#show'
get '/rbt/:name', to: redirect {|path_params, _| "/#{path_params[:name].gsub(/^\d+\-/, '')}" }
get ':name', to: 'posts#show'
I need all of these routes, but I don't want an HTML request to hit my Rack middleware unnecessarily....or worse yet, do a DB query which this error seems to suggest is happening.
How do I confine all requests to /blog/ to just resolve to my public/blog/ directory?
Edit 1
I realize the above description may not be clear. My Rails App isn't a blog, and so the posts you see referenced above, are not posts to the blog. They are posts of another kind, separately managed by the Rails app with a DB and all. I have since added a real /blog which will just be a collection of HTML articles generated by MiddleMan that will sit in my Rails /public/blog folder. The idea being that the HTML files in my /blog directory, should not hit my Rack middleware at all.
You can force rack to serve certain folder as static and routes-ignoring by adding config.middleware.use Rack::Static, urls: ['/blog'], root: 'public' to config/application.rb but imho it's better to setup a web server to intercept and serve /blog earlier than your app does.
And also in your case /blog/2015/03/11/hello_world seems to be a directory name, if you add index.html to the link it should work as you expect, without changing any configuration.
When your app is run by webserver, its webroot will be the public directory. So, if you have your blog directory inside public directory this should work. localhost/blog
Try running with nginx with following config :
root /root/path/to/your_app/public;
Couldn't you create a static_url controller and configure your routes so that
get '/blog', to: 'static_url#show'
Then in your controller have
def show
render file: request.fullpath
end
Sorry I can't test right now but I'm sure you get the idea. Also, I guess you would need to detect non-existent pages.

How to override public folder path in Rails 4?

I would like to use a different public folder from a parent directory called client which contains the entire AngularJS app. Essentially I want to tell Rails to load AngularJS app and the only job that Rails has to do is serve JSON.
Is that possible in Ruby on Rails?
As others have mentioned, it may or may not be a great idea to override the existing paths['public'] folder. But you can do the following safely in somewhere like application.rb:
Rails.application.config.middleware.insert_after(
ActionDispatch::Static,
ActionDispatch::Static,
Rails.root.join("client").to_s,
Rails.application.config.static_cache_control
)
The public folder is exposed to the web server through the Rack middleware ActionDispatch::Static. There's nothing else special about it, and the above code simply adds another instance of the middleware that points to the directory client. So in the above case, the browser would be able to access everything in public as well as client.
Just had to do it myself for an Angular app.
Put this in your application.rb:
config.serve_static_files = true
paths['public'] = File.join 'client', 'app'
Or if you still use asset pipeline (config.assets.enabled = true):
paths['public/javascripts'] = File.join 'client', 'app', 'scripts'
paths['public/stylesheets'] = File.join 'client', 'app', 'styles'
Would be interesting to know if there are any consequences with the second bit as my front-end is served completely separately thus I keep asset pipeline switched off and use grunt instead.
You can define another path like
# config/application.rb
paths['my_website'] = 'website'
Then you can use this path in your routes like
# routes.rb
get '/my_website', to: redirect('my_website/index.html')

Re-entrant subrequests in Rack/Rails

I've got a couple Engine plugins with metal endpoints that implement some extremely simple web services I intend to share across multiple applications. They work just fine as they are, but obviously, while loading them locally for development and testing, sending Net::HTTP a get_response message to ask localhost for another page from inside the currently executing controller object results in instant deadlock.
So my question is, does Rails' (or Rack's) routing system provide a way to safely consume a web service which may or may not be a part of the same app under the same server instance, or will I have to hack a special case together with render_to_string for those times when the hostname in the URI matches my own?
It doesn't work in development because it's only serving one request at a time, and the controller's request gets stuck. If you need this you can run multiple server locally behind a load balancer. I recommend using Passenger even for development (and the prefpane if you are on OS X).
My recommendation for you is to separate the internal web services and the applications that use them. This way you do not duplicate the code and you can easily scale and control them individually.
This is in fact possible. However, you need to ensure that the services you call are not calling each other recursively.
A really simple "reentrant" Rack middleware could work like this:
class Reentry < Struct.new(:app)
def call(env)
#current_env = env
app.call(env.merge('reentry' => self)
end
def call_rack(request_uri)
env_for_recursive_call = #current_env.dup
env_for_recursive_call['PATH_INFO'] = request_uri # ...and more
status, headers, response = call(env_for_recursive_call)
# for example, return response as a String
response.inject(''){|part, buf| part + buf }
end
end
Then in the calling code:
env['reentry'].call_rack('/my/api/get-json')
A very valid use case for this is sideloading API responses in JSON
format within your main page.
Obviously the success of this technique will depend on the sophistication
of your Rack stack (as some parts of the Rack env will not like being reused).

Resources