Skooma input validator - validation

I was given a task to implement an input validator with the Skooma library https://github.com/bobfp/skooma#validators
The general concept is pretty clear, but for some inputs I have a list of "legal" words, and I have zero clue on how to implement the validation for this case.
Hence why I came here, I wanted to ask if you know any examples / projects that used this library? I googled but didn't find anything.
Of if you have any other tipps just let me know! 🙂
This is the example:
my schema:
schema = %{
:titel => :string,
:category => :string,
:high_level_category => :string,
:description => :string,
:potential_impacts => :string,
:affected_assets => :string,
:rating => :string }
The legal inputs for category:
category = %{core: 'Core network threats', access: 'Access network threats', multi: 'Multi edge computing threats',
virtualisation: 'Virtualisation threats', phyiscal: 'Physical infrastructure threats', generic: 'Generic threats'}
I tried it with a normal list as well, such as
category = ['Core network threats', 'Access network threats', 'Multi edge computing threats' .......]
But I just cant get my head around how to check if the :category is present in the category list.

You need a custom validator function, here's an example:
alias Skooma.Validators
#valid_categories [
"Access network threats",
"Core network threats",
"Generic threats",
"Multi edge computing threats",
"Physical infrastructure threats",
"Virtualisation threats"
]
def valid?(data), do: Skooma.valid?(data, schema())
defp schema,
do: %{
:category => [:string, inclusion(#valid_categories)],
... # rest of the schema
}
# copied from:
# https://github.com/bobfp/skooma/blob/master/lib/validators.ex#L38-L48
defp inclusion(values_list) when is_list(values_list) do
fn data ->
bool = data in values_list
if bool do
:ok
else
{:error, "Value is not included in the options: #{inspect(values_list)}"}
end
end
end
You can replace the inclusion function with Validators.inclusion/1. In this case, you'll need to install Skooma from Github because it has not been published as speaking today (31 Jan 2022).

Related

Why can't my script access classes that are in different directory?

I am trying to access classes DealState and NotAnEndState that are in another directory, where I have a lib called move-to-go.
move-to-go folder contains modules where the one in my example is named deal_state.rb. When i open deal_state.rb it contains the following the code below.
Path to lib: F:\Ruby25-x64\lib\ruby\gems\2.5.0\gems\move-to-go-5.3.0\lib\move-to-go
module MoveToGo
module DealState
# This is the default, a deal with a status with this state is
# currently being worked on.
NotAnEndState = 0
# The deal has reached a positive end state, eg we have won
# the deal.
PositiveEndState = 1
# The deal has reached a negative end state, eg we have lost
# the deal.
NegativeEndState = -1
end
end
Path to my code: C:Users/Shahin/MigrationFolder/converter.rb
class Converter
def configure(rootmodel)
rootmodel.settings.with_organization do |organization|
organization.set_custom_field( { :integration_id => 'source', :title => 'Källa', :type => :Link } )
end
rootmodel.settings.with_person do |person|
person.set_custom_field( { :integration_id => 'source', :title => 'Källa', :type => :String} )
end
rootmodel.settings.with_deal do |deal|
assessment is default DealState::NotAnEndState
deal.add_status( {:label => '1. Kvalificering' })
deal.add_status( {:label => '2. Deal closed', :assessment => MoveToGo::DealState::PositiveEndState })
deal.add_status( {:label => '4. Deal lost', :assessment => MoveToGo::DealState::NegativeEndState })
end
end
When I execute my script I get this error message:
C:Users/MyUserName/MigrationFolder/converter.rb:63:in `block in configure': uninitialized constant Converter::DealState (NameError)
Did you mean? DEAL_SHEET
New things have however come to light. The error message seems to have an issue with the Converter-class, but i cant really interpret what it is implying.
This line is the error : assessment is default DealState::NotAnEndState.
First you should use MoveToGo::DealState::NotAnEndState and second assessment is default should be in a spec file not here.
If you juste remove this line, there should be no error anymore.

Ruby Sinatra Datamapper/database confusion

I'm confused as to how database relationship works.
Say I have a Border Crossing('crossing'), which has two
Directions('north', 'south'), each of which direction has 2 types of lanes ('normal','fast'), each of which lane has 2 metrics (=data) ('delay','queue_length').
In reality there are several crossings, with more lane types and more metrics.
How the heck should I store that in a database? I've used databases before, but never did table joins or one-to-many or anything like that.
I came across Datamapper and since I'm learning how to us Sinatra I thought I'd give it a go.
In the tutorial (http://datamapper.org/getting-started.html), the "one-to-many" part just screamed "this is what you need", so I started fiddling around.
require 'data_mapper'
DataMapper.setup(:default, ENV['DATABASE_URL'] || "sqlite3://#{Dir.pwd}/development.db")
class Crossing
include DataMapper::Resource
property :id, Serial
property :name, String,:unique=>true
has n, :directions
end
class Direction
include DataMapper::Resource
property :id, Serial
property :direction, String,:unique=>true
belongs_to :crossing
has n, :lanes
end
class Lane
include DataMapper::Resource
property :id, Serial
property :lane, String
belongs_to :direction
has n, :datas
end
class Data
include DataMapper::Resource
property :id, Serial
property :name, String,:unique=>true
property :value, String
belongs_to :lane
end
DataMapper.finalize.auto_migrate!
I just thought this looked so elegantly put: "crossing has n directions, directions has n lanes, etc"
Then:
Crossing.create(:name => "crossing")
Direction.create(:direction => "north")
Direction.create(:direction => "south")
Lane.create(:lane => 'normal')
Lane.create(:lane => 'fast')
Data.create(:data => 'delay')
Data.create(:data => 'queue_length')
// now how do I retrieve find the data of a lane of a direction of a crossing?
Now, what I will input and retrieve all the time is the Data part. Does this whole thing make sense or I'm just not understanding what table associations are for? I know I could just have a gigantic object instead of this but I'm pretty sure that's a weird way of doing things.
#crossing = {
'crossing name' => {
:directions => {
:north => {
:normal => {
:delay => '10 min',
:queue => '100 m'
},
:fast => {
:delay => '1 min',
:queue => '10 m'
}
},
etc etc etc
}
and then access the data like #crossing[:north][:normal][:delay]....but I kinda feel like a database would be better?
Am I making any sense in any way? Anybody got some pointers for a young grasshoper?
I would rather go with this structure:
Data belongs to Crossing, Direction and Lane; it has properties for delay and queue
Direction has many Data, and has exactly two rows
Lane has many Data, and has exactly two rows
Crossing has many Data, and has many rows
The reason is, you don't want to repeat the strings "north", "south" etc in your database.
Then, first seed the database with constant tables:
Direction.create(direction: 'north')
Direction.create(direction: 'south')
Lane.create(lane: 'normal')
Lane.create(lane: 'fast')
Then you can make your crossings:
cool_crossing = Crossing.create(name: 'My Cool Crossing')
not_cool_crossing = Lane.create(name: 'My Not So Cool Crossing')
and add data points:
north = Direction.first(name: "north")
normal = Lane.first(name: "normal")
Data.create(
crossing: cool_crossing,
lane: normal,
direction: north,
delay: 10,
queue: 1
)
and retrieve data by:
all_data_for_cool_crossing = Data.all(
crossing: cool_crossing
)
or
data_for_cool_crossing_normal_north = Data.first(
crossing: cool_crossing,
lane: normal,
direction: north
)

How can I avoid duplication in a join query using Sequel with Postgres on Sinatra?

I want to do a simple join. I have two tables: "candidates" and "notes".
Not all candidates have notes written about them, some candidates have more than one note written about them. The linking fields are id in the candidates table and candidate_id in the notes table. The query is:
people = candidates.where(:industry => industry).where("country = ?", country).left_outer_join(:notes, :candidate_id => :id).order(Sequel.desc(:id)).map do |row|
{
:id => row[:id],
:first => row[:first],
:last => row[:last],
:designation => row[:designation],
:company => row[:company],
:email => row[:email],
:remarks => row[:remarks],
:note => row[:note]
}
end
It works kind of fine and gets all the specified candidates from the candidates table and the notes from the notes table but where there is more than one note it repeats the name of the candidate. In the resulting list, person "abc" appears twice or three times depending on the number of notes associated with that person.
I am not actually printing the notes in the HTML result just a "tick" if that person has notes and "--" if no notes.
I want the person's name to appear only once. I have tried adding distinct in every conceivable place in the query but it made no difference.
Any ideas?
In order for distinct to work, you need to make sure you are only selecting columns that you want to be distinct on. You could try adding this to the query
.select(:candidates__id, :first, :last, :designation, :company, :email, :remarks, Sequel.as({:notes=>nil}).as(:notes)).distinct
But you may be better off using a subselect instead of a join to check for the existence of notes (assuming you are using a decent database):
candidates.where(:industry => industry, :country=>country).select_append(Sequel.as({:id=>DB[:notes].select(:candidate_id)}, :note)).order(Sequel.desc(:id)).map do |row|
{ :id => row[:id], :first => row[:first], :last => row[:last], :designation => row[:designation], :company => row[:company], :email => row[:email], :remarks => row[:remarks], :note => row[:note] }
end

Chaining datamapper relationships across different repositories

class A
include DataMapper::Resource
def self.default_repository_name
:alt_db
end
property :aid, Integer, :key => true
# other stuff
belongs_to :b, :model => 'B', :child_key => [ :bid ]
end
class B
include DataMapper::Resource
# this one is in the default repo
property :bid, Integer, :key => true
# other stuff
belongs_to :c, :model => 'C', :child_key => [ :cid ]
end
class C
include DataMapper::Resource
# this one is in the default repo
property :cid, Integer, :key => true
# other stuff
end
If I just have A and B, this works fine. If I add C, however, I get an error:
dm-core/model/property.rb:73:in `new': wrong number of arguments (4 for 3) (ArgumentError)
If I want to make a chain of relationships with DataMapper, so that I can give an ID in one place and get a piece of data that's, say, four tables away through a series of references to subsequent tables' primary key ID field, how can I do this?
EDIT: Digging into the DM source from the stack trace:
DataMapper.repository(other_repository_name) do
properties << klass.new(self, name, options, type)
end
That's where the error is raised. Indeed, in this case klass is a DataMapper Integer property, and it's initialize method only accepts three options (model, name, and an options hash).
This whole block is only executed because I'm using more than one repository, though B and C are in the same one so I don't know if that sheds any light on why it's erroring on the cid property.
EDIT2:
I have tried all permutations, and it appears that when you're chaining, once you cross a database-boundary, that must be the end of the chain. For example, since A is :alt_db and B is :default, B is as deep as I can go, regardless of whether C is :default, :alt_db, or a third option.
If instead both A and B were :default, or both were :alt_db, and then C were the opposite one, C would be as deep as I could go.
I don't understand this behavior really, though.
You found a bug actually. It's been fixed in master. You can try grabbing sources from git and see if it works.
Your code works fine for me.
irb(main):001:0> A.first.b.c
DEBUG - "(0.001168) SELECT "aid", "bid" FROM "as" ORDER BY "aid" LIMIT 1"
DEBUG - "(0.000337) SELECT "bid", "cid" FROM "bs" WHERE "bid" = 2 LIMIT 1"
DEBUG - "(0.000046) SELECT "cid" FROM "cs" WHERE "cid" = 3 LIMIT 1"
=> #<C #cid=3>
My gem is dm-core-1.1.0, you should check your version.
It turns out this was a small issue with DataMapper chaining across repositories. Submitted to them and it's allegedly been fixed already!
http://datamapper.lighthouseapp.com/projects/20609/tickets/1506-can-only-chain-up-to-first-time-changing-default-repository#ticket-1506-1

Is it possible to specify what index a query should use in Mongoid?

MongoDB seems like it is using an inefficient query pattern when one index is a subset of another index.
class Model
field :status, :type => Integer
field :title, :type => String
field :subtitle, :type => String
field :rating, :type => Float
index([
[:status, Mongo::ASCENDING],
[:title, Mongo::ASCENDING],
[:subtitle, Mongo::ASCENDING],
[:rating, Mongo::DESCENDING]
])
index([
[:status, Mongo::ASCENDING],
[:title, Mongo::ASCENDING],
[:rating, Mongo::DESCENDING]
])
end
The first index is being used both when querying on status, title and subtitle and sorting on rating and when querying on just status and title and sorting on rating even though using explain() along with hint() in the javascript console states that using the second index is 4 times faster.
How can I tell Mongoid to tell MongoDB to use the second index?
You can pass options such as hint to Mongo::Collection using Mongoid::Criterion::Optional.extras
An example:
criteria = Model.where(:status => true, :title => 'hello world').desc(:rating)
criteria.extras(:hint => {:status => 1, :title => 1, :rating => -1})
extras accepts anything that Mongo::Collection can handle
http://www.mongodb.org/display/DOCS/Optimization#Optimization-Hint
While the mongo query optimizer often
performs very well, explicit "hints"
can be used to force mongo to use a
specified index, potentially improving
performance in some situations.
db.collection.find({user:u, foo:d}).hint({user:1});
You need to work from http://www.rdoc.info/github/mongoid/mongoid/master/Mongoid/Cursor here as I do not know Ruby enough. It mentions hint.

Resources