Ruby on Rails 3 - to_json not including all attributes - ruby-on-rails

I'm using the to_json method on my model object that I created by doing something like:
user = User.find(1)
When I do user.to_json, a lot of attributes are missing, including user.id from the encoded JSON string. It appears that all of the attributes that I've added as attr_accessible from the User model are there, but none of the others. Perhaps that is what to_json is doing, but I think that adding id to attr_accessible is a no go.
What is the right way of solving this problem?
UPDATE
This looks to be a specific issue with Devise. If I comment out the following from user.rb, everything works as expected:
devise :rememberable, :trackable, :token_authenticatable, :omniauthable

I haven't checked but I believe Devise does that for you; it includes only certain attributes via attr_accessible.
In any case the right way to solve this is to override the as_json method like so:
def as_json(options = nil)
{
my_attr: my_attr,
etc: etc
}
end
It's a simple hash and it's a really powerful method to generate JSON in AR, without messing with the to_json method.

By default Devise overrides the serializable_hash method to expose only accessible attributes (so things like the encrypted_password doesn't get serialized by default on APIs).
You could try to override this method and add the auth_token to the hash, something like this:
def serializable_hash(options = nil)
super(options).merge("auth_token" => auth_token)
end

Devise indeed filters the attributes for you, as mentioned by kain.
Nevertheless, I'd rather just append exactly what I need to instead of overriding Devise's logic.
Instead, I'd rather do
def as_json(options={})
json_res = super options
json_res['locked_at'] = self.locked_at
json_res['confirmed_at'] = self.confirmed_at
end
or whatever other attributes your user might have that you want to pass

If you came here looking for Rails 4, like I did, here's some information that will help.
As Alan David Garcia said above, Devise overrides serializable_hash. To force an override, you could do the following, for example, to return all attributes except password_digest when calling Model#as_json.
def as_json(options = {})
options[:force_except] ||= [:password_digest]
super(options)
end
You can specify any desired model attributes to exclude in options[:force_except] instead of just :password_digest.

include something like this in your model class:
attr_accessible :id, :email, :password, :password_confirmation, :remember_me
Initially the id wasn't included in json but after I added it to attr_accessible it worked!!

Related

How do I validate certain fields with rails devise on registration only

I have a set of custom fields attached to a devise model called Entrant.
I have two forms, one for registration form (three fields) and one which sits in the account area (12 fields). Most of the custom fields area required but only within the form the sits in the account area.
How do I achieve this?
I am using rails 4.2 and ruby 2.1
You can simply specify validations on actions, that is:
validates :name, presence: true, on: :create # which won't validate presence of name on update action
If you ask where to put your custom fields, then generate devise's views and update corresponding ones with these fields.
There are several ways! You could do conditional validations, for instance
class Entrant < ActiveRecord::Base
validate :foo, if: :account_area?
def account_area?
!new_record? # Assumes that Entrant that has already been saved
# is in the account area
end
end
However, it sounds like your needs are advanced enough that you should consider making a Form Object
A form object is an object that accepts parameters, performs validations on that data, then saves a model instance.
class AccountForm
include ActiveModel::Model
include Virtus # Provides AR like attribute functionality and mass assignment
def initialize(entrant)
#entrant = entrant
end
attribute :foo, String
validates :foo, presence: true # This is only used on the account page, so no need to mess with conditional logic
def save
if valid?
persist!
true
else
false
end
end
def persist!
#entrant.update_attributes(foo: self.foo)
end
end
This is just a great example of how non-rails-specific object oriented programming can make your life easier and your app more maintainable. Make a class like above, stick it in app/forms and restart your server. Then in your controller, you'll just pass it the model
class EntrantController < ApplicationController
def update
#form = Form.new(Entrant.find(params[:id]))
#form.attributes = params[:entrant]
if #form.save
redirect_to some_path
else
render "edit"
end
end
end
By default devise only asks for a combination of email/password, you can add other fields by adding a sanitizer (see there -> Devise how to add a addtional field to the create User form?).
If you want to add other fileds to validate, you should create a secondary Entrant controller and add a specific callback to your model.
Typically:
after_update :validate_entrant_form, if: :property_changed?
I hope this will help you.
validates :name, presence: true, if: :condition_holds?
def condition_holds?
# some code here that evaluates to a boolean
end
Maybe this way help you.
Add attribute in devise model : say attr_accessor :validate_certain. In your controller action, devise model instance say #user have to update like this #user.validate_certain = true. and change your appropriate validation conditions in devise model
validates :name, presence: true, if: :validate_certain_changed?
def validate_certain_changed?
validate_certain.present?
end
When I have to do something like this I like to think of it as it validates if something in in the field but you can also take a nil value
Entrant.validates_presence_of(:foo, :allow_nil => true)
I also have this concern when using devise on customer with forms on separate pages updating different set of customer fields
I believe most of the solution works but I was looking for the simplest, easiest and foolproof way to implement the solution
Thus came this.
validates :phone, :country, :postal_code, :street_address, presence: true, allow_nil: true
The allow_nil: true instruct the model to validate the fields ONLY if it exists on the submitted form. If you want more protection, you can use extra para like :on => :update

Require Nested Params in Controller

I need to know how to require nested params in a Ruby on Rails API. I have my method set up for the param whitelisting as below:
def user_params
params.require(:user).permit(:email, :password, :profile => [:name, :birthdate, :addy])
end
However, that makes profile a permitted param, not a required one. I want profile to be required. It's allowed to have no other nested params (essentially everything nil), but must still be required. How do I accomplish this?
Most likely the "required" portion is going to come from your actual model. So, in this case, go to your user.rb file and add the following.
validates :profile, presence: true
Is that what you're talking about? Usually, you're going to want to allow params in the controller and perform validations and stuff in the actual model file.
If you want to make multiple items required in the controller you can add them like so:
def user_params
params.require(:user).permit(...)
params.require(:profile).permit(...)
end
Rails strong parameters documentation in github

how attr_accessor works in ActiveResource rails 3?

How does attr_accessor works in ActiveResource?
class User < ActiveResource::Base
attr_accessor :name
end
How its different from attr_accessor in ActiveRecord?
attr_accessor is built into Ruby, not rails. You may be confusing it with attr_accessible, which is part of ActiveRecord. Here's the difference:
attr_accessor
Take a class:
class Dog
attr_accessor :first_name, :last_name
def initialize(first_name, last_name)
self.first_name = first_name
self.last_name = last_name
end
end
attr_accessor creates a property and creates methods that allow it to be readable and writeable. Therefore, the above class would allow you to do this:
my_dog = Dog.new('Rex', 'Thomas')
puts my_dog.first_name #=> "Rex"
my_dog.first_name = "Baxter"
puts my_dog.first_name #=> "Baxter"
It creates two methods, one for setting the value and one for reading it. If you only want to read or write, then you can use attr_reader and attr_writer respectively.
attr_accessible
This is an ActiveRecord specific thing that looks similar to attr_accessor. However, it behaves very differently. It specifies which fields are allowed to be mass-assigned. For example:
class User
attr_accessible :name, :email
end
Mass assignment comes from passing the hash of POST parameters into the new or create action of a Rails controller. The values of the hash are then assigned to the user being created, e.g.:
def create
# params[:user] contains { name: "Example", email: "..."}
User.create(params[:user])
#...
end
For the sake of security, attr_accessible has to be used to specify which fields are allowed to be mass-assigned. Otherwise, if the user had an admin flag, someone could just post admin: true as data to your app, and make themselves an admin.
In summary
attr_accessor is a helper method for Ruby classes, whereas attr_accessible is an ActiveRecord thing for rails, to tighten up security.
You don't need to have attr_accessor to work with ActiveResource.
The base model (ActiveResource::Base) contains the #attributes hash in which you can 'dump' properties as you wish. (you should be careful though on what params you allow)
The way it does this, is by handling the method_missing? method.
You can take a look here
If you define attr_accessor, what ruby does is that it creates a setter and a getter method, so it will break the method_missing functionality since it will never get to execute that code.
If you still want to use attr_accessor, you should create a Concern something like this:
module Attributes
extend ActiveSupport::Concern
module ClassMethods
def attr_accessor(*attribs)
attribs.each do |a|
define_method(a) do
#attributes[a]
end
define_method("#{a}=") do |val|
#attributes[a] = val
end
end
end
end
end

How to prevent updating a single attribute in Rails?

When a form is submitted, how to prevent a single attribute from being updated in Rails? All other attributes should be updated.
Is it before_save, attr_reader or some other way?
If using before_save, how to access to the attributes hash?
Rails 3.0.7
Check out attr_protected.
Class YourModel << ActiveRecord::Base
attr_protected :the_one_column, as: :update
# ...
end
Now as part of a call to update_attributes, you'd specify the :update role, for example
klass = YourModel.find(some_id)
klass.update_attributes(params[:your_model], as: :update)
If :the_one_column is set in params passed to update_attributes, it will throw an error.
As #Beerlington mentioned in his comment to your Question, you should also check out attr_accessible. It's generally better to spend the 30 minutes going through all models of your application white-listing attributes using attr_accessible than it is to blacklist specific attributes with attr_protected.
Other option is simply doing this in your controller:
klass.update_attributes( params[:your_model].except(:attributes_to_avoid) )

How to Skip Validations w/ find_or_create_by_?

Is it possible to skip validations with a dynamic find/create by method?
For example with regular save I can do something like:
p = Post.new
p.title = nil
p.body = nil
p.save(:validate => false)
Would love to do the same with find_or_create_by_title.
It dosnt look possible with the code in Rails right now however you may have better luck being a little more verbose in how you write the code. You can use find_or_initialize_by_ which creates a new object but does not save it. You can then call save with your custom options, also in the documentation they have a neat demonstration that is hard to find so I will include it below:
# No 'Winter' tag exists
winter = Tag.find_or_initialize_by_name("Winter")
winter.new_record? # true
Good luck and let me know if you need more pointers in the right direction.
For some cases, find_or_initialize_by_ will not be useful and need to skip validations with find_or_create_by.
For this, you can use below alternative flow and method of ROR:
Update your model like this:
class Post < ActiveRecord::Base
attr_accessor :skip_validation
belongs_to :user
validates_presence_of :title, unless: :skip_validation
end
You can use it now like this:
Post.where(user_id: self.id).first_or_create!(skip_validation: true)
I have used first_or_create instead of find_or_create_by here. You can pass more column names and values with this, and your validation will not be worked with this.
You can continue without any changes for strong parameters end and no need to permit this 'skip_validation' so it will work with validations while adding entries.
Using this, you can use it with and without validations by passing a parameter.
Currently skipping validation DOES work with find_or_create_by.
For example, running:
Contact.find_or_create_by(email: "hello#corsego.com).save(validate: false)
will skip a validation like:
validates :name, :email, presence: true, uniqueness: true

Resources