Ed de Almeida Ed de Almeida - 1 month ago 11
Ruby Question

I need to initialize something I really don't know previously what it is in Ruby

Let me try to explain what I need, then maybe someone may tell me how to do it.

I have a series of entities in my system. They are listed in an array

@entities = [ :component, :email, :form, :library, :page, :project, :resource, :template ]


I parse a command line using Slop gem, then I get all parsed options in the opts hash and the remaining command line parameters in the args array.

The first element of args array is always the entity I need to create to handle the command line inputs. Let's assume, for the sake of this reasoning, that I'm creating a :project.

Now, I have a file project.rb, which contains:

entity_block = lambda { |ent|
ent.entity_type = :project
# All other initialization options
.
.
.
}


And in my main module I have the following method:

def self.start(args,opts)

# Saves arguments and options
@arguments = args
@options = opts

# Isolates the entity which will handle the command line request
@entity = @arguments[0]
exit_error(103,@entites) if not @entities.include?(@entity.to_sym)

# Requires the correct block for the entity type
require "entities/#{@entity}"

# Creates the entity class
EmeraldFW::Entity.new(@arguments,@options) do
entity_block
end
end


And here is my entity.rb file:

module EmeraldFW

class Entity

attr_accessor :entity_type, :entity_base_dir

def initialize(args,opts)
@args = args
@opts = opts
end

def execute
puts entity_base_dir
puts "Do something..."
end

end

end


As you may see, what I intend to do is requiring among the
<entity_name>.rb
files, the file which contains the specific initial values for that specific entity.

The idea is having
entity_type
, for instance, initialized after running the code. Not just his, but all other parameters needed to identify that specific entity the user mentioned at the command line.

It happens that this is not working. If it were, I wouldn't be here bothering you, of course.

I tried do use
#tap
to inject the newly created Entity object in the lambda block defined by
entity_block
, like this:

EmeraldFW::Entity.new(@arguments,@options).tap do
entity_block
end


And I also tried to do
&entity_block
like this?

EmeraldFW::Entity.new(@arguments,@options).tap do
&entity_block
end


Summary: My problem, then, is how to create an entity dinamically?

Of course I considered the possibility of doing something like

entity_class = "EmeraldFW::#{@entity.capitalize}".split('::').inject(Object) {|o,c| o.const_get c}.new(@arguments,@options)


But first I'd like to know if this other way is possible and what I am missing here.

By the way, this is the error I always get:

/home/edvaldo/software/github/emeraldfw21/lib/emeraldfw.rb:34:in `block in start': undefined local variable or method `entity_block' for EmeraldFW:Module (NameError)

Answer

if entity_block is a proc or lambda it won't be called if you just type it's name with out parenthesis (unlike a typical method, which will be called).

So I think your issue is that your block is never being called.

Try, instead, this:

entity = EmeraldFW::Entity.new(@arguments,@options)

entity_block.call(entity)

# similarly
entity.instance_eval { entity_block.call(self) }

By the way, regarding your use of tap - It looks like you're trying to achieve some dynamic scoping, which it won't do for you. It's really just syntactic sugar for assigning a variable, if you ask me. To get dynamic scoping for a block you can use class_exec or instance_exec. However doing this is unnecessary if you're explicitly passing around objects as arguments and aren't trying to redefine self.