Alias attribute ruby ActiveResource::Base - ruby

class ChatMessage < ActiveResource::Base
alias_attribute :user_id, :userId
alias_attribute :chat_id, :chatId
alias_attribute :message_text, :MessageText
...
I Have the problem that what I return from an API has attribute names that I don't like, e.g. see camelCaps. I don't want to do this to every model in my application. Is there some method missing magic I could apply?
Cheers
Thomas

You can do a little of metaprogramming here:
module JavaAliasing
def initialize(hash)
super(Hash[hash.map do |k,v|
[k.to_s.gsub(/[a-z][A-Z]/) { |s| s.split('').join('_') }.downcase.to_sym, v]
end])
end
end
Let me illustrate this:
class Instantiator
def initialize(hash)
hash.each { |k,v| instance_variable_set "##{k}", v }
end
end
Instantiator.new(asdf: 2).instance_variable_get('#asdf') #=> 2
class MyARModel < Instantiator
include JavaAliasing
end
MyARModel.new(asdfQWER: 2).instance_variable_get("#asdf_qwer") #=> 2
Here, a real life example (rails 4.0):
> Player.send :include, JavaAliasing
> Player.new(name: 'pololo', username: 'asdf', 'teamId' => 23)
=> #<Player id: nil, name: "pololo", username: "asdf", email: nil, type: "Player", created_at: nil, updated_at: nil, provider: nil, uid: nil, team_id: 23, last_login: nil, last_activity: nil>

Related

rspec expect symbol not equal to created object

I wrote a test for controll with rspec:
it "populates an array of books" do
book = FactoryGirl.create(:book)
get :index
expect(:books).to eql([book])
end
books_controller.rb
def index
#books = Book.all.order("created_at DESC")
end
books.rb
FactoryGirl.define do
factory :book do |f|
f.name { Faker::Book.title }
f.author { Faker::Book.author }
f.press { Faker::Book.publisher }
f.cover { fixture_file_upload(Rails.root.join('spec', 'photos', 'testcover.jpg'), 'image/png') }
end
end
Run bin/rake spec, the result is:
1) BooksController GET #index populates an array of books
Failure/Error: expect(:books).to eql([book])
expected: [#<Book id: 1, name: "The Waste Land", author: "谢靖琪", isbn: nil, press: "University of Chicago Press"...e: "image/png", cover_file_size: 104531, cover_updated_at: "2016-08-26 04:00:19", page_number: nil>]
got: :books
(compared using eql?)
Diff:
## -1,2 +1,2 ##
-[#<Book id: 1, name: "The Waste Land", author: "谢靖琪", isbn: nil, press: "University of Chicago Press", description: nil, grade_level: nil, lexile_level: nil, douban_link: nil, scholastic_link: nil, created_at: "2016-08-26 04:00:19", updated_at: "2016-08-26 04:00:19", cover_file_name: "fcbcb7417dbc88827d16765a.jpg", cover_content_type: "image/png", cover_file_size: 104531, cover_updated_at: "2016-08-26 04:00:19", page_number: nil>]
+:books
They are not equal. It seems that the first expected result, '...' is an abbreviation. How can I fix it?
I think what you want is
expect(assigns(:books)).to eq([book])

Ruby object mass assignment

Is there any better way to do the below code?
user.name = "abc"
user.email = "abc#test.com"
user.mobile = "12312312"
Something like this will do:
user.prepare do |u|
u.name = "abc"
u.email = "abc#test.com"
u.mobile = "12312312"
end
tap let's you do exactly that:
user.tap do |u|
u.name = "abc"
u.email = "abc#test.com"
u.mobile = "12312312"
end
Alternative option when your attributes come in the form of a hash:
attrs = {
name: "abc",
email: "abc#test.com",
mobile: "12312312"
}
attrs.each { |key, value| user.send("#{key}=", value) }
You could do the following as well:
user.instance_eval do
#name = "abc"
#email = "abc#test.com"
#mobile = "12312312"
end
You can access the instance variables of user inside the block given to instance_eval
You could use the below code if you wish to invoke the accessor methods instead of directly manipulating the instance variables.
user.instance_eval do
self.name = "xyz"
self.email = "abc#test.com"
self.mobile = "12312312"
end
or
user.instance_eval do |o|
o.name = "xyz"
o.email = "abc#test.com"
o.mobile = "12312312"
end
With ActiveRecord objects you can use .assign_attributes or the update
methods:
user.assign_attributes( name: "abc", email: "abc#test.com", mobile: "12312312")
# attributes= is a shorter alias for assign_attributes
user.attributes = { name: "abc", email: "abc#test.com", mobile: "12312312" }
# this will update the record in the database
user.update( name: "abc", email: "abc#test.com", mobile: "12312312" )
# or with a block
user.update( name: "abc", mobile: "12312312" ) do |u|
u.email = "#{u.name}#test.com"
end
.update accepts a block, while assign_attributes does not. If you are simply assigning a hash of literal values - such as those passed by a user in the params then there is no need to use a block.
If you have a plain old ruby object which you want to spice up with mass assignment you can do:
class User
attr_accessor :name, :email, :mobile
def initialize(params = {}, &block)
self.mass_assign(params) if params
yield self if block_given?
end
def assign_attributes(params = {}, &block)
self.mass_assign(params) if params
yield self if block_given?
end
def attributes=(params)
assign_attributes(params)
end
private
def mass_assign(attrs)
attrs.each do |key, value|
self.public_send("#{key}=", value)
end
end
end
This will let you do:
u = User.new(name: "abc", email: "abc#test.com", mobile: "12312312")
u.attributes = { email: "abc#example.com", name: "joe" }
u.assign_attributes(name: 'bob') do |u|
u.email = "#{u.name}#example.com"
end
# etc.
Assuming that 'user' is a class that you control, then you can just define a method to do what you want. For example:
def set_all(hash)
#name, #email, #mobile = hash[:name], hash[:email], hash[:mobile]
end
And then in the rest of your code:
user.set_all(name: "abc", email: "abc#test.com", mobile: "12312312")
If 'user' is an instance of, say, an ActiveRecord model, then I'm a little shaky on the details of how you would get this to work. But the principal still applies: DRY up code by moving the responsibility for the complexity to the receiver.

parse json to object ruby

I looked into different resources and still get confused on how to parse a json format to a custom object, for example
class Resident
attr_accessor :phone, :addr
def initialize(phone, addr)
#phone = phone
#addr = addr
end
end
and JSON file
{
"Resident": [
{
"phone": "12345",
"addr": "xxxxx"
}, {
"phone": "12345",
"addr": "xxxxx"
}, {
"phone": "12345",
"addr": "xxxxx"
}
]
}
what's the correct way to parse the json file into a array of 3 Resident object?
Today i was looking for something that converts json to an object, and this works like a charm:
person = JSON.parse(json_string, object_class: OpenStruct)
This way you could do person.education.school or person[0].education.school if the response is an array
I'm leaving it here because might be useful for someone
The following code is more simple:
require 'json'
data = JSON.parse(json_data)
residents = data['Resident'].map { |rd| Resident.new(rd['phone'], rd['addr']) }
If you're using ActiveModel::Serializers::JSON you can just call from_json(json) and your object will be mapped with those values.
class Person
include ActiveModel::Serializers::JSON
attr_accessor :name, :age, :awesome
def attributes=(hash)
hash.each do |key, value|
send("#{key}=", value)
end
end
def attributes
instance_values
end
end
json = {name: 'bob', age: 22, awesome: true}.to_json
person = Person.new
person.from_json(json) # => #<Person:0x007fec5e7a0088 #age=22, #awesome=true, #name="bob">
person.name # => "bob"
person.age # => 22
person.awesome # => true
require 'json'
class Resident
attr_accessor :phone, :addr
def initialize(phone, addr)
#phone = phone
#addr = addr
end
end
s = '{"Resident":[{"phone":"12345","addr":"xxxxx"},{"phone":"12345","addr":"xxxxx"},{"phone":"12345","addr":"xxxxx"}]}'
j = JSON.parse(s)
objects = j['Resident'].inject([]) { |o,d| o << Resident.new( d['phone'], d['addr'] ) }
p objects[0].phone
"12345"
We recently released a Ruby library static_struct that solves the issue. Check it out.

How to test Rspec For controller,

How to Rectify this error in RSpec for Controller,
1) SellersController GET index find the Activity
Failure/Error: assigns(:activity).should eq([activity])
expected: [#<Activity id: 65, transactable_type: "admin", transactable_id: 1, action_type: "seller", user_id: 1, is_approved: false, approved_by: nil, created_at: "2012-04-09 11:02:17", updated_at: "2012-04-09 11:02:17", associatable_type: nil, associatable_id: nil>]
got: nil
(compared using ==)
Seller_rspec.rb
describe "GET index" do
it "find the Activity" do
activity = Activity.create!(:transactable_type=>"admin",:transactable_id=>1,:action_type=>"seller",:user_id =>1,:is_approved=>0)
get :index,{:is_approved => activity.to_param,:user_id=>1,:approved_by=>"admin"}
assigns(:activity).should eq([activity])
end
In controller
def index
#activities=Activity.find(:all,:select => 'DISTINCT transactable_type,transactable_id,action_type,is_approved,approved_by',:conditions=>["is_approved= ? and user_id=? and approved_by IS NULL",false,current_user.id])
end
You are putting a code into controller which should go to the model. Create a method or scope in Activity model like:
def self.find_not_approved(current_user_id)
find(:all,
:select => 'DISTINCT transactable_type,transactable_id,action_type,is_approved,approved_by',
:conditions= ["is_approved= ? and user_id=? and approved_by IS NULL",
false,
current_user_id])
end
So you can just have in controller (I've made up the method name):
def index
#activities = Activity.find_not_appoved(current_user.id)
end
And just to anser your question, it should be assigns(:activities).should eq([activity]) not assigns(:activity).should eq([activity]) - as your are checking #activities variable in controller not, #activity.

Attribute Not Being Added to Object

I'm trying to add an attribute to a model object. Direct access works but when I print the entire object or encode it into JSON, that attribute is left out. Can someone tell me what I'm doing wrong?
Here is my rails console output:
irb(main):010:0> b=ChatMessage.new(:user_id=>4,:room_id=>1,:message=>"Hello World")
=> #<ChatMessage id: nil, room_id: 1, user_id: 4, message: "Hello World", created_at: nil, updated_at: nil>
irb(main):011:0> b.sender_nickname="bbb"
=> "bbb"
irb(main):012:0> b.sender_nickname
=> "bbb"
irb(main):013:0> b
=> #<ChatMessage id: nil, room_id: 1, user_id: 4, message: "Hello World", created_at: nil, updated_at: nil>
Here is my model code:
class ChatMessage < ActiveRecord::Base
attr_accessor :sender_nickname
def self.get_last_message_id
last_message=ChatMessage.all.last
last_message.nil? ? 0 : last_message.id
end
def self.get_all_messages_after(room_id,message_id)
ChatMessage.where("room_id = ? AND id > ?",room_id,message_id)
end
end
edit:
Here is the migration file for chat_messages table.
I'm not really looking to save sender_nickname. So it's more like a virtual attribute (but is still in db through association). And I might need to add other attributes later that aren't in the db. Is it possible to do it without using association?
def self.up
create_table :chat_messages do |t|
t.integer :room_id
t.integer :user_id
t.string :message
t.timestamps
end
end
as far as I know to_json will only take the attributes in the model and serialize (as in chat_message.attributes, not attr_accessor).
You properbly got a sender, or user model, or anything like that.
What I would do is to make a relation to the sender, user or what its called, with a belong_to, and then use this code to convert it to json:
chat_message.to_json(:include => { :sender => { :only => :nickname } })
It may also work with you code, and then just:
chat_message.to_json(:include => { :sender_nickname })
There also some documentation here: http://api.rubyonrails.org/classes/ActiveModel/Serializers/JSON.html
Hope it helps :)

Resources