# Rails OrderedOptions vs. Ruby's OpenStruct

**URL:** <https://discuss.rubyonrails.org/t/rails-orderedoptions-vs-rubys-openstruct/82686>\
**Category:** rubyonrails-talk\
**Created:** [April 16, 2023, 1:52am UTC](https://discuss.rubyonrails.org/t/rails-orderedoptions-vs-rubys-openstruct/82686 "2023-04-16T01:52:36Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![software\_writer](https://sea2.discourse-cdn.com/rubyonrails/user_avatar/discuss.rubyonrails.org/software_writer/32/4461_2.png) [@software\_writer](https://discuss.rubyonrails.org/u/software_writer)\
**Post date:** [April 16, 2023, 1:52am UTC](https://discuss.rubyonrails.org/t/rails-orderedoptions-vs-rubys-openstruct/82686/1 "2023-04-16T01:52:36Z")

</div>

I am trying to understand the difference between [`OrderedOptions`](https://api.rubyonrails.org/classes/ActiveSupport/OrderedOptions.html) class provided by ActiveSupport and Ruby’s [`OpenStruct`](https://ruby-doc.org/3.2.0/stdlibs/ostruct/OpenStruct.html) class.

**What’s the difference between the two? When would you use one over the other?**

## `OrderedOptions`

```ruby
require "active_support/ordered_options"

config = ActiveSupport::OrderedOptions.new

# set the values
config.api_key = "my-api-key"
config.api_secret = "my-api-secret"

# access the values
config.api_key # => 'my-api-key'
config.api_secret # => 'my-api-secret'

```

## OpenStruct

```ruby
require 'ostruct'

config = OpenStruct.new

# set the values
config.api_key = "my-api-key"
config.api_secret = "my-api-secret"

# access the values
config.api_key # => 'my-api-key'
config.api_secret # => 'my-api-secret'

```

Thanks!

---

<div class="post-metadata">

**Author:** ![nikita](https://sea2.discourse-cdn.com/rubyonrails/user_avatar/discuss.rubyonrails.org/nikita/32/5647_2.png) [@nikita](https://discuss.rubyonrails.org/u/nikita)\
**Post date:** [May 10, 2023, 3:08pm UTC](https://discuss.rubyonrails.org/t/rails-orderedoptions-vs-rubys-openstruct/82686/2 "2023-05-10T15:08:47Z")

</div>

My comment may not age well but `OpenStruct` occasionally being slower compared to alternatives. It may not be true anymore or not true for specific ruby versions but still these uncertainties would be the reason why I would prefer an alternative

[https://docs.rubocop.org/rubocop-performance/cops\_performance.html#performanceopenstruct](https://docs.rubocop.org/rubocop-performance/cops_performance.html#performanceopenstruct)

> **[How OpenStruct Can Kill Performance](https://www.honeybadger.io/blog/how-openstruct-and-hashes-can-kill-performance/)**
>
> An OpenStruct is around 10x slower to initialize than a Struct. That was the surprising result of this benchmark where we pitted structs vs classes vs hashes vs OpenStruct. Hashes didn't do much better.

> **[Bug #18032: Openstruct is ~20..25x slower with Ruby 3.0.0 and 3.0.1 compared...](https://bugs.ruby-lang.org/issues/18032)**
>
> Redmine

> **[Bug #19424: Significant performance decreases in \`OpenStruct#marshal\_load\` in...](https://bugs.ruby-lang.org/issues/19424)**
>
> Redmine

---

<div class="post-metadata">

**Author:** ![brenogazzola](https://sea2.discourse-cdn.com/rubyonrails/user_avatar/discuss.rubyonrails.org/brenogazzola/32/3856_2.png) [@brenogazzola](https://discuss.rubyonrails.org/u/brenogazzola)\
**Post date:** [May 10, 2023, 4:34pm UTC](https://discuss.rubyonrails.org/t/rails-orderedoptions-vs-rubys-openstruct/82686/3 "2023-05-10T16:34:50Z")

</div>

OrderedOption can be accessed like a hash, and you can use symbols or strings:

```ruby
config = ActiveSupport::OrderedOptions.new
config.api_key = "my-api-key"

config["api_key"] # => "my-api-key"
config[:api_key] # => "my-api-key"

```

---

<div class="post-metadata">

**Author:** ![davetron5000](https://sea2.discourse-cdn.com/rubyonrails/user_avatar/discuss.rubyonrails.org/davetron5000/32/1111_2.png) [@davetron5000](https://discuss.rubyonrails.org/u/davetron5000)\
**Post date:** [May 12, 2023, 2:15pm UTC](https://discuss.rubyonrails.org/t/rails-orderedoptions-vs-rubys-openstruct/82686/4 "2023-05-12T14:15:12Z")

</div>

The use-cases you are showing are similar, and while both support `[]` like a hash, `OrderedOptions` has these additional features:

- `is_a?(Hash)` returns true, so it can be used with anything expecting a hash. Because of this it also supports the `Hash` constructor that returns a default when a key isn’t found

- you can access required options with a bang and raise an exception:

I _think_ the point of `OrderedOptions` is to make a somewhat safer hash to store options or configuration. To be opinionated for a moment, I don’t think it’s any better than `OpenStruct` for that purpose and if you want to model some configuration, you would be better served by making a class that has defined properties for all the config options and can validate itself. You could that with plain ole Ruby:

```ruby
class MyConfig
  attr_reader :api_key, :api_secret
  def initialize(api_key:, api_secret:)
    @api_key = api_key
    @api_secret = api_secret
    raise ArgumentError, "api_key is required" if @api_key.nil?
    raise ArgumentError, "api_secret is required" if @api_secret.nil?
  end
end

config = MyConfig.new(
  api_key: ENV["API_KEY"],
  api_secret: ENV["API_SECRET"]) # or whatever

```

That’s a bit more code for sure, but you are unlikely to modify this code frequently and the tradeoff of getting explicit errors when required stuff is missing will be more of a benefit over time than saving a few lines of code up front.

---

<div class="post-metadata">

**Author:** ![software\_writer](https://sea2.discourse-cdn.com/rubyonrails/user_avatar/discuss.rubyonrails.org/software_writer/32/4461_2.png) [@software\_writer](https://discuss.rubyonrails.org/u/software_writer)\
**Post date:** [May 17, 2023, 4:44pm UTC](https://discuss.rubyonrails.org/t/rails-orderedoptions-vs-rubys-openstruct/82686/5 "2023-05-17T16:44:22Z")

</div>

Thanks for the detailed answer @davetron5000, much appreciated!
