How to validate only json -schema object instead of its properties? - ruby

1.Below is the ruby code with json - schema validations, Here the point is i want to validate only object instead of its properties
require "json-schema"
schema={"type"=>"object"}
begin
JSON::Validator.validate(schema,{"a":5})
rescue JSON::Schema::ValidationError => e
puts e.message
end
For example the key values of object can be anything, just i want to validate, whether {"a":"5"} it is an object/Hash or not instead of checking its properties like "a":5.

You Have Invalid Ruby Syntax
JSON:Validator is invalid namespacing. You need two colons when specifying a nested module or class, so use JSON::Validator instead. When you fix that, your test schema will validate. For example:
require 'json-schema'
schema={'type' => 'object'}
JSON::Validator.validate(schema, {'a': 5})
#=> true
You may have other problems, too, but they aren't evident from your example, since almost everything descends from Object. If you run into problems with specific types, you may want to take action on the value of e in your rescued SON::Schema::ValidationError, especially if it's essentially a form of TypeError.

Related

Passing Boolean value as a String to a method expecting a String

In Ruby, I'm attempting to pass a boolean result to a method which accepts as a string as a parameter. This is as an experiment.
fileexists = File.file?("#{$fileLocation}")
puts File.file?("#{$fileLocation}")
puts fileexists
puts fileexists.to_s
This will result in:
true
true
true
Now if I attempt to call a method which accepts a string and pass this parameter in a number of ways.
slack(message: "#{fileexists}")
Results in the error message.
'message' value must be a String! Found TrueClass instead.
Which confuses me as I understand that Ruby evaluates anything within "" as a String. So placing a TrueClass object within a placeholder, should effectively cast this value to a string.
So let's try something slightly different:
slack(message: "#{fileexists.to_s}")
This results in the same error.
'message' value must be a String! Found TrueClass instead.
Now this is where things GET REALLY WEIRD!!
slack(message: "#{fileexists.to_s} ")
slack(message: "#{fileexists} ")
If I add a single bit of whitespace to the end of the string after the placeholder, it passes, and a slack message is sent my way, displaying 'true'.
I understand I may be asking for a bit of 'Crystal-ball' insight here as
I don't have the implementation of the 'slack' method, and this may be a result of the way that's implemented.
Does Ruby check types of params as they're passed like this?
Is that a Ruby standard error message you might receive, or a custom error thrown by the slack() method?
The dependency you are using, fastlane, auto-converts values that are passed into the actions (your call to slack).
The reason for this is that parameters in fastlane can also be specified via the commandline, so conversion is necessary. It converts your value of "true" to a boolean automatically because there is no Boolean class in ruby and the type of parameters is specified by giving it the name of a class, so it automatically converts "true" to a boolean. The offending line in the code is here.
As you can see in the code above, a workaround would be to do slack(message: "#{fileexists.to_s.capitalize}") or slack(message: fileexists ? "t" : "f"). Anything really as long as you avoid yes, YES, true, TRUE, no, NO, false, and FALSE
I understand I may be asking for a bit of 'Crystal-ball' insight here as I don't have the implementation of the 'slack' method, and this may be a result of the way that's implemented.
Sounds like youre using a lib (gem) which contains the methods slack, you can check the gem code location running gem which gem_name on your console.
Does Ruby check types of params as they're passed like this?
No
Is that a Ruby standard error message you might receive, or a custom error thrown by the slack() method?
Custom Error
As Jorg W Mittag stated this looks like a misimplementation of slack method when trying to deserialize, and then checking the types. You could fix the slack method on the gem by contributing to this gem, monkeypatch it or you can try to hack it the way it is... this last onde depends on how slack was implemented, maybe adding an extra pair of quotes, like "\"#{fileexists}\""
PS: You don't have to embbed the string inside another string if you're going to use it as it is, like fileexists = File.file? $fileLocation , this should work.
I'm only guessing here because we don't know what the method definition of slack is expecting an un-named String, but you're passing a hash.
slack(fileexists.to_s)

What is the correct way to check for the existence of a nested attribute in Chef?

There are multiple ways to check for the existence of a nested attribute in chef, and I'm not sure which is correct/best, and if any will result in empty attributes being stored on the node:
node[:parent] and node[:parent][:child]
node.attribute?(:parent) and node[:parent].attribute?(:child))
node[:parent].nil? and node[:parent][:child].nil?
It'd be greatly preferred to be able to check for the parent and child at the same time, but I don't know if that's possible. I am using Chef 10, not Chef 11, though answers explaining either are welcome.
Node attribute object is HashMap. You can use ruby native API to lookup nested attributes.
Chef Node Object provides a number of helper methods like:
node.attribute?()
node[:foo].attribute?(:bar)
node[:foo].member?(:bar)
There is also a new method node.debug_value() in chef 11 to help you debug node attributes which is also helpful:
node.debug_value(:foo, :bar)
Details can be found from the article Chef 11 In-Depth: Attributes Changes
The correct "modern" way to do this is to use the exist?() helper:
if node.exist?('foo', 'bar', 'baz')
# do stuff with node['foo']['bar']['baz']
end
This supersedes the old chef-sugar deep_fetch mechanism and has been built into chef-client for a very long time at this point.
There is also a read() helper to get the value out, which will return nil if any intermediate key does not exist:
fizz = node.read('foo', 'bar', 'baz')
It is identical to the Hash#dig method which was later added to ruby which is also supported as an alias:
fizz = node.dig('foo', 'bar', 'baz')
The way I've solved this more recently has been to always set default values for attributes used in a cookbook where possible.
For example, cookbook/attributes/default.rb would contain:
default[:parent][:child] = nil
And the in the recipe, any check for the attributes value can be reduced to:
node[:parent][:child].nil?
Of course, it's usually far more useful to have a usable default value and to not have to check at all.
Check out the chef-sugar cookbook deep_fetch extension that allows for safe references to deep attributes.
I found a really elegant solution here that uses rescue NoMethodError at the end of the if conditional.
if node['foo']['bar']['baz']
do the stuff I want
end rescue NoMethodError

Sinatra matches params[:id] as string type, additional conversion needed to match the database id?

I am using sinatra and DataMapper to access an sqlite3 database. I always get an nil when calling get(params[:id]). But when I call get(params[:id].to_i) I can get the right record. Is there anything wrong such that I have to do the conversion explicitly?
The sinatra app is simple:
class Record
include DataMapper::Resource
property :id, Serial
....
end
get '/list/:id' do
r = Record.get(params[:id])
...
end
Obviously this is a problem with Datamapper (if you believe it should be casting strings to numbers for id's), but there are ways Sinatra can mitigate it. When params come in you need to check:
They exist.
They're the right type (or castable).
They're within the range of values required or expected.
For example:
get '/list/:id' do
r = Record.get(params[:id].to_i)
# more codeā€¦
curl http://example.org/list/ddd
That won't work well, better to check and return an error message:
get '/list/:id' do |id| # the block syntax is helpful here
halt 400, "Supply an I.D. *number*" unless id =~ /\d+/
Then consider whether you want a default value, whether the value is in the right range etc. When taking in ID's I tend to use the regex syntax for routes, as it stops following sub routes being gobbled up too, while providing a bit of easy type checking:
get %r{/list/(\d+)} do |id|
Helpers are also useful in this situation:
helpers do
# it's not required to take an argument,
# the params helper is accessible inside other helpers
# it's but easier to test, and (perhaps) philosophically better.
def id( ps )
if ps[:id]
ps[:id].to_i
else
# raise an error, halt, or redirect, or whatever!
end
end
end
get '/list/:id' do
r = Record.get id(params)
To clarify, the comment in the original question by #mbj is correct. This is a bug in dm-core with Ruby 2.0. It worked fine with ruby 1.9. You are likely on dm-core version 1.2 and need 1.2.1, which you can get by running 'gem update dm-core'.

Marshal class, is there a way to find whether a data is already serialized or not?

I am using Marshal class to serialize a Ruby object, using the functions: dump() and load() everything works well, but when a value not related to any serialized data is passed, the load() function returns the expected and logical error:
incompatible marshal file format (can't be read)
format version 4.8 required; 45.45 given
What I need is to check if this data had already been serialized or not before loading it. My goal is to avoid this error and do something else.
Ok. I've encountered very similar problem and, based on hints from this post http://lists.danga.com/pipermail/memcached/2007-December/006062.html, I've figured out that this happens when you either try to load data that was not marshaled before or the data was stored improperly (e.g. not in binary field in database).
In my case specifically I used text type instead of binary field in database, and marshal data got mangled.
Changing type of column from text to binary helped. Unfortunately you cannot convert old (corrupted) data, so you have to drop the column and create it again as binary.
Maybe just rescue from the error?
begin
Marshal.load("foobar")
rescue TypeError
# not a marshalled object, do something else
puts "warning: could not load ..."
end
I have applied Padde way, but using a function that does the job for me and get me back the object, either preexistent or new created as following:
def get_serialized_banner
begin
#banner_obj = Marshal.load(self.path)
rescue TypeError
self.path = Marshal.dump(Banner.new())
self.save
#banner_obj = Marshal.load(self.path)
end
return #banner_obj
end

Odd behaviour when saving instances in Ruby DataMapper

having just started to look at DataMapper for Ruby ORM I've run into an issue that confuses me to no end.
The default behaviour when saving instances in DataMapper (through DataMapper::Resource.save) is, as far as I understand, to silently fail, return false from the save method and collect any errors in the errors collection. So far so good, this works as expected. The issue I see is with natural primary keys, where setting duplicate ones will throw an exception instead of silently returning false from the save method, blatantly disregarding the current setting of raise_on_save_failure. Consider the following snippet;
require 'data_mapper'
class Thing
include DataMapper::Resource
property :name , String, :key=> true
property :description, String, length: 2..5
end
DataMapper.setup :default, "sqlite:memory"
DataMapper.finalize
DataMapper.auto_migrate!
#Create a thing and have it properly silently fail saving
t1=Thing.new(:name=>"n1",:description=>"verylongdescription" )
t1.save #ok
puts("t1 is saved:"+t1.saved?.to_s)
t1.errors.each do |e|
puts e
end
#Create two things with the same key, and fail miserably in silently failing the second save...
t1=Thing.new(:name=>"n1",:description=>"ok" )
t2=Thing.new(:name=>"n1",:description=>"ok" )
t1.save #ok
t2.save #not ok, raises Exception event though it shouldn't?
puts("t2 is saved:"+t1.saved?.to_s)
t2.errors.each do |e|
puts e
end
The first save, on an instance failing a validation rule for the :description property behaves as expected. The third save, of an instance with duplicate keys, however does not, since it raises an execption instead of nicely just returning false.
Why is this? It is obviously possible to work around, but it doesn't feel very clear. Is the case that the silent behaviour will only apply to validation errors in the DataMapper layer, and any hard errors from the underlying datastore will be propagated as exceptions to the caller?
Thanks!
As another user pointed out in a comment, this happens because setting raise_on_save_failure to false doesn't mean that no exceptions will occur. It will always return false (with no exceptions) when validation errors occur.
Database errors will blow up, as you noticed, in the form of an exception. Such errors could happen due to a large number of factors (the connection failed, no disk space), including mundane ones like a duplicate key. DataMapper can't know whether the object you're trying to save possesses a duplicate key, so it just validates it and sends to the database, where the error actually occurs.

Resources