Idiomatic Object Creation in Ruby

Idiomatic Ruby for creating an object representing a node in a tree that's aware of its parent

It's not clear to me how the code samples and description provided ties in to REST, but I'll offer my two cents on what I do understand:

I think it would be perfectly valid to use a combination of all three. As for what would be idiomatic, I'd take your cues from the Rails association methods.

api.groups.create follows Rails' has_many syntax. api.create_group and group.create_project follow the has_one syntax. And I'm assuming that, beneath the syntactic sugar of create/create_association, you are using composition to tie together the references to all the classes anyway, so Group.new(api, "group-name")/Project.new(group, "project-name") seem like perfectly valid options as well. There's no rule saying you can't use all three approaches to good effect (and Rails does).

I'd suggest avoiding subclassing Ruby's base classes (you mentioned subclassing Array) unless you have a really good reason to do so. Just use composition to delegate to them when necessary.

How would you express an idiom with this object, if it exists, do this in Ruby?

What about tap?

@objects.some_association.something.hit_database.process.tap do |objects|
if objects.any?
puts "We have these objects:"
objects.each { ... }
end
end

class self idiom in Ruby

First, the class << foo syntax opens up foo's singleton class (eigenclass). This allows you to specialise the behaviour of methods called on that specific object.

a = 'foo'
class << a
def inspect
'"bar"'
end
end
a.inspect # => "bar"

a = 'foo' # new object, new singleton class
a.inspect # => "foo"

Now, to answer the question: class << self opens up self's singleton class, so that methods can be redefined for the current self object (which inside a class or module body is the class or module itself). Usually, this is used to define class/module ("static") methods:

class String
class << self
def value_of obj
obj.to_s
end
end
end

String.value_of 42 # => "42"

This can also be written as a shorthand:

class String
def self.value_of obj
obj.to_s
end
end

Or even shorter:

def String.value_of obj
obj.to_s
end

When inside a function definition, self refers to the object the function is being called with. In this case, class << self opens the singleton class for that object; one use of that is to implement a poor man's state machine:

class StateMachineExample
def process obj
process_hook obj
end

private
def process_state_1 obj
# ...
class << self
alias process_hook process_state_2
end
end

def process_state_2 obj
# ...
class << self
alias process_hook process_state_1
end
end

# Set up initial state
alias process_hook process_state_1
end

So, in the example above, each instance of StateMachineExample has process_hook aliased to process_state_1, but note how in the latter, it can redefine process_hook (for self only, not affecting other StateMachineExample instances) to process_state_2. So, each time a caller calls the process method (which calls the redefinable process_hook), the behaviour changes depending on what state it's in.

Is there a more ruby idiomatic way to remove creation of configurable object dependencies?

I am not seeing any benefit of having a dedicated factory class in Ruby, i.e. it smells of Java-style patterns (working around the fact that classes are not first-class objects there). Why wouldn't you simply:

class Bar
def initialize parameter
@parameter = parameter
end
end

class Foo
attr_reader :barlike_class
def initialize some_parameter, barlike_class: Bar
@barlike_class = barlike_class
@some_parameter = some_parameter
end
def some_method
...
...
bar = barlike_class.new parameter
...
end
end

Ruby: Idiom for create and increment

The question is clear:

  • A variable is known to hold nil or an integer. If nil the variable is to be set equal to 1, else it is to be set equal to its value plus 1.
  • What is the best way to implement this in Ruby?

First, two points.

  • The question states, "If it is nil, it should be initialized with 1.". This contradicts the statement that the variable is known to be nil or an integer, meaning that it has already been initialized, or more accurately, defined. In the case of an instance variable, this distinction is irrelevant as Ruby initializes undefined instance variables to nil when they are referenced as rvalues. It's an important distinction for local variables, however, as an exception is raised when an undefined local variable is referenced as an rvalue.
  • The comments largely address situations where the variable holds an object other than nil or an integer. They are therefore irrelevant. If the OP wishes to broaden the question to allow the variable to hold objects other than nil or an integer (an array or hash, for example), a separate question should be asked.

What criteria should be used in deciding what code is best? Of the various possibilities that have been mentioned, I do not see important differences in efficiency. Assuming that to be the case, or that relative efficiency is not important in the application, we are left with readability (and by extension, maintainability) as the sole criterion. If x equals nil or an integer, or is an undefined instance variable, perhaps the clearest code is the following:

x = 0 if x.nil?
x += 1

or

x = x.nil? ? 1 : x+1

Ever-so-slightly less readable:

x = (x || 0) + 1

and one step behind that:

x = x.to_i + 1

which requires the reader to know that nil.to_i #=> 0.

The OP may regard these solutions as "clumsy", but I think they are all beautiful.

Can an expression be written that references x but once? I can't think of a way and one has not been suggested in the comments, so if there is a way (doubtful, I believe) it probably would not meet the test for readability.

Consider now the case where the local variable x may not have been defined. In that case we might write:

x = (defined?(x) ? (x || 0) : 0) + 1

defined? is a Ruby keyword.

Common Ruby Idioms

The magic if clause that lets the same file serve as a library or a script:

if __FILE__ == $0
# this library may be run as a standalone script
end

Packing and unpacking arrays:

# put the first two words in a and b and the rest in arr
a,b,*arr = *%w{a dog was following me, but then he decided to chase bob}
# this holds for method definitions to
def catall(first, *rest)
rest.map { |word| first + word }
end
catall( 'franken', 'stein', 'berry', 'sense' ) #=> [ 'frankenstein', 'frankenberry', 'frankensense' ]

The syntatical sugar for hashes as method arguments

this(:is => :the, :same => :as)
this({:is => :the, :same => :as})

Hash initializers:

# this
animals = Hash.new { [] }
animals[:dogs] << :Scooby
animals[:dogs] << :Scrappy
animals[:dogs] << :DynoMutt
animals[:squirrels] << :Rocket
animals[:squirrels] << :Secret
animals #=> {}
# is not the same as this
animals = Hash.new { |_animals, type| _animals[type] = [] }
animals[:dogs] << :Scooby
animals[:dogs] << :Scrappy
animals[:dogs] << :DynoMutt
animals[:squirrels] << :Rocket
animals[:squirrels] << :Secret
animals #=> {:squirrels=>[:Rocket, :Secret], :dogs=>[:Scooby, :Scrappy, :DynoMutt]}

metaclass syntax

x = Array.new
y = Array.new
class << x
# this acts like a class definition, but only applies to x
def custom_method
:pow
end
end
x.custom_method #=> :pow
y.custom_method # raises NoMethodError

class instance variables

class Ticket
@remaining = 3
def self.new
if @remaining > 0
@remaining -= 1
super
else
"IOU"
end
end
end
Ticket.new #=> Ticket
Ticket.new #=> Ticket
Ticket.new #=> Ticket
Ticket.new #=> "IOU"

Blocks, procs, and lambdas. Live and breathe them.

 # know how to pack them into an object
block = lambda { |e| puts e }
# unpack them for a method
%w{ and then what? }.each(&block)
# create them as needed
%w{ I saw a ghost! }.each { |w| puts w.upcase }
# and from the method side, how to call them
def ok
yield :ok
end
# or pack them into a block to give to someone else
def ok_dokey_ok(&block)
ok(&block)
block[:dokey] # same as block.call(:dokey)
ok(&block)
end
# know where the parentheses go when a method takes arguments and a block.
%w{ a bunch of words }.inject(0) { |size,w| size + 1 } #=> 4
pusher = lambda { |array, word| array.unshift(word) }
%w{ eat more fish }.inject([], &pusher) #=> ['fish', 'more', 'eat' ]

Idiomatic way to initialize a Ruby class

Typically in ruby, objects are created either like person1 or in the following fashion:

class Person
attr_reader :first, :middle, :last, :phone_number

def initialize(options)
@first = options[:first]
@middle = options[:middle]
@last = options[:last]
@phone_number = options[:phone_number]
end
end

person = Person.new(first: "John", middle: "T", last: "Smith", phone_number"555-5555")

The nice thing about this alternate approach, is that you can optionally include as many or as few of the attributes as you like, as well as allow an arbitrary ordering.

Creating multiple instances of a class. Most idiomatic?

TL;DR

Modeling business domain objects isn't trivial. For robust real-world inventory management, you'll probably want a real database rather than "plain old Ruby objects" (POROs) to manage CRUD operations and data normalization, but you can certainly model your problem domain using some fairly basic POROs like Array and Struct.

Domain Modeling Example and Sample Usage

I may be misunderstanding your question, but it seems like the problem is more about how to properly model the domain than anything else. You really need a fully normalized database with a schema that describes the items in your inventory (and likely each item's location data such as aisles and shelves) rather than complex Ruby objects.

That said, if you're just trying to find a way to represent your current data set in plain Ruby, you might consider the following approach:

require 'singleton'

class Inventory
include Singleton
attr_accessor :items

def initialize
@items = []
end
end

class Item < Struct.new(:name, :price, :quantity, :promo_group)
end

inventory = Inventory.instance

inventory.items.append(
Item.new('apple', 0.99, 5, 'A'),
Item.new('orange', 1.50, 7, 'B'),
)

Basically, the idea is to create a singleton Inventory object that contains an array of Item objects. Each Item object is a Struct that contains whatever data you may need. You manipulate the inventory through the Inventory#items getter and setter methods, or reference the attributes of a single Item in the inventory through the Enumerable-like behavior of its Struct. For example:

inventory.items
#=> [#<struct Item name="apple", price=0.99, quantity=5, promo_group="A">, #<struct Item name="orange", price=1.5, quantity=7, promo_group="B">]

inventory.items.map &:name
#=> ["apple", "orange"]

inventory.items.select { _1.promo_group == ?A }
#=> [#<struct Item name="apple", price=0.99, quantity=5, promo_group="A">]

inventory.items.filter { _1.name == 'apple' }.count
#=> 1

inventory.items.filter { _1.name == 'apple' }.map &:to_h
#=> [{:name=>"apple", :price=>0.99, :quantity=>5, :promo_group=>"A"}]

This solves for the question you asked, but you'll have to build your own methods for basic operations like searching, adding, deleting, or updating specific inventory items. A real database with object-relational mapping (e.g. an ORM interface like ActiveRecord, Sequel, or similar on top of a SQL or NoSQL database) is really a better solution for a real-world application.

Ruby's tap idiom in Python

You can implement it in Python as follows:

def tap(x, f):
f(x)
return x

Usage:

>>> tap([], lambda x: x.append(1))
[1]

However it won't be so much use in Python 2.x as it is in Ruby because lambda functions in Python are quite restrictive. For example you can't inline a call to print because it is a keyword, so you can't use it for inline debugging code. You can do this in Python 3.x although it isn't as clean as the Ruby syntax.

>>> tap(2, lambda x: print(x)) + 3
2
5


Related Topics



Leave a reply



Submit