Getting error when trying to encode message with Protobuf - ruby-on-rails

Protobuf generated:
# source: event.proto
require 'google/protobuf'
Google::Protobuf::DescriptorPool.generated_pool.build do
add_file("event.proto", :syntax => :proto3) do
add_message "myapp.Event" do
optional :name, :string, 1
optional :entity, :enum, 2, "myapp.Event.Entity"
oneof :event_data do
optional :first_event_data, :message, 3, "myapp.Event.FirstEventData"
optional :second_event_data, :message, 4, "myapp.Event.SecondEventData"
end
end
add_message "myapp.Event.FirstEventData" do
optional :id, :string, 1
optional :to, :string, 2
optional :from, :string, 3
end
add_message "myapp.Event.SecondEventData" do
optional :metadata_url, :string, 1
end
add_enum "myapp.Event.Entity" do
value :FIRST, 0
value :SECOND, 1
end
end
end
module Myapp
Event = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("myapp.Event").msgclass
Event::FirstEventData = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("myapp.Event.FirstEventData").msgclass
Event::SecondEventData = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("myapp.Event.SecondEventData").msgclass
Event::Entity = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("myapp.Event.Entity").enummodule
end
Now in the console when I do
message = Myapp::Event.new(
entity: :SECOND,
name: "started",
event_data: {
second_event_data:
Myapp::Event::SecondEventData.new(
metadata_url: "local-dev-url",
)
}
)
I get this error:
Traceback (most recent call last):
3: from (irb):178
2: from (irb):178:in `new'
1: from (irb):178:in `initialize'
ArgumentError (Unknown field name 'event_data' in initialization map entry.)
I have tried different combinations and every time different error, I think I am organising my message incorrectly.
Any help would be appreciated greatly. Thanks

Well, after lots of research, apparently I can't do it all at once. I need to create 2 or 3 seperate objects:
i.e
message = Myapp::Event.new(
entity: :SECOND,
name: "started")
And then I can do this:
message.second_event_data = Myapp::Event::SecondEventData.new(
metadata_url: "local-dev-url",
)
Then when I encode it, second_event_data will sits under the event_data

Related

Why GraphQL-Ruby does not understand dynamically generated schema definitions?

In Rails app, I have the following part of the schema defined:
# frozen_string_literal: true
module Types
module KVInfo
def self.kv_value_scalar(typename, raw_type: String, typeid:)
clazz = Class.new(BaseObject) do
graphql_name "KVEntry#{typename}Value"
field :value, raw_type, null: false
end
clazz.define_singleton_method(:typeid) { typeid }
clazz.define_singleton_method(:typename) { typename }
clazz
end
# typeids taken from enum in (.../kv_info.ts)
KVScalars = [
kv_value_scalar('String', typeid: 0),
kv_value_scalar('Markdown', typeid: 1),
kv_value_scalar(
'Date',
raw_type: GraphQL::Types::ISO8601DateTime,
typeid: 2
),
kv_value_scalar('Country', typeid: 3),
kv_value_scalar('Address', typeid: 5)
].freeze
KVScalars.each { |t| KVInfo.const_set(t.graphql_name, t) }
class KVScalarValue < BaseUnion
possible_types(*KVScalars)
def self.resolve_type(obj, _ctx)
KVScalars.select { |t| t.typeid == obj['type'] }.first
end
end
def self.kv_value_array(subtype)
clazz = Class.new(BaseObject) do
graphql_name "KVEntryArray#{subtype.typename}"
field :value, [subtype], null: false
end
clazz.define_singleton_method(:sub_typeid) { subtype.typeid }
clazz
end
KVArrays = KVScalars.map { |s| kv_value_array(s) }
KVArrays.each { |t| KVInfo.const_set(t.graphql_name, t) }
class KVArrayValue < BaseUnion
possible_types(*KVArrays)
def self.resolve_type(obj, _ctx)
KVArrays.select { |t| t.sub_typeid == obj['subtype'] }
end
end
class KVValue < BaseUnion
# PP HERE
possible_types(KVArrayValue, KVScalarValue)
def self.resolve_type(obj, _ctx)
obj['type'] == 4 ? # typeid for array
KVArrayValue :
KVScalarValue
end
end
class KVEntry < BaseObject
field :name, String, null: false
field :value, KVValue, null: false
end
end
end
While running a Rake task that dumps the whole schema to a file to be consumed by frontend, I see the type denoted by KVEntry class having only the name field.
If I put all possible types in the KVValue class like such:
pp(*KVScalars, *KVArrays)
possible_types(*KVScalars, *KVArrays)
it works and generates types correctly.
But note the pp line above - it does not work without this line (???).
Also, if I keep it as is (with nested unions), it does not work regardless of number and positions of pp clauses. When going through with the debugger, all classes are loaded correctly, including generated ones, but the schema still lacks required types.
So the question is what the bisq... why the types are not processed and how can pp affect this process in any sense?
P.S. The data format is fixed by frontend and no way subject to change.
The problem was in the nested unions. GraphQL does not support these. And on the part of plain union not working - I still have no idea of reasons for such behavior, but it fixed itself after N-th restart.

ArgumentError (invalid argument: nil) in Rails/GraphQL

So I'm working through building out an app based on HowToGraphQL and stuck on the filtering of the app. I end up getting
ArgumentError (invalid argument: nil.):
app/graphql/resolvers/items_search.rb:26:in 'apply_filter'
My items_search.rb looks like:
require 'search_object/plugin/graphql'
class Resolvers::ItemsSearch
include SearchObject.module(:graphql)
scope { InventoryItem.all }
# return type
type !types[Types::InventoryItemType]
InventoryItemFilter = GraphQL::InputObjectType.define do
name 'InventoryItemFilter'
argument :OR, -> { types[InventoryItemFilter] }
argument :material_contains, types.String
# argument :balance, !types.Int
# argument :age, !types.Int
# argument :width, !types.Int
argument :roll_number_contains, types.String
argument :dye_number_contains, types.String
end
option :filter, type: InventoryItemFilter, with: :apply_filter
def apply_filter(scope, value)
branches = normalize_filters(value).reduce { |a, b| a.or(b) }
scope.merge(branches)
end
def normalize_filters(value, branches = [])
# add like SQL conditions
scope = InventoryItem.all
scope = scope.where('material LIKE ?', "%#{value['material_contains']}%") if value['material_contains']
scope = scope.where('roll_number LIKE ?', "%#{value['roll_number_contains']}%") if value['roll_number_contains']
scope = scope.where('dye_number LIKE ?', "%#{value['dye_number_contains']}%") if value['dye_number_contains']
# continue to normalize down
value['OR'].reduce(branches) { |s, v| normalize_filters(v, s) } if value['OR'].present?
branches
end
end
No idea what it's throwing up invalid argument: nil though.

How to compare if a record exist with json data type field?

I want to check if a record already exist on database, but I have one json data type field and I need to compare it too.
When I try check using exists? I got the following error:
SELECT 1 AS one FROM "arrangements"
WHERE "arrangements"."deleted_at" IS NULL AND "arrangements"."account_id" = 1
AND "arrangements"."receiver_id" = 19 AND "config"."hardware" = '---
category: mobile
serial: ''00000013''
vehicle:
' AND "arrangements"."recorded" = 't' LIMIT 1
PG::UndefinedTable: ERROR: missing FROM-clause entry for table "config"
LINE 1: ...id" = 1 AND "arrangements"."receiver_id" = 19 AND "config"."...
^
Code that I using to check if a exists:
#arrangement = Arrangement.new({account_id: receiver.account.id, receiver_id: receiver.id, config: params[:config], recorded: true})
if Arrangement.exists?(account_id: #arrangement.account_id, receiver_id: #arrangement.receiver_id, config: #arrangement.config, recorded: #arrangement.recorded)
puts 'true'
end
I already tried:
if Arrangement.exists?(#arrangement)
puts 'true'
end
But always return false
Table:
create_table :arrangements do |t|
t.references :account, index: true
t.references :receiver, index: true
t.json :config, null: false
t.boolean :recorded, default: false
t.datetime :deleted_at, index: true
t.integer :created_by
t.timestamps
end
You cannot compare jsons. Try to compare some jsons values
where("arrangements.config->>'category' = ?", params[:config][:category])
Look in postgresql docs for other JSON functions and operators
This will convert both field(in case it is just json) and the parameter(which will be a json string) to jsonb, and then perform a comparison of everything it contains.
def existing_config?(config)
Arrangement.where("config::jsonb = ?::jsonb", config.to_json).any?
end

Parse ruby code

I need help in one problem. I have a table with columns that contain some ruby code, like this: self.org_premium = self.volume / 12 * 0.1492 self.billing_premium = self.subscriber_premium + self.org_premium or employment_level == 'P' or vol_life.save.
And now I want find methods in these strings, but some Rails methods, like save or nil? must be ignored.
I used Ripper, but his method slice return only 1 param.
Maybe you have some idea about this?
When you use Ripper to slice, e.g.:
$ irb
2.0.0p247 :001 > p Ripper.slice('def m(a) nil end', 'ident')
To see what events are available, just evaluate the constants it refers to in the doc: EVENTS which are further broken down into PARSER_EVENTS, and SCANNER_EVENTS.
$ irb
2.0.0p247 :001 > require 'ripper'
2.0.0p247 :002 > Ripper::EVENTS
=> [:BEGIN, :END, :alias, :alias_error, :aref, :aref_field, :arg_ambiguous, :arg_paren, :args_add, :args_add_block, :args_add_star, :args_new, :array, :assign, :assign_error, :assoc_new, :assoc_splat, :assoclist_from_args, :bare_assoc_hash, :begin, :binary, :block_var, :block_var_add_block, :block_var_add_star, :blockarg, :bodystmt, :brace_block, :break, :call, :case, :class, :class_name_error, :command, :command_call, :const_path_field, :const_path_ref, :const_ref, :def, :defined, :defs, :do_block, :dot2, :dot3, :dyna_symbol, :else, :elsif, :ensure, :excessed_comma, :fcall, :field, :for, :hash, :if, :if_mod, :ifop, :lambda, :magic_comment, :massign, :method_add_arg, :method_add_block, :mlhs_add, :mlhs_add_star, :mlhs_new, :mlhs_paren, :module, :mrhs_add, :mrhs_add_star, :mrhs_new, :mrhs_new_from_args, :next, :opassign, :operator_ambiguous, :param_error, :params, :paren, :parse_error, :program, :qsymbols_add, :qsymbols_new, :qwords_add, :qwords_new, :redo, :regexp_add, :regexp_literal, :regexp_new, :rescue, :rescue_mod, :rest_param, :retry, :return, :return0, :sclass, :stmts_add, :stmts_new, :string_add, :string_concat, :string_content, :string_dvar, :string_embexpr, :string_literal, :super, :symbol, :symbol_literal, :symbols_add, :symbols_new, :top_const_field, :top_const_ref, :unary, :undef, :unless, :unless_mod, :until, :until_mod, :var_alias, :var_field, :var_ref, :vcall, :void_stmt, :when, :while, :while_mod, :word_add, :word_new, :words_add, :words_new, :xstring_add, :xstring_literal, :xstring_new, :yield, :yield0, :zsuper, :CHAR, :__end__, :backref, :backtick, :comma, :comment, :const, :cvar, :embdoc, :embdoc_beg, :embdoc_end, :embexpr_beg, :embexpr_end, :embvar, :float, :gvar, :heredoc_beg, :heredoc_end, :ident, :ignored_nl, :int, :ivar, :kw, :label, :lbrace, :lbracket, :lparen, :nl, :op, :period, :qsymbols_beg, :qwords_beg, :rbrace, :rbracket, :regexp_beg, :regexp_end, :rparen, :semicolon, :sp, :symbeg, :symbols_beg, :tlambda, :tlambeg, :tstring_beg, :tstring_content, :tstring_end, :words_beg, :words_sep]
2.0.0p247 :009 > Ripper::PARSER_EVENTS
=> [:BEGIN, :END, :alias, :alias_error, :aref, :aref_field, :arg_ambiguous, :arg_paren, :args_add, :args_add_block, :args_add_star, :args_new, :array, :assign, :assign_error, :assoc_new, :assoc_splat, :assoclist_from_args, :bare_assoc_hash, :begin, :binary, :block_var, :block_var_add_block, :block_var_add_star, :blockarg, :bodystmt, :brace_block, :break, :call, :case, :class, :class_name_error, :command, :command_call, :const_path_field, :const_path_ref, :const_ref, :def, :defined, :defs, :do_block, :dot2, :dot3, :dyna_symbol, :else, :elsif, :ensure, :excessed_comma, :fcall, :field, :for, :hash, :if, :if_mod, :ifop, :lambda, :magic_comment, :massign, :method_add_arg, :method_add_block, :mlhs_add, :mlhs_add_star, :mlhs_new, :mlhs_paren, :module, :mrhs_add, :mrhs_add_star, :mrhs_new, :mrhs_new_from_args, :next, :opassign, :operator_ambiguous, :param_error, :params, :paren, :parse_error, :program, :qsymbols_add, :qsymbols_new, :qwords_add, :qwords_new, :redo, :regexp_add, :regexp_literal, :regexp_new, :rescue, :rescue_mod, :rest_param, :retry, :return, :return0, :sclass, :stmts_add, :stmts_new, :string_add, :string_concat, :string_content, :string_dvar, :string_embexpr, :string_literal, :super, :symbol, :symbol_literal, :symbols_add, :symbols_new, :top_const_field, :top_const_ref, :unary, :undef, :unless, :unless_mod, :until, :until_mod, :var_alias, :var_field, :var_ref, :vcall, :void_stmt, :when, :while, :while_mod, :word_add, :word_new, :words_add, :words_new, :xstring_add, :xstring_literal, :xstring_new, :yield, :yield0, :zsuper]
2.0.0p247 :010 > Ripper::SCANNER_EVENTS
=> [:CHAR, :__end__, :backref, :backtick, :comma, :comment, :const, :cvar, :embdoc, :embdoc_beg, :embdoc_end, :embexpr_beg, :embexpr_end, :embvar, :float, :gvar, :heredoc_beg, :heredoc_end, :ident, :ignored_nl, :int, :ivar, :kw, :label, :lbrace, :lbracket, :lparen, :nl, :op, :period, :qsymbols_beg, :qwords_beg, :rbrace, :rbracket, :regexp_beg, :regexp_end, :rparen, :semicolon, :sp, :symbeg, :symbols_beg, :tlambda, :tlambeg, :tstring_beg, :tstring_content, :tstring_end, :words_beg, :words_sep]
The 'ident' is an event for method name definition in this case, and that event isn't really equivalent to a method that is called in the code.
I'm not sure that Ripper would be the easiest way to parse out method names that are used. In addition, the ability for Ruby to handle calls handled by method_missing really make it difficult to see what could be interpreted.
Like I said in the comments, there are several other ways to parse methods you might look into.
You could even just make something similar with string operations/checking available methods, e.g.
class A
IGNORE = %w{save nil?}
def find_possible_methods(s)
s.split(/[\-\ ,\.\(\)\{\}\[\]]/).reject{|c| c =~ /[0-9\*\-\/\+\%\=\~].*/ || c.empty? || IGNORE.include?(c)}
end
def find_implemented_methods(s)
(s.split(/[\-\ ,\.\(\)\{\}\[\]]/) & (methods + private_methods).collect(&:to_s)).reject{|c| IGNORE.include?(c)}
end
end
Usage:
a = A.new
=> #<A:0x007facb9a94be8>
a.find_possible_methods 'self.org_premium = self.volume / 12 * 0.1492 self.billing_premium = self.subscriber_premium + self.org_premium'
=> ["self", "org_premium", "self", "volume", "self", "billing_premium", "self", "subscriber_premium", "self", "org_premium"]

cannot understand rails activerecord typecast reasons

consider that i have a migration as follows
create_table :dummies do |t|
t.decimal :the_dummy_number
end
i instantiate like the following
dummy = Dummy.new
dummy.the_dummy_number = "a string"
puts dummy.the_dummy_number
the output for the above is
0.0
how did this happen? since i assign a wrong value shouldn't it raise an error?
The biggest problem is the following.
Since it automatically converts my validate method fails miserably.
update-the validate method
validate :is_dummy_number_valid, :the_dummy_number
def is_dummy_number_valid
read_attribute(:the_dummy_number).strip()
end
The reason that this does not work as you expect is that the underlying ruby implementation of BigDecimal does not error when passed a string.
Consider the following code
[ 'This is a string', '2is a string', '2.3 is also a string',
' -3.3 is also a string'].each { |d| puts "#{d} = #{BigDecimal.new(d)}" }
This is a string = 0.0
2is a string = 2.0
2.3 is also a string = 2.3
-3.3 is also a string = -3.3
So BigDecimal scans the string and assigns anything at the beginning of the string that could be a decimal to its value.
If you set your model up like this
class Dummy < ActiveRecord::Base
validates_numericality_of :the_dummy_number
end
Then the validation should work fine
>> d=Dummy.new(:the_dummy_number => 'This is a string')
=> #<Dummy id: nil, the_dummy_number: #<BigDecimal:5b9230,'0.0',4(4)>, created_at: nil, updated_at: nil>
>> puts d.the_dummy_number
0.0
=> nil
>> d.valid?
=> false
>> d.errors
=> #<ActiveRecord::Errors:0x5af6b8 #errors=#<OrderedHash
{"the_dummy_number"=>[#<ActiveRecord::Error:0x5ae114
#message=:not_a_number, #options={:value=>"This is a string"}
This works because the validates_numericality_of macro uses the raw_value method to get at the value before it was typecast and assigned to the internal decimal value.

Resources