How does Devise hide some fields when output json - devise

I am using Grape + Mongoid + Devise.
I found that the Devise user model have more fields (e.g. encrypted_password, sign_in_count, last_sign_in_at) than the user json output when I wrote API response.
I have searched in Devise code, didn't find anything like custom to_json, how does Devise achieve that?

I'm not sure on Grape, but on Rails you can do it with a serializer (as Grape has many code compatible with Rails, I think there's a big chance to work).
To use a serializer, you need to include the "active_model_serializers" gem.
Example:
class UserSerializer < ActiveModel::Serializer
attributes :id, :email, :username
end
On this example, Devise will always print only these 3 fields on a JSON output.
To include all attributes except some of them, you can do something like this:
class UserSerializer < ActiveModel::Serializer
attributes(*(User.attribute_names - ["date_created", "first_name"] ).map(&:to_sym))
end
Also, at least on Rails, you'll want to remove the root from the output. To do this, add this code to your application_controller.rb:
def default_serializer_options
{root: false}
end

Related

Best practices to implement "complex" urls in Rails

I am developing an enterprise application and would like to use urls with long / non trivial ids instead of the regular, incremental ids.
For instance I'd like to have something like http://mysite.com/users/23lkfjls0934 or http://mysite.com/companies/23lkfjls0934 instead of http://mysite.com/users/3 or http://mysite.com/companies/company-name
What are the steps to follow (migrations, associations, models...) to achieve this result and what is important to keep in mind while building this feature?
You could use friendly_id gem to do the contrary: make URLs as not-friendly as possible :)
Add a new field to your table, it will store your long key. Then use it like this:
class User < ActiveRecord::Base
extend FriendlyId
friendly_id :name, use: :long_random_key
end
You can initialize this long key in a before_create hook, for example.
class User < ActiveRecord::Base
extend FriendlyId
friendly_id :name, use: :long_random_key
before_create :generate_long_random_key
private
def generate_long_random_key
self.long_random_key = SecureRandom.hex(32)
# TODO: Make sure it's unique
end
end

Storing user_id in the paper_trail versions table

I am using paper trail to audit changes to data and would like to store the user_id of the current user in addition to the "whodunnit" column that paper_trail stores by default.
I had no trouble modifying the versions migration to add the user_id column. But I haven't figured out an easy way to set that column from the various models in my app.
It seems like this should work:
has_paper_trail :meta => { :user_id => current_user.id
}
And, I think it might work if I had access to the current_user in my models. But I don't. After researching how to get access to the current_user in my model, I see there is a philosophical debate here. That's not my question though.
So I'm thinking of using a gem like sentient_user or sentient_model to give me access to the current_user in my models so I can set it with something like the code above.
However, adding these gems seems complicated for the little thing I'm trying to do here. I'm wondering if there is an easier way.
What is the easiest way to add the user_id of the person who took the action to the versions table?
The current_user don't exists in models by itself, it appears from controller. So, standard approach is applicable:
class ApplicationController < ActionController::Base
def user_for_paper_trail
current_user if user_signed_in?
end
def info_for_paper_trail
{ user_id: current_user.id } if user_signed_in?
end
end
# config/initializers/paper_trail.rb
class Version < ActiveRecord::Base
attr_accessible :user_id
end

avoiding code duplication in Rails 3 models

I'm working on a Rails 3.1 application where there are a number of different enum-like models that are stored in the database. There is a lot of identical code in these models, as well as in the associated controllers and views. I've solved the code duplication for the controllers and views via a shared parent controller class and the new view/layout inheritance that's part of Rails 3.
Now I'm trying to solve the code duplication in the models, and I'm stuck. An example of one of my enum models is as follows:
class Format < ActiveRecord::Base
has_and_belongs_to_many :videos
attr_accessible :name
validates :name, presence: true, length: { maximum: 20 }
before_destroy :verify_no_linked_videos
def verify_no_linked_videos
unless self.videos.empty?
self.errors[:base] << "Couldn't delete format with associated videos."
raise ActiveRecord::RecordInvalid.new self
end
end
end
I have four or five other classes with nearly identical code (the association declaration being the only difference). I've tried creating a module with the shared code that they all include (which seems like the Ruby Way), but much of the duplicate code relies on ActiveRecord, so the methods I'm trying to use in the module (validate, attr_accessible, etc.) aren't available. I know about ActiveModel, but that doesn't get me all the way there.
I've also tried creating a common, non-persistent parent class that subclasses ActiveRecord::Base, but all of the code I've seen to accomplish this assumes that you won't have subclasses of your non-persistent class that do persist.
Any suggestions for how best to avoid duplicating these identical lines of code across many different enum models?
I found a solution to the code sharing for Rails 3 models, so thought I'd share it with others. It turns out ActiveModel does have everything I need (so far, at least). I created an Enum module using ActiveSupport::Concern, ActiveModel::Validations, and ActiveModel::MassAssignmentSecurity, and I include the module in each of my enum models:
module Enum
extend ActiveSupport::Concern
include ActiveModel::Validations
include ActiveModel::MassAssignmentSecurity
included do
attr_accessible :name
validates :name, presence: true, length: { maximum: 20 }
before_destroy :verify_no_linked_videos
private
def verify_no_linked_videos
unless self.videos.empty?
self.errors[:base] << "Couldn't delete object with associated videos."
raise ActiveRecord::RecordInvalid.new self
end
end
end
end
The way the Rails 3 team pulled out the non-database code from ActiveRecord into ActiveModel really is pretty slick! The following links helped solidify my understanding of how to use this stuff:
http://www.fakingfantastic.com/2010/09/20/concerning-yourself-with-active-support-concern/
http://asciicasts.com/episodes/237-dynamic-attr-accessible

rails 3 pagination with kaminari on mongoid embedded documents

When I call paginate with kaminari on a collection of embedded documents I get the following error:
(Access to the collection for Document is not allowed since it is an embedded document, please access a collection from the root document.):
Any idea on how I can fix that ? I have installed kaminari as a gem.
Alex
You just need to access the collection through the parent object. For example, given the following models:
class User
include Mongoid::Document
embeds_many :bookmarks
end
class Bookmark
include Mongoid::Document
embedded_in :user
end
Then to paginate a given user's bookmarks you would do:
#user.bookmarks.page(params[:page])
I found this issue on Kaminari: https://github.com/amatsuda/kaminari/issues/89
So I forked it, and fixed it following the solution provided by spatrik. I am not 100% sure it will work on all cases and that this solution does not have any drawbacks. But for the moment it works exactly as expected.
Alex
I just sent a patch for the issue. Take a look at the request. hope this helps solve your issue.
https://github.com/amatsuda/kaminari/pull/155/files
With the previous theTRON example :
class User
include Mongoid::Document
embeds_many :bookmarks
end
class Bookmark
include Mongoid::Document
field :created_at, :type => DateTime
embedded_in :user
end
the following, will get you the error you described in your post :
#user.bookmarks.desc(:created_at).page(params[:page])
while the nex one will works fine :
#user.bookmarks.page(params[:page]).desc(:created_at)
I hope it help.

How to user defined friendly URLs in Rails 3?

Now i have something like this
http://myapp.com/pages/1
http://myapp.com/pages/2
http://myapp.com/pages/3
http://myapp.com/pages/4
And each page belong to one user
What i need is to each user to set it's own custom name for the page.
I was thinking of using the friendly_id gem http://norman.github.com/friendly_id/
but I don't find any method to directly edit the slug to set a custom friendly url
how should i proceed?
FriendlyID is a great gem.
It shouldn't be hard to implement user defined page URL.
Create table pages with user_id and link
class User < ActiveRecord::Base
has_many :pages
class Page < ActiveRecord::Base
belongs_to :user
has_friendly_id :link # link is name of the column whose value will be replaced by slugged value
On the page#new you add an input for the link attribute.
Alternatively, you could set friendly_id on title or something else with :use_slug => true option. This way FriendlyID will take the title and modify it so it doesn't have and restricted characters. It will use it's own table to store slugs. Use cached_slug to increase performanse.
Updated
To give users a choice whether they wan't to set a custom link, you could do this:
Set friendly_id on the link field without slugs..
Make a virtual attribute permalink so you could show it in your forms.
In the before_filter, check whether the permalink is set.
If it is, write it to the link field.
If it's not, write title to the link field.
FriendlyID uses babosa gem to generate slugs. If you decide to use it as well, this is how your filter could look like:
protected
def generate_link
#you might need to use .nil? instead
self.link = self.permalink.empty? ? make_slug(self.title) : make_slug(self.permalink)
end
def make_slug(value)
value.to_slug.normalize.to_s #you could as well use ph6py's way
end
Adding to_param method to one of the models should help:
def to_param
"#{id}-#{call_to_method_that_returns_custom_name.parameterize}"
end
Hope this is what you are looking for :)
I am not using the friendly_url gem and am not sure whether my way is efficient. But it works fine for me.
I have a model called Node with id and friendly url field called url_title.
My routes.rb file:
resource 'nodes/:url_title', :to => 'Nodes#view'
nodes_controller.rb
class NodesController <ActiveController
def view
#node = Node.find_by_url_title(:params(url_title))
end
end
And use the #node variable to populate your view.
Now, whenever I type www.example.com/nodes/awesome-title , it takes me to the proper page. One argument against this can be need to create an index on a non-primary field. But I think that might be required for better performance even in the friendly_url gem. Also, the non-primary field url_title needs to be unique. Again, this might be required even for correct working for friendly_url .
Feel free to correct me if I am wrong in these assumptions.
There are a variety of ways, you can achieve this-
1) using Stringex
2) sluggable-finder
3) friendly_id
A complete step by step methodology with reasons for each to be used can be found out here. Happy reading!