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.