Active resource complaining about expects an hash - ruby-on-rails

I am using active resource to get data from an api and display it,
My controller model.rb has
class Thr::Vol::Dom < ActiveResource::Base
class << self
def element_path(id, prefix_options = {}, query_options = nil)
prefix_options, query_options = split_options(prefix_options) if query_options.nil?
"#{prefix(prefix_options)}#{collection_name}/#{id}#{query_string(query_options)}"
end
def collection_path(prefix_options = {}, query_options = nil)
prefix_options, query_options = split_options(prefix_options) if query_options.nil?
"#{prefix(prefix_options)}#{collection_name}#{query_string(query_options)}"
end
end
ActiveResource::Base.site = 'http://10.00.0.00:8888/'
self.format = :json
self.collection_name= "/vv/test/domains"
def self.find
x = superclass.find(:one, :from => '/vv/test/domains/2013-06-25T05:03Z')
x
end
end
When i call this Thr::Vol::Dom.find method it returns the following error:
ArgumentError: expected an attributes Hash,
got ["0.0.0.0", "1.1.1.1", "2.2.2.2", "3.3.3.3", "4.4.4.4"]
The api is expected to feed something like this
{"abs.com":["0.0.0.0", "1.1.1.1", "2.2.2.2", "3.3.3.3", "4.4.4.4"]}
for the call i made.
The API returns the correct hash but i guess active resource is not able to read it properly, it is directly reading the value in the key-value pair of the hash.
I want to fix this "ArgumentError" error , i want to display the contents of the returned hash in the view.

You can change how ActiveResource handle json response with
class YourModel < ActiveResource::Base
self.format = ::JsonFormatter.new(:collection_name)
end
In lib/json_formatter.rb
class JsonFormatter
include ActiveResource::Formats::JsonFormat
attr_reader :collection_name
def initialize(collection_name)
#collection_name = collection_name.to_s
end
def decode(json)
remove_root(ActiveSupport::JSON.decode(json))
end
private
def remove_root(data)
if data.is_a?(Hash) && data[collection_name]
data[collection_name]
else
data
end
end
end
If you pass self.format = ::JsonFormatter.new(:categories) it will find and remove categories root element in your json returned by your API.

The API is returning a JSON object, not a Ruby hash. You'll need to convert it into a hash by using Ruby's JSON module:
require 'JSON'
hash = JSON.parse('{"abs.com":["0.0.0.0", "1.1.1.1", "2.2.2.2", "3.3.3.3", "4.4.4.4"]}')
This will return a hash and then you'll notice that the key/value pair will work as expected:
hash["abs.com"] => ["0.0.0.0", "1.1.1.1", "2.2.2.2", "3.3.3.3", "4.4.4.4"]

Related

Extending ActiveModel::Serializer with custom attributes method

I am trying to create my own attributes method called secure_attributes where I pass it an array of attributes and the minimum level the authorized user needs to be to view those attributes. I pass the current level of the authorized user as an instance_option. I'd like to extend the Serializer class so I can use this method in multiple serializers, but Im having issues.
This is what i have so far:
in config/initializers/secure_attributes.rb
module ActiveModel
class Serializer
def self.secure_attributes(attributes={}, minimum_level)
attributes.delete_if {|attr| attr == :attribute_name } unless has_access?(minimum_level)
attributes.each_with_object({}) do |name, hash|
unless self.class._fragmented
hash[name] = send(name)
else
hash[name] = self.class._fragmented.public_send(name)
end
end
end
end
end
and then in the individual serializer I have things like this:
secure_attributes([:id, :name, :password_hint], :guest)
and then
def has_access?(minimum_level=nil)
return false unless minimum_level
return true # based on a bunch of logic...
end
But obviously secure_attributes cannot see the has_access? method and if I put has_access inside the Serializer class, it cannot access the instance_options.
Any idea how I can accomplish what I need?
Maybe you want to do following - but I still do not get your real purpose, since you never did anything with the attributes but calling them:
module ActiveRecord
class JoshsSerializer < Serializer
class << self
def secure_attributes(attributes={}, minimum_level)
#secure_attributes = attributes
#minimum_level = minimum_level
end
attr_reader :minimum_level, :secure_attributes
end
def initialize(attr, options)
super attr, options
secure_attributes = self.class.secure_attributes.dup
secure_attributes.delete :attribute_name unless has_access?(self.class.minimum_level)
secure_attributes.each_with_object({}) do |name, hash|
if self.class._fragmented
hash[name] = self.class._fragmented.public_send(name)
else
hash[name] = send(name)
end
end
def has_access?(minimum_level=nil)
return false unless minimum_level
return true # based on a bunch of logic...
end
end
end

ruby on rails accessing custom class attributes from its object

I have a custom class in my application controller. Like below:
class Defaults
def initialize
#value_1 = "1234"
#value_2 = nil
#data = Data.new
end
end
class Data
def initialize
#data_1 = nil
end
end
Now in my controller method i have created an object of type Defaults
def updateDefaultValues
defaults = Defaults.new
# i am unable to update the value, it says undefined method
defaults.value_2 = Table.maximum("price")
defaults.data.data_1 = defaults.value_2 * 0.3
end
How to access value_2 from defaults object?
defaults.value_2
Also, how to access data_1 attribute from data object within defaults object?
defaults.data.data_1
You should use attr_accessor:
class Defaults
attr_accessor :value_1, :value_2, :data
# ...
end
defaults = Defaults.new
defaults.value_1 = 1
# => 1
defaults.value_1
# => 1
As you are using def as a keyword to define the method, that means def is a reserved keyword. You can't use reserved keywords as a variable.
You just need to rename your variable name from def to something_else and it should work! Your code will look like this:
def updateDefaultValues
obj = Defaults.new
obj.value_2 = Table.maximum("price")
obj.data.data_1
end
EDIT:
As per OP's comment & updated question, he had used def just as an example, here is the updated answer:
You may need attr_accessor to make attrs accessible:
class Defaults
attr_accessor :value_1, :value_2, :data
...
...
end
class Data
attr_accessor :data_1
...
...
end
Add value_2 method in Defaults class
class Defaults
def initialize
#value_1 = "1234"
#value_2 = nil
#data = Data.new
end
def value_2
#value_2
end
end
class Data
def initialize
#data_1 = nil
end
end

Add extra filter parameters in RoR

Suppose I have a method in a controller:
def my_find(is_published, count)
items = Idea.where(published: is_published)
#......
end
Sometimes I want to pass some extra filter arguments
def my_find(is_published, count, some_extra_filter = nil)
items = Idea.where(published: is_published) #.where (some_extra_filter)
#......
end
where some_extra_filter can be lambda or just an plain sql "where" string and it can also be nil or "".
So how do I concatenate .where(published: is_published) with where (some_extra_filter) to get what I need?
This is actually very easy using scopes:
def my_find
#items = Idea.scoped
#items = #items.where(published: is_published) unless is_published.nil?
#items = #items.where(other: other_param) if other_params < 10
# etc, etc
end

Cannot override/monkey patch a rails method with rails 3.1.3

I am trying to override an active resource method like explained in this question : Remove .xml extension from ActiveResource request and this one :
i want to use a REST api, i cannot manage to set active resource to use it
To do so i tested :
Creating in the /config/in itializers/ folder of my app a file named active_resource.rb with the following code :
class ActiveResource::Base
def element_path(id, prefix_options = {},query_options = nil)
check_prefix_options(prefix_options)
prefix_options, query_options = split_options(prefix_options) if query_options.nil?
"#{prefix(prefix_options)}#{collection_name}/#{URI.parser.escape id.to_s}#{query_string(query_options)}"
end
end
Adding the method inside my model. Here is my model code :
class Player < ActiveResource::Base
def element_path(id, prefix_options = {}, query_options = nil)
check_prefix_options(prefix_options)
prefix_options, query_options = split_options(prefix_options) if query_options.nil?
"#{prefix(prefix_options)}#{collection_name}/#{URI.parser.escape id.to_s}#{query_string(query_options)}"
end
self.site = "http://ws.maniaplanet.com/"
self.user="**********"
self.password="*********"
end
To validate the overriding of my custom code I have tried to use
puts "called this method"
or
ActionController::Base.logger.info "called this method"
It has never worked.
Why can't i override the rails method element path ?
UPDATE
Tried to put active_resource.rb in extra after uncommenting the config.autoload_paths += %W(#{config.root}/extras) line in application.rb. No change
If i put the base.rb file with my class and method in lib/active_resource/ it breaks my app. I cannot launch rails server anymore
You should override class method, not instance one, so:
class Player < ActiveResource::Base
def self.element_path(id, prefix_options = {}, query_options = nil)
#...
end
end
That would be enough, if you going to make requests only from Player model.
If you want this behaviour for any model, you should monkey patch ActiveResource::Base, again, the class method.
# config/initializers/active_resource_patch.rb
class ActiveResource::Base
def self.element_path(id, prefix_options = {}, query_options = nil)
#...
end
end

to_xml for fully-qualified trees using acts_as_tree

I have an ActiveRecord class that uses acts_as_tree. I'm trying to update the to_xml method so that if a child record's to_xml is called it will return it's xml nested in the parent/ancestor xml to give the fully-qualified path to that resource. As an example I have Compiler which is a parent of Compiler/Version. Compiler should be rendered as xml:
While Compiler/Version should render as
I've tried to do this by passing around a fully_qualified flag, but it dies with 'Builder::XmlMarkup#to_ary should return Array'
def to_xml(options={}, &block)
options[:fully_qualified] ||= true
options[:indent] ||= 2
options[:builder] ||= Builder::XmlMarkup.new(:indent => options[:indent])
if options[:fully_qualified] and not parent.nil?
55: parent.to_xml(options) do |foo|
relative_options = options.dup
relative_options[:fully_qualfied] = false
relative_options[:skip_instruct] = true
relative_options.delete(:builder)
foo << to_xml(relative_options)
end
else
xml = options[:builder]
xml.instruct! unless options[:skip_instruct]
66: xml.parameter(:name => name, &block)
end
end
The method works fine for the case of Compiler, but fails for Compiler/Version:
/usr/lib/ruby/1.8/builder/xmlbase.rb:133:in method_missing'
/usr/lib/ruby/1.8/builder/xmlbase.rb:133:incall'
/usr/lib/ruby/1.8/builder/xmlbase.rb:133:in _nested_structures'
/usr/lib/ruby/1.8/builder/xmlbase.rb:57:inmethod_missing'
app/models/parameter.rb:66:in to_xml'
app/models/parameter.rb:55:into_xml'
It appears that you can't call to_xml on any singular-association. to_xml on parent and parameter (both has_one relationships) failed, but if I did the lookup with find_by_id it worked:
def to_xml(options={}, &block)
my_options = options.dup
my_options[:fully_qualified] = true unless my_options.has_key?(:fully_qualified)
my_options[:only] = [:name]
if my_options[:fully_qualified] and not parent.nil?
# do block here fails with 'Builder::XmlMarkup#to_ary should return Array'
# if called as parent.to_xml, so call on explicit lookup of parent and
# it works
p = self.class.find_by_id(parent_id)
p.to_xml(my_options) do |xml|
relative_options = my_options.dup
relative_options[:builder] = xml
relative_options[:fully_qualified] = false
relative_options[:skip_instruct] = true
to_xml(relative_options, &block)
end
else
super(my_options, &block)
end
end

Resources