error_class=NoMethodError error="undefined method `bytesize' Fluentd - ruby

I have the below Fluentd plugin code:
require 'avro'
module Fluent
module TextFormatter
class Sample
end
class AvroFormatter < Formatter
Fluent::Plugin.register_formatter('avro', self)
config_param :schema_file, :string, :default => nil
config_param :schema_json, :string, :default => nil
def configure(conf)
super
if not (#schema_json.nil? ^ #schema_file.nil?) then
raise Fluent::ConfigError, 'schema_json or schema_file (but not both) is required'
end
if #schema_json.nil? then
#schema_json = File.read(#schema_file)
end
#schema = Avro::Schema.parse(#schema_json)
end
def format(tag, time, record)
handler = Sample.new()
end
end
end
end
And I need to instance the class "Sample" in the def "Format". The problem is that when I try to do a http POST against Fluentd the below error appears:
failed: error_class=NoMethodError error="undefined method `bytesize'
This error only appears when the class "Sample" is instanced. I'm new with ruby, and I don't know where is the problem. Should I create the class "Sample" in another file?

I think you're getting this error because code, that calls format expects string result, but instead it gets an instance of Sample class. Try to return some string instead.
You can also use this example here: http://docs.fluentd.org/articles/plugin-development#text-formatter-plugins.

Related

ruby: calling a instance method without using instance

I know in ruby, when we call an instance method, we need to firstly instantiate a class object.
But when I see a open sourced code I got confused.
The code is like this:
File Message.rb
require 'json'
module Yora
module Message
def serialize(msg)
JSON.generate(msg)
end
def deserialize(raw, symbolized_key = true)
msg = JSON.parse(raw, create_additions: true)
if symbolized_key
Hash[msg.map { |k, v| [k.to_sym, v] }]
else
msg
end
end
end
end
File. Persistance.rb
require 'fileutils'
require_relative 'message'
module Yora
module Persistence
class SimpleFile
include Message
def initialize(node_id, node_address)
#node_id, #node_address = node_id, node_address
FileUtils.mkdir_p "data/#{node_id}"
#log_path = "data/#{node_id}/log.txt"
#metadata_path = "data/#{node_id}/metadata.txt"
#snapshot_path = "data/#{node_id}/snapshot.txt"
end
def read_metadata
metadata = {
current_term: 0,
voted_for: nil,
cluster: { #node_id => #node_address }
}
if File.exist?(#metadata_path)
metadata = deserialize(File.read(#metadata_path)) #<============
end
$stderr.puts "-- metadata = #{metadata}"
metadata
end
.....
You can see the line I marked with "<==="
It uses deserialize function that been defined in message class.
And from message class we can see that method is a instance method, not class method.
So why can we call it without instantiating anything like this?
thanks
Message ist an module. Your Class SimpleFile includes this module. so the module methods included in your class SimpleFile. that means, all module methods can now be used like as methods from SimpleFile
see http://ruby-doc.org/core-2.2.0/Module.html for more infos about module in ruby. it's a great feature.
It is being called on an instance. In Ruby, if you leave out the explicit receiver of the message send, an implicit receiver of self is assumed. So, deserialize is being called on an instance, namely self.
Note that this exact same phenomenon also occurs in other places in your code, much earlier (in line 1, in fact):
require 'fileutils'
require_relative 'message'
Here, you also have two method calls without an explicit receiver, which means that the implicit receiver is self.

Confusion about ways to use JSON in ruby sinatra application

I'm making a Ruby Sinatra application that uses mongomapper and most of my responses will be in the JSON form.
Confusion
Now I've come across a number of different things that have to do with JSON.
The Std-lib 1.9.3 JSON class: http://www.ruby-doc.org/stdlib-1.9.3/libdoc/json/rdoc/JSON.html
The JSON Gem: http://flori.github.io/json/
ActiveSupport JSON http://api.rubyonrails.org/classes/ActiveSupport/JSON.html because I'm using MongoMapper which uses ActiveSupport.
What works
I'm using a single method to handle responses:
def handleResponse(data, haml_path, haml_locals)
case true
when request.accept.include?("application/json") #JSON requested
return data.to_json
when request.accept.include?("text/html") #HTML requested
return haml(haml_path.to_sym, :locals => haml_locals, :layout => !request.xhr?)
else # Unknown/unsupported type requested
return 406 # Not acceptable
end
end
the line:
return data.to_json
works when data is an instance of one of my MongoMapper model classes:
class DeviceType
include MongoMapper::Document
plugin MongoMapper::Plugins::IdentityMap
connection Mongo::Connection.new($_DB_SERVER_CNC)
set_database_name $_DB_NAME
key :name, String, :required => true, :unique => true
timestamps!
end
I suspect in this case the to_json method comes somewhere from ActiveSupport and is further implemented in the mongomapper framework.
What doesn't work
I'm using the same method to handle errors too. The error class I'm using is one of my own:
# Superclass for all CRUD errors on a specific entity.
class EntityCrudError < StandardError
attr_reader :action # :create, :update, :read or :delete
attr_reader :model # Model class
attr_reader :entity # Entity on which the error occured, or an ID for which no entity was found.
def initialize(action, model, entity = nil)
#action = action
#model = model
#entity = entity
end
end
Of course, when calling to_json on an instance of this class, it doesn't work. Not in a way that makes perfect sense: apparantly this method is actually defined. I've no clue where it would come from. From the stack trace, apparently it is activesupport:
Unexpected error while processing request: object references itself
object references itself
/home/id833541/.rvm/gems/ruby-1.9.3-p392/gems/activesupport-3.2.13/lib/active_support/json/encoding.rb:75:in `check_for_circular_references'
/home/id833541/.rvm/gems/ruby-1.9.3-p392/gems/activesupport-3.2.13/lib/active_support/json/encoding.rb:46:in `encode'
/home/id833541/.rvm/gems/ruby-1.9.3-p392/gems/activesupport-3.2.13/lib/active_support/json/encoding.rb:246:in `block in encode_json'
/home/id833541/.rvm/gems/ruby-1.9.3-p392/gems/activesupport-3.2.13/lib/active_support/json/encoding.rb:246:in `each'
/home/id833541/.rvm/gems/ruby-1.9.3-p392/gems/activesupport-3.2.13/lib/active_support/json/encoding.rb:246:in `map'
/home/id833541/.rvm/gems/ruby-1.9.3-p392/gems/activesupport-3.2.13/lib/active_support/json/encoding.rb:246:in `encode_json'
But where is this method actually defined in my class?
The question
I will need to override the method in my class like this:
# Superclass for all CRUD errors on a specific entity.
class EntityCrudError < StandardError
def to_json
#fields to json
end
end
But I don't know how to proceed. Given the 3 ways mentioned at the top, what's the best option for me?
As it turned out, I didn't need to do anything special.
I had not suspected this soon enough, but the problem is this:
class EntityCrudError < StandardError
..
attr_reader :model # Model class
..
end
This field contains the effective model class:
class DeviceType
..
..
end
And this let to circular references. I now replaced this with just the class name, which will do for my purposes. Now to_json doesn't complain anymore and I'm happy too :)
I'm still wondering what's the difference between all these JSON implementations though.

riak content_type no method error

I have the 4 node riak setup running on my os x machine. I have the following program -
require 'riak'
class RiakClient < Riak::Client
#attr_accessor :bucket
def initialize(hosts="")
return Riak::Client.new(:nodes => [{:http_port => 8091},{:http_port =>8092},{:http_port=>8093},{:http_port =>8094}])
end
def get_me(bucket, key)
obj = self.bucket(bucket).get(key)
puts obj.data
end
def put_me(bucket, key, data, content_type)
obj=self.bucket(bucket).get_or_new(key)
puts obj.class
obj.content_type=content_type
obj.raw_data=data
obj.store
end
end
if __FILE__ == $0
my_client=RiakClient.new
my_client.put_me("doc", "index.html", "some data goes here", "text/html")
hash=my_client.get_me("doc", "index.html")
end
I am getting the following error
NilClass
riak_client.rb:32:in `put_me': undefined method `content_type=' for nil:NilClass (NoMethodError)
from riak_client.rb:42:in `<main>'
Do I have to import the RiakBucket and RiakObject classes? It seems that the RiakBucket methods cannot be accessed here?
The actual question here is: why does get_or_new return nil?
It's probably because your initialize() method returns a new Riak::Client, but the Riak::Client that is your parent object never gets initialized. Try putting a call to super instead of the return Riak::Client.new in initialize.
Subclassing Riak::Client is a bit dubious here. I would tend to delegate to it instead.

Generating JSON for Sinatra

I'm having an issue with passing the generated JSON notation of my object to my Sinatra application. The problem I have is twofold:
I have 2 classes that are mapped to a database using the Sequel gem. When they generate JSON it is ok and properly implemented.
I have a custom class called registration that maps one of the classes with an additional field. The goal is to generate JSON out of this and pass that JSON to the application using cucumber (test purpose)
The application code responsible for handling the request has the following function defined:
post '/users' do
begin
hash = JSON.parse(self.request.body.read)
registration = Registration.new.from_json(#request.body.read)
registration.user.country = Database::Alaplaya.get_country_by_iso_code(registration.user.country.iso_code)
return 400 unless(registration.is_valid?)
id = Database::Alaplaya.create_user(registration.user)
# If the registration failed in our system, return a page 400.
return 400 if id < 1
end
problem 1: I cannot use the params hash. It exists but is just an empty hash. Why?
problem 2: I cannot deserialize the JSON generated by the class itself. Why?
The registration class looks like this:
require 'json'
class Registration
attr_accessor :user, :project_id
def to_json(*a)
{
'json_class' => self.class.name,
'data' => [#user.to_json(*a), #project_id]
}.to_json(*a)
end
def self.json_create(o)
new(*o['data'])
end
# Creates a new instance of the class using the information provided in the
# hash. If a field is missing in the hash, nil will be assigned to that field
# instead.
def initialize(params = {})
#user = params[:user]
#project_id = params[:project_id]
end
# Returns a string representing the entire Registration.
def inspect
"#{#user.inspect} - #{#user.country.inspect} - #{#project_id}"
end
# Returns a boolean valid representing whether the Registration instance is
# considered valid for the API or not. True if the instance is considered
# valid; otherwise false.
def is_valid?
return false if #user.nil? || #project_id.nil?
return false if !#user.is_a?(User) || !#project_id.is_a?(Fixnum)
return false if !#user.is_valid?
true
end
end
I had to implement the methods to generate the JSON output correctly. When I run this in console I get the following output generated:
irb(main):004:0> r = Registration.new(:user => u, :project_id => 1)
=> new_login - nil - 1
irb(main):005:0> r.to_json
=> "{\"json_class\":\"Registration\",\"data\":[\"{\\\"json_class\\\":\\\"User\\\
",\\\"login\\\":\\\"new_login\\\"}\",1]}"
Which looks like valid JSON to me. However when I POST this to the application server and try to parse this, JSON complains that at least 2 octets are needed and refuses to deserialize the object.
If you're using Sequel as your ORM, try something like this:
In your model:
class Registration < Sequel::Model
many_to_one :user
many_to_one :project
plugin :json_serializer
end
The server:
before do
#data = JSON.parse(request.body.read) rescue {}
end
post '/users' do
#registration = Registration.new #data
if #registration.valid?
#registration.save
#registration.to_json #return a JSON representation of the resource
else
status 422 #proper status code for invalid input
#registration.errors.to_json
end
end
I think you may be overcomplicating your registration process. If the HTTP action is POST /users then why not create a user? Seems like creating a registration is overly complex. Unless your user already exists, in which case POST /users would be incorrect. If what you're really intending to do is add a user to to a project, then you should PUT /projects/:project_id/users/:user_id and the action would look something like this:
class User < Sequel::Model
many_to_many :projects
end
class Project < Sequel::Model
many_to_many :users
end
#make sure your db schema has a table called users_projects or projects_users
put '/projects/:project_id/users/:user_id' do
#find the project
#project = Project.find params[:project_id]
raise Sinatra::NotFound unless #project
#find the user
#user = Project.find params[:project_id]
raise Sinatra::NotFound unless #user
#add user to project's users collection
#project.add_user #user
#send a new representation of the parent resource back to the client
#i like to include the child resources as well
#json might look something like this
#{ 'name' : 'a project name', 'users' : ['/users/:user_id', '/users/:another_user_id'] }
#project.to_json
end

Active Record to_json\as_json on Array of Models

First off, I am not using Rails. I am using Sinatra for this project with Active Record.
I want to be able to override either to_json or as_json on my Model class and have it define some 'default' options. For example I have the following:
class Vendor < ActiveRecord::Base
def to_json(options = {})
if options.empty?
super :only => [:id, :name]
else
super options
end
end
end
where Vendor has more attributes than just id and name. In my route I have something like the following:
#vendors = Vendor.where({})
#vendors.to_json
Here #vendors is an Array vendor objects (obviously). The returned json is, however, not invoking my to_json method and is returning all of the models attributes.
I don't really have the option of modifying the route because I am actually using a modified sinatra-rest gem (http://github.com/mikeycgto/sinatra-rest).
Any ideas on how to achieve this functionality? I could do something like the following in my sinatra-rest gem but this seems silly:
#PLURAL.collect! { |obj| obj.to_json }
Try overriding serializable_hash intead:
def serializable_hash(options = nil)
{ :id => id, :name => name }
end
More information here.
If you override as_json instead of to_json, each element in the array will format with as_json before the array is converted to JSON
I'm using the following to only expose only accessible attributes:
def as_json(options = {})
options[:only] ||= self.class.accessible_attributes.to_a
super(options)
end

Resources