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
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])
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.
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 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.
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 :)