Neo4j::Rails::Model to_json - node id is missing - json

I have a Jruby on Rails application with Neo4j.rb and a model, let's say Auth, defined like this:
class Auth < Neo4j::Rails::Model
property :uid, :type => String, :index => :exact
property :provider, :type => String, :index => :exact
property :email, :type => String, :index => :exact
end
And this code:
a = Auth.find :uid => 324, :provider => 'twitter'
# a now represents a node
a.to_json
# outputs: {"auth":{"uid": "324", "provider": "twitter", "email": "email#example.com"}}
Notice that the ID of the node is missing from the JSON representation. I have a RESTful API within my application and I need the id to perform DELETE and UPDATE actions.
I tried this to see if it works:
a.to_json :only => [:id]
But it returns an empty JSON {}.
Is there any way I can get the ID of the node in the JSON representation without rewriting the whole to_json method?
Update The same problems applies also to the to_xml method.
Thank you!

I am answering my own question. I still think that there is a better way to do this, but, for now, I am using the following hack:
In /config/initializers/neo4j_json_hack.rb I put the following code:
class Neo4j::Rails::Model
def as_json(options={})
repr = super options
repr.merge! '_nodeId' => self.id if self.persisted?
end
end
And now every JSON representations of my persisted Neo4j::Rails::Model objects have a _nodeId parameter.

The ID is typically not included because it shouldn't be exposed outside the Neo4j database. Neo4j doesn't guarantee that the ID will be identical from instance to instance, and it wouldn't surprise me if the ID changed in a distributed, enterprise installation of Neo4j.
You should create your own ID (GUID?), save it as a property on the node, index it, and use that to reference your nodes. Don't expose the Neo4j ID to your users or application, and definitely don't rely on it beyond a single request (e.g. don't save a reference to it in another database or use it to test for equality).

Related

Convert nested struct to JSON

I find the answer for the reverse but not for the way nested struct to JSON
Suppose that I have this ruby structs
Attributes = Struct.new :name, :preferredLanguage, :telephoneNumber, :timeZone
User = Struct.new :email, :service, :preferredLanguage, :attributes
I create the struct for Attributes
attributes = Attributes.new "Pedro", "es", "5555555", "Madrid"
# => #<struct Attributes name="Pedro", preferredLanguage="es", telephoneNumber="5555555", timeZone="Madrid">
attributes.to_h.to_json
# => "{\"name\":\"Pedro\",\"preferredLanguage\":\"es\",\"telephoneNumber\":\"5555555\",\"timeZone\":\"Madrid\"}"
Oj.dump attributes
# => "{\"^u\":[\"Attributes\",\"Pedro\",\"es\",\"5555555\",\"Madrid\"]}"
Oj.dump attributes, mode: :compat
# => "\"#<struct Attributes name=\\\"Pedro\\\", preferredLanguage=\\\"es\\\", telephoneNumber=\\\"5555555\\\", timeZone=\\\"Madrid\\\">\""
So it works well, except when I use the gem Oj, that I cannot remove the name of the object and get the same as with the to_h.to_json methods
But the problem comes when I use a nested Struct like User
user = User.new "Pedro#email.com", "coolService", "es", attributes
# => #<struct User email="Pedro#email.com", service="coolService", preferredLanguage="es", attributes=#<struct Attributes name="Pedro", preferredLanguage="es", telephoneNumber="5555555", timeZone="Madrid">>
user.to_h.to_json
# => "{\"email\":\"Pedro#email.com\",\"service\":\"coolService\",\"preferredLanguage\":\"es\",\"attributes\":\"#<struct Attributes name=\\\"Pedro\\\", preferredLanguage=\\\"es\\\", telephoneNumber=\\\"5555555\\\", timeZone=\\\"Madrid\\\">\"}"
Oj.dump user, mode: :compat
# => "\"#<struct User email=\\\"Pedro#email.com\\\", service=\\\"coolService\\\", preferredLanguage=\\\"es\\\", attributes=#<struct Attributes name=\\\"Pedro\\\", preferredLanguage=\\\"es\\\", telephoneNumber=\\\"5555555\\\", timeZone=\\\"Madrid\\\">>\""
With the to_h.to_json I get the string of the attributes object, And with the oj, this is not a valid JSON. and also I have another question, there is any GSON, jackson library from java that works the same way in ruby
If you were to use ActiveSupport (Rails), you would get this out of the box. As you seem to be using barebones Ruby, just do it recursively:
hashify = lambda do |struct|
as_hash = struct.to_h
struct_keys = as_hash.select { |_, v| v.is_a? Struct }.map(&:first)
struct_keys.each { |key| as_hash[key] = hashify.(as_hash[key]) }
as_hash
end
hashify.(user).to_json
# => "{\"email\":\"Pedro#email.com\",\"service\":\"coolService\",\"preferredLanguage\":\"es\",\"attributes\":{\"name\":\"Pedro\",\"preferredLanguage\":\"es\",\"telephoneNumber\":\"5555555\",\"timeZone\":\"Madrid\"}}"
As for GSON, there seems to be a wrapper for Ruby, but I don't think it's that widely used. Rails' monkey patched behaviour is good enough for 99.99% of possible uses. It also lets you define your custom serializers if you want to change it.

How do you Skip an Object in an ActiveModel Serializer Array?

I have searched through all the active model serializer (v 0.9.0) documentation and SO questions I can find, but can't figure this out.
I have objects which can be marked as "published" or "draft". When they aren't published, only the user who created the object should be able to see it. I can obviously set permissions for "show" in my controller, but I also want to remove these objects from the json my "index" action returns unless it is the correct user. Is there a way to remove this object from the json returned by the serializer completely?
In my activemodel serializer, I am able to user filter(keys) and overloaded attributes to remove the data, as shown using my code below, but I can't just delete the entire object (I'm left having to return an empty {} in my json, trying to return nil breaks the serializer).
I'm probably missing something simple. Any help would be much appreciated!
class CompleteExampleSerializer < ExampleSerializer
attributes :id, :title
has_many :children
def attributes
data = super
(object.published? || object.user == scope || scope.admin?) ? data : {}
end
def filter(keys)
keys = super
(object.published? || object.user == scope || scope.admin?) ? keys : {}
end
end
That looks correct, try returning an array instead of a hash when you dont want any keys. Also, I don't think calling super is necessary b/c the filter takes in the keys.
Also, I don't think defining an attributes method is necessary.
I have chapters that can either be published or unpublished. They're owned by a story so I ended doing something like below.
has_many :unpublished_chapters, -> { where published: false }, :class_name => "Chapter", dependent: :destroy
has_many :published_chapters, -> { where published: true }, :class_name => "Chapter", dependent: :destroy
Inside of my serializer, I choose to include unpublished_chapters only if the current_user is the owner of those chapters. In ams 0.8.0 the syntax is like so.
def include_associations!
include! :published_chapters if ::Authorization::Story.include_published_chapters?(current_user,object,#options)
include! :unpublished_chapters if ::Authorization::Story.include_unpublished_chapters?(current_user,object,#options)
end
In my case, it's not so bad to differentiate the two and it saves me the trouble of dealing with it on the client. Our situations are similar but say you want to get all of the chapters by visiting the chapters index route. This doesn't make much sense in my app but you could go to that controller and render a query on that table.

NameError - undefined local variable - Parsing array from ruby file to haml file

We are using a Mysql database and make use of haml files.
The issue we have now is when we dynamically create a table with data from our mysql database it gives us this error:
NameError - undefined local variable or method `allsites' for #<Sinatra::Application:0x00
00000128c980>:
/home/usr/testsinatra/views/sites.haml:16:in `block in singleton class'
/home/usr/testsinatra/views/sites.haml:-8:in `instance_eval'
/home/usr/testsinatra/views/sites.haml:-8:in `singleton class'
/home/usr/testsinatra/views/sites.haml:-10:in `__tilt_12132720'
The weird thing is that the table is actually created !
This is the part of the ruby file where we fill our array with mysql data.
get '/getsites' do
allsites = con2.query("SELECT * FROM tblSites", :as => :array)
haml :sitesOverzicht, :locals => {:allsites => allsites}
end
This is the part where we make our table dynamically:
%table{:border => "1px"}
%tbody
%h1 All Sites
-allsites.each do |id,name|
%tr
%td
= id
%td
= name
The other strange part is when the page loads, we don't get any error and the table is created when the page loads.
But when we create a new tablerow in another table from another array and store this data with Ajax, we get this error for this variable even if we don't do anything.
I've managed to find a fix for my own problem !
To make use of our local variable we had to use the "#" sign like this for our Query:
#allSites = con2.query("SELECT * FROM tblSites", :as => :array)
Afterwards we changed it also in our link to send it to the haml file:
haml :sitesOverzicht,:locals => {:allSites => #allSites}
Afterwards in our haml file we got the NoMethodException but we found out that we had to make use of the to_a.each function to make use of the "each" function we allready had like this:
-#allSites.to_a.each do |id,name|
%tr
%td
= id
%td
= name
With the to_a.each function you say that you will make use of an array as parameter. So the each function will loop over an array. If you use the each function without saying you are using an array, it will give a nill exception.
Your Answer maybe work but it's not really nice.
Because with # you define a Instance variable probably not what you want. But of course you can do that but then you can remove :locals => {:allSites => #allSites}
But in most cases it's better to use locals:
app.rb
get "/" do
allsites = {"1" => "name", "2" => "name 2", "3" => "name3"}
p allsites
haml :index, :locals => {:allsites => allsites}
end
views/index.haml
-allsites.each do |id,name|
%tr
%td
= id
%td
= name
works fine for me so this indicate that what ever your con2.query("SELECT * FROM tblSites", :as => :array) returns it's not exactly what you expected.

how to store instance methods name of a class in a column in sql database?

I want to create a column that has nested hash structure.
structure of that column:
column_name: company
{ production=> {watch=> "yes",pen =>"no",pencil => "yes"}
quality => {life_test =>"yes", strong_test =>"yes", flexibility_test => "no"}
}
Here production, quality are my models and watch, pen, pencil, life_test,strong_test are my instance method of respective classes. each instance method will get the Boolean value from the view page.
How to achieve this structure.
This is called serialization and it is pretty easy. You could do the following:
class Something < ActiveRecord::Base
serialize :company, JSON
end
bar = Something.new
bar.company = { :production=> {:watch=> true,:pen => false, :pencil => false}
:quality => {:life_test =>true, :strong_test =>true, :flexibility_test => false} }
bar.save
If you want more info go here: http://api.rubyonrails.org/classes/ActiveRecord/Base.html and read the part on "Saving arrays, hashes, and other non-mappable objects in text columns" just make sure your company column in the database is a text column.
Use serialization to store hash in db .
Follow api link

DataMapper can't save to mysql and create object with id = nil

I am following the tinyclone example in Cloning Internet Application with Ruby and is trying to create an object to be stored into mysql.
However, whenever I try to create a Url object (see code below), the result is always
"=> Url #id=nil #original="http://www.gmail.com" #link_identifier=nil"
The id is not created nor the data is stored in the database. The link to the sql database is correct as I already tried dropping the database and recreating it using DataMapper.auto_migrate!
Can anyone help? Thanks.
DataMapper.setup(:default,'mysql://root#localhost/tinyclone')
class Url
include DataMapper::Resource
property :id, Serial
property :original, String, :length => 255
belongs_to :link
end
class Link
include DataMapper::Resource
property :identifier, String, :key => true
property :created_at, DateTime
has 1, :url
has n, :visits
end
DataMapper.finalize
url = Url.create(:original => 'http://www.gmail.com')
=> #<Url #id=nil #original="http://www.gmail.com" #link_identifier=nil>
In your Url model Link is a required association. You can't create a url without a link because validation will fail. If you want to be able to create urls without associating them with a link you can write belongs_to :link, :required => false