1

I'd like to know how to make structs using a class. I know this sounds a bit abstract, but this is what I'd like to achieve:

class Person < StructHelper
   string :name
   string :last_name
   int :age
   long :birth_date

   def handle()
     puts "My name is #{name} and I'm #{age} years old"
   end
end

It could be useful in binary serialization.

Please note that Marshall cannot be used because the binary format I use is very specific (ie: int is 4bytes, long is 8bytes, string is 4bytes[len]+the string itself, etc), that is why I use a class.

It kind of is the same as BinData::Structure but I'd like it to be simpler than what BinData provides and I'd like to understand how it works.

Kind regards

  • So you want a `Struct`-like thing which maintains/restricts element types? – undur_gongor Dec 21 '15 at 15:17
  • It sounds like what you're asking for is limited static data-typing. It's not quite clear what the value of this is in a language where types are dynamic. If you really need to have type-checked APIs, use [queries that test features of the input you get](http://stackoverflow.com/a/2095512/1858225). If you just want serialization, use [a built-in feature](http://www.skorks.com/2010/04/serializing-and-deserializing-objects-with-ruby/). – Kyle Strand Dec 21 '15 at 15:29
  • Edited the subject so that it is clearer. – Alexandre De Angelis Dec 21 '15 at 15:32
  • 1
    @AlexandreDeAngelis If you're looking for a *known binary representation*, that's unfortunately a much stronger guarantee than what you've actually asked for, regardless of the language (even in C/C++ you don't have strong guarantees about the actual binary layout of data unless you use something like `#pragma pack` or a bitfield). But in Ruby in particular, a the binary representation of a `class` *can't* consist of its data members and nothing else, because Ruby classes must contain metadata of some form (this is necessary for Ruby's language features to work). – Kyle Strand Dec 21 '15 at 17:13
  • 1
    Fortunately, `BinData` looks like it's *exactly* what you need for serialization (presumably the real reason you need a known binary format rather than `Marshall` is that you're serializing data for transfer between Ruby and another language, though if so this should be stated in the question). If by "simpler than what `BinData` provides" you mean that you don't need to worry about endianness, well, if you're dealing with binary data you're just going to need to bite the bullet and learn about endianness (which is actually not that complicated anyway). – Kyle Strand Dec 21 '15 at 17:14
  • 1
    If you're just looking for a way to do this in pure Ruby so that you understand the techniques used rather than hiding this with `BinData`, then you're just going to need to write code that looks like the `BinData` example (`len = io.read(2).unpack("v")[0]`, etc), which is presumably what's going on at the heart of `BinData` anyway. The idea is that instead of a "struct", you just have a chunk of bytes, and you pack/unpack data to and from the byte-array explicitly. – Kyle Strand Dec 21 '15 at 17:16
  • @KyleStrand thank you for your detailed answer. Now, I'd rather use a custom class with things I made than using a third-party library. `BinData` is way too complicated, whereas what I'm asking for doesn't look like it... I'm going to have a look at the `BinData` source and see how it works. – Alexandre De Angelis Dec 21 '15 at 17:19
  • 1
    If you don't want to use BinData you're going to become very familiar with using `pack` and `unpack`, as BinData is a convenient layer on top of those. `pack` and `unpack` are the tools used to build and take apart binary structures. – the Tin Man Dec 21 '15 at 18:23
  • 2
    I'm not sure your needs are as simple as you think (because you're fighting the language--Ruby doesn't have a concept of binary structs) or `BinData` is as complicated as you think (though admittedly I haven't used it). This also really seems like an [XY problem](http://meta.stackexchange.com/a/66378/218334). – Kyle Strand Dec 21 '15 at 18:28
  • 1
    It also think it's an XY problem of sorts. Underneath we understand that you have to deal with binary data, but it seems you're going about it the wrong way by not wanting to get familiar with BinData. Assuming you can build a class that will circumvent the need to get your hands dirty is probably the root of the problem. Your hands are going to get dirty dealing with binary data, but BinData will help reduce that somewhat. BinData is like digging dirt with a shovel and `pack`/`unpack` are like using a spoon. You can dig a hole either way but one is easier. – the Tin Man Dec 21 '15 at 20:50

2 Answers2

1

A quick example

Struct.new("Person", :name, :last_name, :age, :birth_date)
a = Struct::Person.new("Abc", "XYZ", 12, 121)
#=> #<struct Struct::Person name="Abc", last_name="XYZ", age=12, birth_date=121>

Serialization using Marshal#dump

serialized_object = Marshal::dump(a)
shivam
  • 16,048
  • 3
  • 56
  • 71
  • Same as undur_gongor, this Struct class doesn't permit me to define types so the binary serialization doesn't know if it's a string, a long, w/e. (since I want to use it in order to serialize in a binary format, big-endian) – Alexandre De Angelis Dec 21 '15 at 15:11
  • 1
    you can use marshal dump for binary serialization. Not sure why you need to specify types. Maybe you can explain your requirements further? – shivam Dec 21 '15 at 15:18
1

Do you mean creating a class from a struct ? Like below ? I recently read somewhere that would be a bad practice, Perhaps we can start a discussion here if that is realy the case.

# approach 1
Team = Struct.new(:players, :coach)
class Team
  def slogan
    "A stitch in time saves nine"
  end
end
some_team = Team.new(some_players, some_coach)

# approach 2
class Team < Struct.new(:players, :coach)
  def slogan
    "It takes one to know one"
  end
end

# approach 3
Foo = Struct.new(:x) do
  def hello
    "hi"
  end
end
peter
  • 41,770
  • 5
  • 64
  • 108