I want to make different views for mobiles on rails 4 - ruby

I installed rack-user_agent gem and edited application_controller like below but doesn't work. Mine is rails4.
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
before_action :detect_mobile
private
def detect_mobile
case request.user_agent
when /mobile/i
request.variant = :mobile
end
end
end
app > views > pages > home.html.erb & mobile.html.erb
Does anyone know how to fix this? or someone knows better ways to suggest on this?

If the content all almost same foe web and mobile view then you have to use same html code just use css #media screen
w3schools css
If you want a particular file render under mobile view only then you can use rack-user_agent gem
just create an helper in application_helper.rb
def is_mobile?
request.from_smartphone?
end
and put use this helper in views where you need to render file only on mobile like:
<%=
if is_mobile?
render "mobile_file"
end
%>
If you have two different views for mobile and web and you want controller will return views based on the variant
then you can use like
class YourController < ApplicationController
def your_method
#your code
if view_context.is_mobile?
render "mobile_view"
else
render "web_view"
end
end
end

Try this Gem ......
gem 'mobile-fu'
https://github.com/brendanlim/mobile-fu
Hope this will work for you.

Related

Sinatra route function call & helpers

So I have two classes like this.
class ApplicationController < Sinatra::Base
# don't enable logging when running tests
configure :production, :development do
enable :logging
end
get '/*' do
$request = request
PageController::render
end
end
and
class PageController < ApplicationController
def self.render()
#page = Page.find_by permalink: $request.path_info
if #page then
else
halt 400
end
end
end
All is well, until I reach the halt statement. Method not found. How could I use the Sinatra halt helper from inside this function call?
You've overcomplicated things. See the Helpers section of docs.
Put this in your Application controller:
helpers do
def render
#page = Page.find_by permalink: request.path_info
if #page then
else
halt 400
end
end
end
Now your route will be:
get '/*' do
render
end
Still, too complicated if you ask me, no need to ape Rails. Why not keep it simple?
require 'sinatra'
get '/*' do
#page = Page.find_by permalink: request.path_info
if #page then
haml :something
else
halt 400
end
end
That's it, that's the whole Sinatra app without recourse to inheritance and a structure that isn't required. Unless you're adding pages dynamically after the app is deployed then I'd also define the routes more explicitly.
Don't use globals. I actually can't remember the last time I saw one used, there are so many better alternatives. If you find you need one it's a clue you're going down the wrong path.

How to map routes to controllers in Sinatra?

I'd like to create a simple experimental MVC framework using Sinatra.
I'd like to define resources by name "pages" for example should resolve to:
/pages (index)
/pages/new
/pages/:id/show (show)
as WELL as map to app/controllers/PagesController.rb with corresponding get('/') to be responsible for the index, post('/pages/create') be responsible for creation, etc.
Trouble is even after reading the official documentation I'm terribly confused. I imagine I need to use non-classic Sinatra model for this, but could anyone point me in the right direction?
Thank you
If you want what I think you're wanting, I do this all the time. Initially for this scheme I used the travis-api source as a reference, but essentially what you want to do is extend Sinatra::Base in a "controller" class and then mount up your individual Sinatra "controllers" in rack, something like this:
module Endpoint
def self.included(base)
base.class_eval do
set(:prefix) { "/" << name[/[^:]+$/].downcase }
end
end
end
class Users < Sinatra::Base
include Endpoint
get '/' do
#logic here
end
get '/:id' do
#logic here
end
post '/' do
#logic here
end
patch '/:id' do
#logic here
end
end
class Posts < Sinatra::Base
include Endpoint
post '/' do
#logic here
end
end
and then something like this:
class App
require "lib/endpoints/users"
require "lib/endpoints/posts"
attr_reader :app
def initialize
#app = Rack::Builder.app do
[Users, Posts].each do |e|
map(e.prefix) { run(e.new) }
end
end
end
def call(env)
app.call(env)
end
end
You can adjust this to whatever you need, but the idea is the same, you separate your app into composable Sinatra applications that each have a prefix that they are mounted under using Rack. This particular example will give you routes for:
get '/users'
get '/users/:id'
post '/users'
patch '/users/:id'
get '/posts'
I'll give you a very simple example here:
Create a file controller.rb
get '/pages' do
#pages = Pages.all
erb :pages
end
Next create a views directory in the same folder as teh controller, and create a file named pages.html.erb
This is the corresponding view to your previously created controller action.
Here, you can type something like:
<% #pages.each do |p| %>
<%= p.title %>
<% end %>
Restart your server, visit localhost:PORT/pages and you will see a list of all your page titles.
You can check out this link for a simple sinatra tutorial - http://code.tutsplus.com/tutorials/singing-with-sinatra--net-18965
You can make this as complicated or as simple as you need. For example:
Rails makes a lot of magic happen under the hood, whereas Sinatra is more flexible at the cost of requiring you to implement some of this stuff yourself.
controller_map = {
'pages' => PagesController
}
post '/:controller/new' do
c = params[:controller]
module = controller_map[c]
module.create_new()
...
end
get '/:controller/:id/show' do
c = params[:controller]
id = params[:id]
module = controller_map[c]
module.get(id)
...
end

Versioning model serialization

I've a model and 2 controllers as follow :
class MyModel < ActiveRecord::Base
def serializable_hash(options={})
super(only: [:id, :foo])
end
end
module V1
class MyController < ApplicationController
def show
render json: {my_model: #my_model}
end
end
end
module V2
class MyController < ApplicationController
def show
render json: {my_model: #my_model}
end
end
end
I want to be able to return a different json depending on the controller :
class MyModel < ActiveRecord::Base
def serializable_hash(options={})
# If V1
super(only: [:id, :foo])
# ElsIf V2
super(only: [:id, :bar])
# End
end
end
I would like to find a generic solution, so I don't have to send the version manually in parameters.
It would be better to decouple the serialization from your model i.e. don't put the serialization code in the model. You have a few options: Using a json builder in your views directory, or using ActiveModelSerializers. Both approaches will make it easy to version your serialization code:
JSON builders:
# app/views/v1/my_controller/my_model.json.builder
json.my_model do
json.id #my_model.id
json.foo #my_model.foo
end
With the above set up you can imagine easily adding a v2 directory with a different serializer.
In your controller you don't even need to specify a render call since rails will notice you have a builder in your controller's view directory. More info about jbuilder in this Railscast
ActiveModelSerializers:
Same goes with serializers, they are just files in app/serializers. You can put your files in app/serializers/v1/my_model_serializer.rb.
The concept with these two approaches is to use Rails modular paths to version your files. A controller in app/controller/v1 will load files from other directories with the same v1: app/serializers/v1.
Here's the railscast for active model serializers. And the asciicast.
Also, here's a gem that helps with api versioning: https://github.com/EDMC/api-versions I use it and find it nice to work with.

How can I use views and layouts with Ruby and ERB (not Rails)?

How can I use views and layouts with Ruby and ERB (not Rails)?
Today i'm using this code to render my view:
def render(template_path, context = self)
template = File.read(template_path)
ERB.new(template).result(context.get_binding)
end
This works very well, but how can I implement the same function, but to render the template inside a layout? I want to call render_with_layout(template_path, context = self), and so that it will have a default layout.
Since you tagged it with Sinatra I assume that you us Sinatra.
By default you view is rendered in your default layout called layout.erb
get "/" do
erb :index
end
This renders your view index with the default layout.
If you need multiple layouts you can specify them.
get "/foo" do
erb :index, :layout => :nameofyourlayoutfile
end
* If you don't use Sinatra you may want to borrow the code from there.
If you use the Tilt gem (which I think is what Sinatra uses) you could do something like
template_layout = Tilt::ERBTemplate.new(layout)
template_layout.render {
Tilt::ERBTemplate.new(template).render(context.get_binding)
}
If you are using Sinatra so it has a good docimentation and one of the topics it's nested layouts (see Sinatra README)
Also good idea to use special default layout file (layout.haml or layout.erb in your view directory) This file will be always use to render others. This is example for layout.haml:
!!!5
%html
%head
##<LOADING CSS AND JS, TILE, DESC., KEYWORDS>
%body
=yield ## THE OTHER LAYOUTS WILL BE DISPALYED HERE
%footer
# FOOTER CONTENT
Thanks for all the answers!
I solved it finally by doing this, I hope someone else also can find this code useful:
def render_with_layout(template_path, context = self)
template = File.read(template_path)
render_layout do
ERB.new(template).result(context.get_binding)
end
end
def render_layout
layout = File.read('views/layouts/app.html.erb')
ERB.new(layout).result(binding)
end
And I call it like this:
def index
#books = Book.all
body = render_with_layout('views/books/index.html.erb')
[200, {}, [body]]
end
Then it will render my view, with the hardcoded (so far) layout..

Call Sinatra erb from another class

I need to render a Sinatra erb template inside a class in my controller. I'm having issues calling this though. I've looked in the Sinatra rdocs and have come up with this:
Sinatra::Templates.erb :template_to_render
When I do this, I get the following error:
undefined method `erb' for Sinatra::Templates:Module
Is there a way to call this from another class?
To imitate rendering behavior of Sinatra controller in some other class (not controller) you can create module like this:
module ErbRender
include Sinatra::Templates
include Sinatra::Helpers
include Sinatra::ContentFor
def settings
#settings ||= begin
settings = Sinatra::Application.settings
settings.root = "#{ROOT}/app"
settings
end
end
def template_cache
#template_cache ||= Tilt::Cache.new
end
end
Here you may need to tune settings.root
Usage example:
class ArticleIndexingPostBody
include ErbRender
def get_body
erb :'amp/articles/show', layout: :'amp/layout'
end
end
This will properly render templates with layouts including content_for
why you don't require 'erb' and after use only erb
## You'll need to require erb in your app
require 'erb'
get '/' do
erb :index
end
You could have your class return the template name and render it in the main app.
Of course that's not exactly an answer (I don't have enough rep to add a comment with this account) and you're probably doing just that by now anyway...

Resources