What is the difference between using view templates in Rails as '.html.haml' and as simply '.haml'?
For example,
show.html.haml
vs
show.haml
Are there any particular advantages of using one over the other? Or is one of them a standard practice? The same question goes for other types of templates as well, like '.erb'.
The format matters when you're using Rails' automatic responders. See http://api.rubyonrails.org/classes/ActionController/MimeResponds.html#method-i-respond_with.
So for example, with:
class CarsController < ActionController::Base
respond_to :html, :js
def index
respond_with Car.all
end
end
The app will render app/views/cars/index.html.erb and app/views/index.js.erb when viewing http://localhost:3000/cars and http://localhost:3000/cars.js, respectively.
Additionally, this is an established convention in Rails-land, so it's just a good idea to do this anyway, even if you're not using the auto-responders.
Related
Been wrestling with trying to get polymorphic serializers working and testing data via rspec. Just upgraded to 0.10+
I found this post, which makes a lot of sense, and does give me a entry into generating the serializations, however, when doing it for polymorphs, I never get the type and id properly named (expecting to see asset_id and asset_type nested)
{:id=>1,
:label=>"Today I feel amazing!",
:position=>0,
:status=>"active",
:media_container_id=>1,
:asset=>
{:id=>4
Test ActiveModel::Serializer classes with Rspec
class MediaSerializer < ApplicationSerializer
attributes :id,
:label,
has_one :asset, polymorphic: true
end
I noticed that the tests dont even seem to properly add the polymorphic identifiers either (ie asset_id, asset_type -- or in the test case imageable_id, imageable_type)
https://github.com/rails-api/active_model_serializers/commit/045fa9bc072a04f5a94d23f3d955e49bdaba74a1#diff-c3565d7d6d40da1b2bf75e13eb8e6afbR36
If I go straight up MediaSerialzer.new(media) I can poke at the .associations, but I cant seem to get them to render as if I was generating a full payload
From the docs
https://github.com/rails-api/active_model_serializers
serializer_options = {}
serializer = SomeSerializer.new(resource, serializer_options)
serializer.attributes
serializer.associations
Im pretty sure Im missing something/doing something wrong - any guidance would be great.
Thanks
It isn't easy to get the effect you are looking for, but it is possible.
You can access the hash generated by the serializer by overriding the associations method.
class MediaSerializer < ApplicationSerializer
attributes :id,
:label,
has_one :asset, polymorphic: true
def associations details
data = super
data[:asset] = relabel_asset(data[:asset])
data
end
def relabel_asset asset
labelled_asset = {}
asset.keys.each do |k|
labelled_asset["asset_#{k}"] = asset[k];
end
labelled_asset
end
end
I learnt alot about ActiveModelSerializer to get the hang of this! I referred to Ryan Bates' podcast on the topic:
http://railscasts.com/episodes/409-active-model-serializers
In there he describes how you can override the attributes method and call super to get access to the hash generated by the serializer. I guessed I could do the same trick for the associations method mentioned in your post. From there it takes a little bit of Ruby to replace all the keys, but, if I have understood correctly what you require, it is technically possible.
Hope that helps!
rails doesn't offer ENUM types, but I do need a data member which can accept only five values. Moreover, I want it to be integrated automatically with the Rails Forms Helper: select_tag.
What's the right solution to my situation?
P.S, I'd rather not to use external plugins, if built-in and neat solution exist.
I keep functionality like this as close to where it's used as possible.
If the values are used by a single model, just keep them in the model, e.g., if users have certain possible types, and only those types, it might look something like:
class User < ActiveRecord::Base
TYPES = %w{guest, paid, admin}
# Plus validation on the `type` field.
# Maybe plus a setter override that also validates.
end
When you need to refer to those types elsewhere, like as allowable values in a select:
User::TYPES
There are a number of valuable tweaks around this, like providing decorators to make them "human readable" (capitalized, spaced, whatever) or metaprogramming methods to allow things like:
user.is_guest? # Or...
user.make_guest! # Or...
user.guest!
I use my own small gem for this functionality because it's often the case that a full-blown association is just too much and provides no value. It allows things like:
class User < ActiveRecord::Base
simple_enum :user_type, %w{guest, paid, admin}
end
Using the tip from this blog post, which offers a very simple approach.
You can set in on your model and then use it on your controller or views.
In this case it will map the status with integers.
STATUS = { pending: 0, active: 1, inactive: 2, deleted: 3 }
def status
STATUS.key(read_attribute(:status))
end
def status=(s)
write_attribute(:status, STATUS[s])
end
Rails 4.1 has enums. I just upgraded to the beta and it's working like a charm!
http://edgeapi.rubyonrails.org/classes/ActiveRecord/Enum.html
I tried active_enum gem, which is great, but it's not compatible with rails 4. The solution from Paulo works pretty well and you can extract the enum into a concern if you want, but it just started getting too heavy for me so I rather upgraded!
You can easily define the Enum as a helper in ApplicationHelper
class ApplicationHelper
def select_range
%w{"a", "b", "c", "d", "e"}
end
end
Then in view you can call select_range freely.
Many people talk about namespace as a way to change the URL that lead to their controller with a prefix (i.e. : /admin/movies instead of /movies)
Changing the URL path
The official doc explains that if we want to change the URL that lead to our controller with a prefix, we just have to change our resource in config/route.rb.
From this :
resources :movies
to this :
resources :movies, :path => "/admin/movies"
Implementing namespace
After a lot of googsearches, I'm wondering why so many people like to have namespace and what are the adavantages of namespace versus just modifying the URL path leading to a specific controller in the router file : myapp/config/route.rb
To implement a namespace, the official doc explains that you need rather the following modifications. You will see that this is a lot of work :
namespace :admin do
resources :movies
end
...and moves your movies_controller.rb controller to app/controllers/admin directory.
But if you follow these instructions, you'll get this error :
*"Expected /var/www/myapp/app/controllers/admin/movies_controller.rb to define Admin::MoviesController"*
*Then you realized that Rails expect this 'alien' writing at the begining of your movies_controller.rb : "Admin::"*
So you change the first line of your movies_controller.rb with :
class admin::MoviesController < ApplicationController
instead of :
class MoviesController < ApplicationController
But you again get another error :
"undefined local variable or method `admin' for main:Object"
Then you realized you forgot Ruby classes MUST be declared with a starting uppercase.
So you add a starting uppercase to your Ruby class :
class Admin::MoviesController < ApplicationController
But you still get an error :
*Missing template admin/movies/index, application/index with {:locale=>[:"fr-CH"], :formats=>[:html], :handlers=>[:erb, :builder, :rxls, :coffee, :haml]}. Searched in: * "/var/www/myapp/app/views"*
What the hell...? Oh did the doc forgot to say as well that controller's corresponding views must as well be moved to an admin directory within the app/view/ ?
So you create a directory and move your views to app/view/admin/
And you still get the same error.
Then you realized you forgot to include the movies folder itself within app/view/admin
So you did it.
You still got an error message :
*undefined method `movies_path' for #<#:0xa817c0c>*
This time you know that routing, controller and view work but you still have to change the pathes on all your views...
Moreover, if you are using the "respond_with" method in your controller file, you have to include the namespace as parameter. This is an example for the index operation :
def index
#movies = Movie.all
respond_with(:admin, #movies)
end
Moreover if you are using declarative_authentification gem (similar as cancan gem), you have to prefix the argument of your permited_to? method with the namespace. For example (with HAML syntax) :
- if permitted_to? :index, :admin_movies
// And prefix as well your path
= link_to 'Show all my movie", admin_movies_path
- if permitted_to? :show, :admin_movies
// And prefix as well your path
= link_to 'Show selected movie", admin_movie_path(#movie)
You realize you were going to create a namespace just for the url path conveniance, so you decide to give up, roll back your modif, and just add the following line in your route.rb file :
resources :movies, :path => "/admin/movies"
Which works immediately like a charm.
As testing purpose I created a new fake project "testapp" including namespace with the generator.
I performed a "rails generate scaffold admin/movie title:string" to check how does the generator handle namespace.
The resulting app/controller/admin/movies_controller.rb is rather different from what we expected it to be.
There are additionnal prefix "Admin::" in front of each instance variable declaration.
Here for instance the index method :
def index
#admin_movies = Admin::Movie.all
respond_to do |format|
format.html # index.html.erb
format.json { render json: #admin_movies }
end
end
I guess that this is because the generator as well moved the model movie.rb into app/models/admin directory
Moreover I see that there is another model in app/models : admin.rb
Which contains :
module Admin
def self.table_name_prefix
'admin_'
end
end
I have no idea how this method will be used nor by who ?
I notice as well that my table will be named "admin_movies" in the /db/migrate/2012blabla_create_admin_movies.rb
class CreateAdminMovies < ActiveRecord::Migration
def change
create_table :admin_movies do |t|
t.timestamps
end
end
end
Well, it seems there will have a lot more work (and mess-up) than I expected in order to put in place a namespace...
For what benefit/advantage ???
To use another url path ? This small modification in my controller
would be far more easier : resources :movies, :path =>
"/admin/movies"
To make it possible to use two different controllers using a same name ? Wouldn't it be easier to just put a prefix to my controler name than do all this mess-up ?
Of course some of the above errors could have been avoided by being more vigilant. But you know, we are just human, and when it's late in the evening while coding, maybe you wished this part of rails would have been, let's say, more human adapted...
I really don't understand why people get so excited to have namespace.
I woudld be gratefull, if an expert could explain what am I missing.
Many Thanks
If you have two different controllers with the same name but different functionality then you can use namespaced routes. You don't have to rename the controller for it. Check out "2.6 Controller Namespaces and Routing" at http://guides.rubyonrails.org/routing.html.
The idea is not to have a ton of controllers in one directory. Namespaces help you do that by grouping controllers in directories.
And as far doing all that work, it's a lot easier just to generate the controllers you need using the namespace paths, eg:
rake generate controller "Foo/Bars"
Then rails does all that heavy lifting so you don't have to.
I wonder if there's a way to auto generate seed_fu files from existing data ?
I'm using Rails 3 and MySql.
seed_fu plugin: https://github.com/mbleigh/seed-fu
Top answer no longer works:
SeedFu::Writer.write('path/to/file.rb',{ class_name: 'Person', constraints: [:first_name, :last_name]}) do |writer|
Person.all.each do |person|
writer << person.as_json
end
end
seed-fu writer uses <<(seed) method to introduce the seed data to the block, top answer will cause a missing block exception.
also you need to pass the class name and constraints inside {options} keys so the writer takes em as options parameters
EDIT
Updated the answer according to Rails Seed-Fu Writer why seed got commented out? #Albert Netymk comment.
It should be:
Person.all.each do |person|
instead of:
Person.each do |person|
You should use SeedFu::Writer to generate seed_fu files, see http://rubydoc.info/github/mbleigh/seed-fu/master/SeedFu/Writer for an example.
I just started checking out Wordpress' CSS Architecture to study a system that's established and pretty powerful to learn better HTML habits. I've noticed they use all hyphens - (post-554 for example), while Rails uses underscores _ (post_554 for example). I'm wondering if there's some setting to customize this in Rails, something like ActionView::Template.word_boundary = "-".
Is there? Not that it really matters, just trying to learn why people do things the way they do.
:)
You can't change se separator. It is hard-coded into Rails.
For example, post_554 is generated by the dom_id helper, which internally relies on the RecordIdentifier class.
Here's the definition.
def dom_id(record, prefix = nil)
if record_id = record.id
"#{dom_class(record, prefix)}#{JOIN}#{record_id}"
else
dom_class(record, prefix || NEW)
end
end
The separator, the JOIN constant, is defined as freezed String so you can't change it.
module RecordIdentifier
extend self
JOIN = '_'.freeze
NEW = 'new'.freeze
There are two ways to change it:
Create your own helper (suggested)
Overwrite the existing methods/helpers with your own implementations (not suggested)
There are also some technical restrictions that explain the reason behind this choice, mainly tied to the language behind Rails.
For instance, talking about symbols
:post_554 # valid symbol
:post-554 # invalid symbol
:"post-554" # valid symbol
Using - would probably require a less cleaner approach to Ruby.
Personally, I prefer using - rather than _ and I tend to avoid standard Rails helpers unless strictly required.