How can I build a GQL from a ruby hash? - ruby-on-rails

I am building a rspec helper to test my graphql requests.
So far this is my helper:
def mutation_params(name, attributes:, return_types:)
{
query:
<<~GQL
mutation {
#{name}(
input: { attributes: #{attributes} })
#{return_types}
}
GQL
}
end
and I have to declare the attributes like this:
let(:attributes) do
<<~GQL
{
email: "#{email_param}",
password: "#{password_param}"
}
GQL
end
Now I want to know what I can do to be able to simply pass my arguments as a hash, and have the mutations_params method build the GQL from that hash, by iterating over them.
let(:attributes) do
{
email: email_param,
password: password_param
}
end
Something like:
def mutation_params(name, attributes:, return_types)
gql_attributes = <<~GQL
{
}
GQL
attributes.each do |key, value|
gql_attributes merge with
<<~GQL
"#{key}": "#{value}"
GQL
end
{
query:
<<~GQL
mutation {
#{name}(
input: { attributes: #{gql_attributes} })
#{return_types}
}
GQL
}
end
but that obviously does not work. I think my problem is I don't really understand what that <<~GQL is and how to manipulate it.

You're looking for the squiggly heredoc which was introduced in Ruby 2.3. It's like a normal heredoc but it leaves off leading indentation. https://ruby-doc.org/core-2.5.0/doc/syntax/literals_rdoc.html
So in other words, it's just a string! The GQL bit is arbitrary but a nice way of communicating the purpose of the heredoc.
You could write a helper like this to turn hashes into GraphQL strings
def hash_to_mutation(hash)
attr_gql_str = attributes.map{|k,v| "#{k}: #{v.inspect}"}.join(", ")
" { #{attr_gql_str} } "
end
Then assuming attributes is a hash as in your example you could just
def mutation_params(name, attributes:, return_types:)
{
query:
<<~GQL
mutation {
#{name}(
input: { attributes: #{hash_to_gql(attributes)} })
#{return_types}
}
GQL
}
end

Related

Rails: How to convert hash of array of objects to json

I'm a java and Js developer, so I'm completetly new to rails and ruby. In one of my project I'm using rails to consume an api and return in back to js. I'm converting the api response to a model in ruby.
Now, it's in format of {KEY1=>[{array of objects(my model)}], KEY2=>[{array of objects(my model)}]}
Also the keys to model is in snake_case. My requirement is to loop through this and convert this into JSON with camel case keys.
Api response after converting to model: { KEY1=>[{#person_name:"abc", #person_id="123"}],KEY2:[{#personName:"bca", #person_id="231"}] }
Desired output: { KEY1:[{personName:"abc", personId:"123"}],KEY2:[{personName:"bca",personId:"231"}] }
I tried using .map and .transform_values till now, but don't know where I'm doing wrong.
Any help is appreciated.
You can add the following to your ApplicationRecord class:
class ApplicationRecord < ActiveRecord::Base
def serializable_hash(options = {})
hash = super
return hash unless options[:camelize]
hash.deep_transform_keys { |key| key.to_s.camelize(options[:camelize]) }
end
end
This will allow you to call to_json(camelize: :lower) on pretty much any object:
{
KEY1: Person.where(...),
KEY2: Person.where(...),
}.to_json(camelize: :lower)
To automatically serialize the whole collection
You can do something like this:
{
KEY1: Person.where(...).select(:name, :id).map { |p| Hash[p.serializable_hash.map { |key, value| ["#{p.model_name.name.downcase}#{key.capitalize}", value] }] },
KEY2: Person.where(...).select(:name, :id).map { |p| Hash[p.serializable_hash.map { |key, value| ["#{p.model_name.name.downcase}#{key.capitalize}", value] }] }
}.to_json

How to write test cases for ruby on rails module without an associated database table?

I have below module
Organization::EmployeeApis
I have the get_employee method in this module.
def get_employee(parameters)
request = {
employee_data: {
name: {
legal_name: {
name_details: {
first_name: parameters["first_name"],
middle_name: parameters["middle_name"],
surname: parameters["surname"]
}
}
},
contact_data: {
email: parameters["email"]
}
}
}
employee = Employee.new(:get_employee, :get_employee_request, parameters["organization_id"])
employee.get_response(employee)
end
I need to write test cases for the above module which is without an associated database table?
You can create a new class, include your module, and test it:
let(:dummy) do
Class.new do
include TheModule
end
end
subject { dummy.new.get_employee(parameters) }
context "..." do
let(:parameters) { ... }
it "..." do
expect(subject).to eq(...)
end
end
It doesn't imply there should be a database table anywhere, just in the case Employee is a model backed by a database table, you'll need it. For the module you don't need one, it just depends on what it does.

Structure json inside controller

I need to do a json structure like this, but with more than 1 items:
charge = {
items: [{
name: "Product A",
value: 1000,
amount: 2
}]
}
I have an #items that is #items.pluck(:name, :price)
And I'm trying to create my json like this:
charge = {
items: [{
#items.each do |item|
'name:' = item.name,
'value:' = item.price,
'amount:' = 2
end
}]
}
And return this error:
SyntaxError in CoursesController#qualquer
syntax error, unexpected '=', expecting keyword_end
'name:' = item.name,
How i do this structure?
There are two things I see wrong. First, you're using "=" operator to set a Ruby Hash value. That's not correct, as Ruby hashes use symbols or strings. So your hash values will need to look like this:
{ "may_hash_key" => my.has_value }
or
{ my_hash_key: my.hash_value }
or
{ :may_hash_key => my.has_value }
Take your pick.
Additionally, if you are rendering JSON from your controller action, you can do something like this:
def index
# presumably some setup code
charge = {
items: #items.map do |item| {
name: item.name,
value: item.price,
amount: 2
} end
}
render json: charge
end
If you are not rendering JSON from your controller action, what you can do is set #charge and interact with it as a Ruby hash in your view instead.

Rspec send params to subject

I wrote a small service, which need params: [:search][:area], [:search][:floor] etc.
I write test:
subject { AwesomeService.new(params) }
let(:params) do
{
"search" => {
'area' => object1.area,
'floor' => object1.floor
}
}
end
But my test fails(manually work perfectly). When I debug my service in test mode, params[:search][:floor] is NULL. How can I fix my params in test?
The params object in rails does not care if you look for values in it as symbols or strings:
params[:search][:floor] == params['search']['floor']
A Hash in ruby, though, is different - if you insert strings as keys, you need to query it with strings.
param_hash[:search][:floor] != params['search']['floor']
You stub params as a hash. This means you should either set it with symbols instead of strings, or use HashWithIndifferentAccess.
subject { AwesomeService.new(params) }
let(:params) do
ActiveSupport::HashWithIndifferentAccess.new {
"search" => {
'area' => object1.area,
'floor' => object1.floor
}
}
end

Reference dynamic to groovy closure

I have to do a refactoring in an advanced search method with 500 lines. I split this method using closures in small parts, and now I have a lot of closures but I want to invoke them dynamically.
For example:
def listCriteria={ ... }
def textCriteria={ ... }
def booleanCriteria={ ... }
criteria.listDistinct {
criteries.eachWithIndex { crit, i->
def criteriaType="${crit.type}Criteria"
...
}
}
How can I do that?
Using methods you could dynamically call this methods from a string like this:
def listCriteria() {"list"}
def textCriteria() {"text"}
def string1 = "list"
def string2 = "text"
assert "${string1}Criteria"() == "list"
assert "${string2}Criteria"() == "text"
Edit:
I don't know an elegant way to get a dynamic reference to a closure.
You could use the properties property of your controller class to find all closures and invoke them.
def allClosures = this.properties.findAll{Closure.isAssignableFrom(it.value.getClass())}
def callCriteriaClosureByName(name) {
def criteriaClosure = allClosures.find{it.key == "${name}Criteria"}.value
if(criteriaClosure)
criteriaClosure()
}
Not that nice - but should work.
Closures are good for scoping. What about using a map?
class Criteria {
def listDistinct(closure) {
closure()
}
}
closures = [
listCriteria : { "list" },
textCriteria : { "text" },
booleanCriteria : { "boolean" }
]
def criteries = ["list", "text", "boolean"]
def criteria = new Criteria()
criteria.listDistinct {
criteries.eachWithIndex { crit, index ->
def criteriaType=closures["${crit}Criteria"]
assert criteriaType instanceof Closure
}
}

Resources