How to initialize Ruby Datamapper object from JSON? - ruby

I'm using Ruby with Sinatra and DataMapper. It is simple enough to create a "get" webservice that delivers a data set to a UI with something like Item.all().to_json
However, the intent is for the UI to use the data set for crud work and return a single JSON object for add or update. I haven't found an equivalent "from_json" DataMapper function to initialize an Item object.
As a work-around, I'm using JSON.parse, like this:
item_data = JSON.parse(request.body.read, :quirks_mode => true)
This works, but then I have to create a new DataMapper object, i.e. item = Item.new,
and copy all the elements from item_data to item, but I'd like to think there's a simpler way.
Any and all suggestions are welcome.

It seems you have:
class Item
property :body, String
end
So you might want to do this:
class Item
property :body, Json
end
The Json style property, works just like String, the only difference is that on load/store the data will go through the JSON parser.

I had the same problem!
You can create a helper like this:
helpers do
def json_params
begin
JSON.parse(request.body.read)
rescue
halt 400, { message:'Invalid JSON' }.to_json
end
end
end
And create your Datamapper object:
#object = Object.new(json_params)
#object.save

Related

Getting Sequel associations through Sinatra

I'm trying to return json-formatted data from my Sinatra REST API. I currently have a bunch of associations set up, but I'm having trouble getting the views I want from my API despite getting them easily in Ruby.
For example, from my tables:
DB.create_table?(:calendars) do
primary_key :id
end
DB.create_table?(:schedules) do
primary_key :id
foreign_key :resource_id, :resources
foreign_key :task_id, :tasks
foreign_key :calendar_id, :calendars
end
In Ruby, I'm able to run a block like this and display all the info I need through my associations:
Calendar.each do |c|
c.schedules.each do |s|
puts "RESOURCE ##{s.resource_id}"
s.tasks.each do |t|
p t
end
puts
end
end
the c.schedules call works because my calendar model contains a one_to_many :schedules association.
Now, I'm wondering how this translates to my Sinatra API. In my simple GET route, I've tried many variations trying to get the schedules associated with a calendar, and convert it to JSON:
get '/calendars' do
c = DB[:calendar].first
c.schedules.to_json
content_type :json
end
... but I'll end up with an error like undefined method 'schedules' for {:id=>1}:Hash
So it looks like it's returning a hash here, but I've tried a bunch of stuff and haven't figured out how I'm supposed to work with my associations in Sinatra. How can I do this?
Thanks!
The reason your first block works but the second doesn't is because in the first case, you're using a Sequel model instance of class Calendar, whereas in the second case you're using a Sequel dataset.
When you iterate over Calendar.each do |c|, the c variable gets populated with an instance of a Calendar class Sequel model object. This object has relationship methods defined (one_to_many) and you're able to query schedules and run other model methods on it.
However, c = DB[:calendar].first gets you a Sequel dataset. This object is different than a model instance, it returns a standard Ruby hash (or an array of hashes).
You can change your 2nd block to use a model instead and it will get the result you want:
get '/calendars' do
c = Calendar.first # <=== CHANGE FROM DATASET TO MODEL
c.schedules.to_json
content_type :json
end

AFMotion HTTP GET request syntax for setting variable

My goal is to set an instance variable using AFMotion's AFMotion::HTTP.get method.
I've set up a Post model. I would like to have something like:
class Post
...
def self.all
response = AFMotion::HTTP.get("localhost/posts.json")
objects = JSON.parse(response)
results = objects.map{|x| Post.new(x)}
end
end
But according to the docs, AFMotion requires some sort of block syntax that looks and seems to behave like an async javascript callback. I am unsure how to use that.
I would like to be able to call
#posts = Post.all in the ViewController. Is this just a Rails dream? Thanks!
yeah, the base syntax is async, so you don't have to block the UI while you're waiting for the network to respond. The syntax is simple, place all the code you want to load in your block.
class Post
...
def self.all
AFMotion::HTTP.get("localhost/posts.json") do |response|
if result.success?
p "You got JSON data"
# feel free to parse this data into an instance var
objects = JSON.parse(response)
#results = objects.map{|x| Post.new(x)}
elsif result.failure?
p result.error.localizedDescription
end
end
end
end
Since you mentioned Rails, yeah, this is a lil different logic. You'll need to place the code you want to run (on completion) inside the async block. If it's going to change often, or has nothing to do with your Model, then pass in a &block to yoru method and use that to call back when it's done.
I hope that helps!

Serialize a class into an activerecord field

I've tried a few different searches, but I'm not really sure how to word the question correctly. Imagine I have a db table create migration that looks like this:
def self.up
create_table :users do |t|
t.string :name, :null => false,
t.text :statistics
end
end
Example users class
class User
serialize :statistics, JSON
def statistics
self.statistics
end
end
But I want self.statistics to be an instance of class Statistics
class Statistics
#stats = {}
def set(statistic, value)
#stats[statistic] = value
end
def to_json(options)
self.to_json(:only => #stats)
end
end
(or something like that)
Ultimately, what I want to happen is that I want to be able to add Statistics-specific methods to manage the data in that field, but when I save the User object, I want it to convert that instance of the Statistics class into a JSON string, which gets saved in the DB.
I'm coming from a PHP background, where this kind of thing is pretty easy, but I'm having a hard time figuring out how to make this work with ActiveRecord (something I don't have much experience with).
Ideas?
Note: This is NOT Rails... just straight-up Ruby
Do you need it to be JSON? If not, serializing it to YAML will allow you to easily marshal the object back to its original class.
ActiveRecord::Base#serialize takes two arguments: attribute and class. If class is defined, the data must deserialize into an object of the type specified. Here's a cool example of it in action.
By default, your attribute will be serialized as YAML. Let’s say you
define MyField like this:
class MyField
end
It will be serialized like this:
--- !ruby/object:MyField {}
It will deserialize back to a MyField instance.

How to set a method dynamically as other class method

Im new to Ruby, and im creating a cli app with Thor and some additional gems. My problem is that i take user input (from the console) and pass the data as a variable to a existing method (This method is from a gem)
My method
def search(searchtype, searchterm)
search = OtherClass.new
result = search.search.searchtype keyword: "#{searchterm}"
puts result
# search.search.searchtype is not a method in the gem im using.
end
The OtherClass gem has these search methods: users, repos
The users method
def users(*args)
arguments(args, :required => [:keyword])
get_request("/legacy/user/search/#{escape_uri(keyword)}", arguments.params)
end
The repos method
def repos(*args)
arguments(args, :required => [:keyword])
get_request("/legacy/repos/search/#{escape_uri(keyword)}", arguments.params)
end
So how can i pass in the user data to the method from the OtherClass? Heres something like what i would want to do. The SEARCHTERM would be dynamically passed to the search.search object as a method parameter.
def search(SEARCHTYPE, searchterm)
search = OtherClass.new
result = search.search.SEARCHTYPE keyword: "#{searchterm}"
puts result
end
The "#{searchterm}" works as expected, but i also want to pass in the method to the search.search object dynamically, this could probably be done with if's but im sure theres a better way, maybe the Ruby way to solve this problem.
Finally i would want to be able to use this little program like this (the serch method)
./search.rb search opensource linux
(where opensource could be users, or another type of search, and linux could be the search keyword for the searchtype)
If this is possible i would apprechiate any help!
Thnx!
If you'd like to call a method dynamically, use Object#send.
http://ruby-doc.org/core-1.9.3/Object.html#method-i-send
I would caution against sending a method that was obtained by user input though, for security reasons.

ActiveRecord to_json specifying options for included methods

I've got the following to_json in my view:
<%=raw #forums.to_json(:methods => [:topic_count, :last_post]) %>;
and in the model I've got the following method:
def last_post
post = ForumPost.joins(:forum_topic).where(:forum_topics => {:forum_id => self.id}).order("forum_posts.created_at DESC").first
return post
end
However ForumPost contains a relation to a ForumTopic (belongs_to :forum_topic) and I want to include this ForumTopic in my json so I end up with {..., "last_post":{..., "forum_topic":{...}...}, ...}. How can I accomplish this?
Usually it's better to keep that kind of declaration in the Model. You can override the as_json method in your models. This approach allows you to control the the serialization behavior for your entire object graph, and you can write unit tests for this stuff.
In this example I'm guessing that your #forums variable refers to a model called Forum, if i'm wrong hopefully you still get the idea.
class Forum
...
def as_json(options={})
super(methods: [:topic_count, :last_post])
end
end
class ForumPost
...
def as_json(options={})
super(methods: [:forum_topic])
end
end

Resources