How to define a type holding an array of arrays? - graphql-ruby

With GraphQL-ruby I want to define a type for a field that holds data in the following structure:
[
["2016-06-07", 14134.84],
["2016-06-08", 14134.84],
# ...
]
How would I go for this?
I tried
module Types
class PerformanceDataType < Types::BaseObject
field :assets, [[Types::AssetType]], null: false
# ....
end
end
module Types
class AssetType < Types::BaseObject
field :date, String, null: false
field :value, Float, null: false
end
end
I am not using that data yet so I can't say if it works, but it feels too unspecific. Generating the schema didn't throw any errors.

We ended up doing it like follows:
# innerArrayType.rb
module Types
class InnerArrayType < Types::BaseObject
field :first_value, GraphQL::Types::ISO8601Date, null: false
field :second_value, Types::DecimalType, null: false
end
end
# someOtherType.rb
module Types
class SomeDataType < Types::BaseObject
field :desiredType, [Types::InnerArrayType], null: false
end
end
I guess the takeaway is that on the lowest level, an array must be defined using Types::BaseObject.

Related

before_create callback generates random array

When I create a randomly generate exam I would like to store all the correct answer in an array. The reason that I am doing this is because when I grade the exam I would like to see if the answer is correct by matching the user_answer with the same element in the correct_answer array. Unfortunately, when i use a callback its putting the correct answers in a random order where I cannot match them appropriately.
##controller##
class ExamsController < ApplicationController
def create
exam = current_user.exams.create!(test_bank_questions: TestBankQuestion.all.sample(5))
exam.answers
redirect_to exam_question_path(exam, '1')
end
end
#####Model######
class Exam
include Mongoid::Document
before_create :answers
field :user_answer, type: Array, default: []
field :correct_answers_ids, type: Array, default: []
belongs_to :user
has_and_belongs_to_many :test_bank_questions
#### This is where my problem is ####
#I am trying to get all the id's of the correct answer
#and put them in an array when the object is created
def answers
exam_questions = self.test_bank_questions
exam_questions.each do |question|
answer_choices = question.answer_choices
answer_choices.each do |choice|
if choice.correct_choice == true
self.correct_answers_ids << choice.id.to_s
end
end
end
return correct_answers_ids
end
end
####Model ####
class TestBankQuestion
include Mongoid::Document
field :question_url, type: String
embeds_many :answer_choices
has_and_belongs_to_many :exams
end
###Model ###
class AnswerChoice
include Mongoid::Document
field :choice_url, type: String, default: []
field :correct_choice, type: Boolean, default: []
embedded_in :test_bank_question
end

Not able to overwrite object properties in "create" method

I have a simple table for storing users accounts information (emails and passwords) with two additional columns:
is_active - says if user account is enable or disabled - the column
type is boolean and in the context of DB2 it is mapped with
decimal(1)
registration_date - says when the user was created - the column
type is datetime and in the context ofDB2 it is mapped with
datetime
As this fields will not be set by the user, I have deleted their inputs from the users _form.
I want to populated this fields in my users controller as follows:
def create
#security_user = SecurityUser.new(params[:security_user])
#security_user.is_active = 0
#security_user.registration_date = DateTime.now
...
end
But I can not pass the validations that I have in the model. They looks like:
class SecurityUser < ActiveRecord::Base
# Loading custom validators
require 'lib/date_format_validator'
...
# Accessible columns
...
# Relationships
...
# Validations
validates :is_active, inclusion: { in: 0..1, message: "only '0' and '1' allowed"}, presence: true
validates :registration_date, date_format:true , presence: true
end
where the 'lib/date_format_validator' looks like follows:
class DateFormatValidator < ActiveModel::EachValidator
def validate_each(object, attribute, value)
if (DateTime.parse(value) rescue ArgumentError) == ArgumentError
object.errors[attribute] << (options[:message] || "is not valid datetime")
end
end
end
What I am doing wrong?
EDIT: The screenshot below displays the errors:
EDIT2: Sam Ruby's answer helps me to finish with something like this for my custom validator method:
class DateFormatValidator < ActiveModel::EachValidator
def validate_each(object, attribute, value)
unless value.kind_of? DateTime || (DateTime.parse(value) rescue ArgumentError) != ArgumentError
object.errors[attribute] << (options[:message] || "is not valid datetime")
end
end
end
and to transform the validates method for the is_active column as follows:
validates :is_active, inclusion: { in: [ true, false ], message: "only 'true' and 'false' allowed"}
because as it is said in the official documentation:
Since false.blank? is true, if you want to validate the presence of a boolean field you should use validates :field_name, :inclusion => { :in => [true, false] }.
The problem is that you are trying to validate the ActiveRecord object as if the columns are of type String. But since you have defined your columns as boolean and datetime, what you will be validating will be of type TrueClass, FalseClass or ActiveSupport::TimeWithZone.
In other words, the values are already parsed.
true is never 0 or 1.
DateTime.parse(DateTime.now) will always raise ArgumentError
If you want to validate the unparsed values, do so in the controller.

rails 3 validations uniqueness on attributes of an association

i have a model called Fund and a model called Company .. where fund belongs_to a company.
i have this validation in my Fund table:
validates :name, presence: true, uniqueness: true
This works both on server side and client side using client_side_validations. But i want my fund names to be unique across both fund.name values and fund.company.name values. And i want to do it in a way it would work with client_side_validations too.
Suggestions?
Ended up creating a very specific validator and adding it to client-side-validation. Here'z the breakdown
In models/fund.rb
validates_fund_name_not_company_name :name
new file in config/initializers/validators .. called fund_name_not_company_name_validator.rb
class FundNameNotCompanyNameValidator < ActiveModel::EachValidator
def validate_each(record, attr_name, value)
if ::Company.exists?(name: value)
record.errors.add(attr_name, :fund_name_not_company_name, options.merge(:value => value))
end
end
end
# This allows us to assign the validator in the model
module ActiveModel::Validations::HelperMethods
def validates_fund_name_not_company_name(*attr_names)
validates_with FundNameNotCompanyNameValidator, _merge_attributes(attr_names)
end
end
module ClientSideValidations::Middleware
class FundNameNotCompanyName < ClientSideValidations::Middleware::Base
def response
if ::Company.exists?(name: request.params[:name])
self.status = 404
else
self.status = 200
end
super
end
end
end
then in app/assets/javascripts/rails.validations.custom.js
clientSideValidations.validators.remote['fund_name_not_company_name'] = function(element, options) {
if ($.ajax({
url: '/validators/fund_name_not_company_name',
data: { name: element.val() },
// async must be false
async: false
}).status == 404) { return options.message; }
}
This helped a great deal

Rails form to edit JSON object as text

I'd like to make a form that lets a user edit one field of a mongoid object as rendered JSON text. There's a field in the model that my rails app should not understand, but I want to expose a generic editor. So for this field, I'd like to render it as pretty JSON, and expose it in a big <textarea> and then parse the JSON back in after any edits.
I can think of a dozen ways to do this, but I'm wonder what would be most consistent with Rails philosophy and least divergent from normal scaffolding. Should I render the object to JSON text in the controller? Then I'd have to repeat that code in the new and edit methods, and the parsing code in the update and create methods, which seems a bit kludgy. Is there a way to define a helper or custom form widget that goes in the _form.html.erb that is more reusable? Or maybe one already written?
You can make your own attribute writer/reader, in the model:
attr_accessible the_field_raw
def the_field_raw
self.the_field.to_s
end
def the_field_raw=(value)
self.the_field = JSON(value)
end
whitch should be compatible with form generators and no extra code in the controllers.
Hope it helps!
Serialize the values as JSON.
class Price < ActiveRecord::Base
serialize :values, JSON
validates :start, :end, :values, :presence => true
end
migration:
class CreateMyModels < ActiveRecord::Migration[7.0]
def change
create_table :my_models do |t|
t.jsonb :name, default: {}, null: false
t.jsonb :description, default: {}, null: false
t.integer :another_param
t.timestamps
end
end
end
model and concern:
class MyModel < ApplicationRecord
AVAILABLE_LOCALES = I18n.available_locales
include JsonLocalize
json_localize :name, :description
end
module JsonLocalize
extend ActiveSupport::Concern
included do
def self.json_localize(*attrs)
self::AVAILABLE_LOCALES.each do |locale|
attrs.each do |attr|
define_method("#{attr}_#{locale}") do
send(attr)[locale.to_s]
end
define_method("#{attr}_#{locale}=") do |value|
send(attr)[locale.to_s] = value
end
end
end
end
end
end
then you can have in your form:
.row
.col-md-6
- MyModel::AVAILABLE_LOCALES.each do |loc|
= f.input "name_#{loc}"
= f.input "description_#{loc}"
controller params:
def resource_params
params.require(:my_model).permit(
[
:another_param
] | [:name, :description].map {|attr| MyModel::AVAILABLE_LOCALES.map { |loc| "#{attr}_#{loc}".to_sym } }.flatten
)
end

How do I insert a record in Rails when there's no model and without using SQL

I have created a table that implements an n-to-n relation using the following statement:
create_table :capabilities_roles, :id => false do |t|
t.integer :capability_id, :null => false
t.integer :role_id, :null => false
end
There is no model for this table. How do I insert records without resorting to SQL?
I found this in the ActiveRecord::ConnectionAdapters::DatabaseStatements module:
insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
and also:
insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [])
I have no idea what arel means. Can someone give me an example of a valid insert? I would like to use stuff like :role_id => Role.find_by_name('Business user') in it.
If you're going to be manipulating the database records via Rails, then there should be a model for it. Just create a role.rb in your models directory with the lines
class Role < ActiveRecord::Base
end
And you're as good as gold.
It looks like a join table for has and belongs to many relationship between Capability and Role models. You should let the Rails handle it for you. First define required associations:
class Capability < ActiveRecord::Base
has_and_belongs_to_many :roles
end
class Role < ActiveRecord::Base
has_and_belongs_to_many :capabilities
end
Then just add instance of Role model to roles array of an instance of Capability model (or vice versa):
capability.roles << role
role.capabilities << capability
Removing records from join table is done via removing object from an array:
capability.roles -= [role]
In our project we have many meta tables which don't have models. To generate active record models on the fly we use follow module:
module EntityModel
module_function
ACCESS_MODELS = {}
def for(table_name)
return ACCESS_MODELS[table_name] if ACCESS_MODELS.has_key?(table_name.id)
ACCESS_MODELS[table_name] = create_access_model(table_name)
end
def create_access_model(table_name)
Class.new(ActiveRecord::Base) do
self.table_name = table_name
end
end
end
This create anonymous models and store it in the global hash for performance purposes.
Uses as:
EntityModel.for(:users)