Am I Doing it All Wrong? Ruby Class Objects

I’ve been using ruby hashes for storing info for quite some time. Hashes are very easy to convert to json and back. The json strings are stored as an attribute of the component instance.

This works pretty good, but I’m wondering if I would be better off using classes instead after reading this post.

A few benefits I can think of are.

  1. Reduce the chance of misspelling property names.
  2. Methods on the class could be contained to the class. Such as calculating position.
  3. Potentially more manageable code base. (shorter methods, and less repetition)

Any thoughts? More pros and cons or tips before I dive into refactoring my code?

I was thinking I would make the initializer take a component instance, then read the json attribute to populate properties.

Before you go and write custom data classes, look at the standard library OpenStruct class. They are compatible with the JSON library and hashes. (Ie, the library extends this class just as it extends others by defining the #to_json and #as_json methods.)

Class: OpenStruct (Ruby 2.5.5) (Better doc)
Class: OpenStruct (Ruby 2.7.1) (The good class introduction missing … I do not know why!)

Search on my name in this category on “OpenStruct” and you’ll find I’ve expounded upon this before.

I even wrote an example of a custom struct-like factory class:

I started out using OpenStruct, but then switched to using hash because I thought it was easier to use. I cant remember exactly why. I think it was because converting nested hashes to json was easier than OpenStruct.

person = { 'name' => 'Neil', 'contact_info' => { 'phones' => ['123-456-7890', '987-654-3210'], 'email' => 'email@gmail.com' } }
json = person.to_json
{"name":"Neil","contact_info":{"phones":["123-456-7890","987-654-3210"],"email":"email@gmail.com"}}
person2 = JSON.parse(json)
{"name"=>"Neil",
 "contact_info"=>
  {"phones"=>["123-456-7890", "987-654-3210"], "email"=>"email@gmail.com"}}

The #to_json method should take care of converting nested hashes, structs, arrays and the like.

1 Like

It depends.
If your classes just act like a data container, I do not see much difference between a key-value pair and an instance property, in Ruby. In strongly typed languages I would say it should be a class and never a key-value container, since your class (or preferably interface) acts as a contract.

1 Like

That’s another thing I’m struggling with, coming from C# where everything is a class, and everything has a type.

Yea, which is why I suggest OpenStructs as they allow both [] and dot notation for accessing the data fields.


ADD: By the way, that DataForge example could be easily modified to make it JSON compatible.

1 Like

Well, in c# you could make everything a Dictionary<string, object>. and for some things you probably do, for dynamic responses of webrequests maybe…

But again, this only make sense for data containers, not for actual objects that ‘do’ things.

1 Like

Hmm I see… Javascript-ish. One other language I work with in a daily base. I actually make that mistake quite often in Ruby, that I accidentally use dot notation on a hash, after working for a few days in javascript for creating an extension’s UI for example.

1 Like

Right I understand that. I’m specifically asking about objects like a window.

I guess that window should redraw itself when you change a property? Then I wonder how you would make that work when your window object is a Hash? I would make a window class with a constructor that receives a ComponentInstance and reads/writes from/to an attributes dictionery whenever the instance properties are get/set

Yes. What I’m currently doing is calling a method from the dialog callback.

sample:

def draw_window(inst)
  json = inst.get_attribute('bc', 'specs')
  specs = JSON.parse(json)

  #redraw the window
end

The problem is with this approach I end up trying to either pass around the specs hash, or reparsing the json when making generic methods that apply to other kinds of objects like doors.

Am I doing something wrong?

parsed_data = JSON.parse('{"color": "blue"}', object_class: OpenStruct)
parsed_data.to_json
"#<OpenStruct color=\"blue\">"

Make a window class that receives a component instance.
Give that class methods such as width(), width=(value), …
So, if, from the exterior you call those (lets cal´ them setters, just like c#) methods with new values, your Window class instance should do 2 things: store the data in the Component’s attributes and secondly redraw itself.

Sorry for the typos, it’s 8.30pm and I’m on my cellphone.

1 Like

Ok that was the path my brain was going down.

Slightly. For json text that a web dialog understands, I’d do …

json = parsed_data.to_h.to_json

But it isn’t you. It seems you are correct that OpenStructs are creating weird JSON text that will probably make Javascript in web dialog choke.

It’s coming back to me now.

parsed_data = JSON.parse('{"color": "blue", "nested": { "test": "value"}}', object_class: OpenStruct)
parsed_data.to_h.to_json
{"color":"blue","nested":"#<OpenStruct test=\"value\">"}
1 Like

I’m thinking down this line for global extension settings.

EDIT: I already have the local storage of settings figures out. I’m only asking about making them available for access to all my classes.

Am I on the right track?

module MyExtension
  class Info
    class << self
      attr_accessor :settings
    end
  end

  class Base
    attr_accessor :settings, :info, :s
    def initialize
      @info = BC::Info
      @settings = info.settings
      @s = Sketchup.active_model.selection
    end
  end

  class Window < Base
    def initialize
      super
    end

    def first_selected
      s.count > 0 ? s[0] : nil 
    end
  end

  window = MyExtension::Window.new
  window.first_selected
end