Code Monkey home page Code Monkey logo

ostruct's Introduction

OpenStruct Version Default Gem Test

An OpenStruct is a data structure, similar to a Hash, that allows the definition of arbitrary attributes with their accompanying values. This is accomplished by using Ruby's metaprogramming to define methods on the class itself.

Installation

The ostruct library comes pre-packaged with Ruby. No installation is necessary.

Usage

  require "ostruct"

  person = OpenStruct.new
  person.name = "John Smith"
  person.age  = 70

  person.name      # => "John Smith"
  person.age       # => 70
  person.address   # => nil

An OpenStruct employs a Hash internally to store the attributes and values and can even be initialized with one:

  australia = OpenStruct.new(:country => "Australia", :capital => "Canberra")
    # => #<OpenStruct country="Australia", capital="Canberra">

Hash keys with spaces or characters that could normally not be used for method calls (e.g. ()[]*) will not be immediately available on the OpenStruct object as a method for retrieval or assignment, but can still be reached through the Object#send method.

  measurements = OpenStruct.new("length (in inches)" => 24)
  measurements.send("length (in inches)")   # => 24

  message = OpenStruct.new(:queued? => true)
  message.queued?                           # => true
  message.send("queued?=", false)
  message.queued?                           # => false

Removing the presence of an attribute requires the execution of the delete_field method as setting the property value to +nil+ will not remove the attribute.

  first_pet  = OpenStruct.new(:name => "Rowdy", :owner => "John Smith")
  second_pet = OpenStruct.new(:name => "Rowdy")

  first_pet.owner = nil
  first_pet                 # => #<OpenStruct name="Rowdy", owner=nil>
  first_pet == second_pet   # => false

  first_pet.delete_field(:owner)
  first_pet                 # => #<OpenStruct name="Rowdy">
  first_pet == second_pet   # => true

Development

After checking out the repo, run bin/setup to install dependencies. Then, run rake test to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.

To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and tags, and push the .gem file to rubygems.org.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/ruby/ostruct.

License

The gem is available as open source under the terms of the 2-Clause BSD License.

ostruct's People

Contributors

burdettelamar avatar casperisfine avatar dependabot[bot] avatar drbrain avatar eregon avatar harryuan65 avatar headius avatar hparker avatar hsbt avatar janlelis avatar jfrazx avatar k-tsj avatar k0kubun avatar ko1 avatar mame avatar marcandre avatar nobu avatar nurse avatar olleolleolle avatar petergoldstein avatar rm155 avatar schwad avatar shyouhei avatar stomar avatar yugui avatar znz avatar zverok avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ostruct's Issues

warning: OpenStruct#define_singleton_method accesses caller method's state and should not be aliased

jruby 9.3.3.0 now shows the following warning.
It did not show up in jruby 9.3.2.0.

jruby 9.3.3.0 (2.6.8) 2022-01-19 b26de1f5c5 OpenJDK 64-Bit Server VM 11.0.13+8-Ubuntu-0ubuntu1.20.04 on 11.0.13+8-Ubuntu-0ubuntu1.20.04 +jit [linux-x86_64]
uri:classloader:/META-INF/jruby.home/lib/ruby/stdlib/ostruct.rb:464: warning: OpenStruct#define_singleton_method accesses caller method's state and should not be aliased
uri:classloader:/META-INF/jruby.home/lib/ruby/stdlib/ostruct.rb:468: warning: OpenStruct#block_given? accesses caller method's state and should not be aliased

Gem release latest

Seems like this has not had a gem release since 2018, but there are changes in 2.6.x that have not been pushed out.

This is causing spec failures for JRuby, since we now source ostruct from the gem (currently 0.1.0).

OpenStruct should not alias :method or :public_send on JRuby

On JRuby 9.4, running require 'ostruct' in verbose mode generates warnings saying that OpenStruct#public_send and OpenStruct#method "accesses caller method's state and should not be aliased":

irb(main):001> $VERBOSE = true
=> true
irb(main):002> require 'ostruct'
.../9.4.6.0/gems/ostruct-0.6.0/lib/ostruct.rb:478: warning: OpenStruct#public_send accesses caller method's state and should not be aliased
.../9.4.6.0/gems/ostruct-0.6.0/lib/ostruct.rb:478: warning: OpenStruct#method accesses caller method's state and should not be aliased
=> true

This seems to be essentially the same issue as #30 (and related to #40), just with more methods now generating these warnings.

(Apparently this warning started to be emitted for method and public_send as a side effect of the implementation of refinements in JRuby 9.4.0.0 in jruby/jruby@dbf10a1 and jruby/jruby@3a76d29.)

Neither public_send! nor method! seem to be used anywhere in the actual OpenStruct code, so simply adding them to the JRuby exclusion list at https://github.com/ruby/ostruct/blob/v0.6.0/lib/ostruct.rb#L473 should be OK.

However, method! is use in one test at https://github.com/ruby/ostruct/blob/v0.6.0/test/ostruct/test_ostruct.rb#L281. That test might need to be skipped on JRuby, unless it can be rewritten not to call method!.

ERROR: Could not find a valid gem 'ostruct' (>= 0) in any repository

I'm a newbie to Ruby, could you help troubleshoot the following error?

eimert@EIM ~ $ sudo gem install ostruct
ERROR:  Could not find a valid gem 'ostruct' (>= 0) in any repository
ERROR:  Possible alternatives: astruct, cstruct, d_struct, dstruct, hstruct```

Whilst I can install other gems, it seems the ostruct gem isn't found. Is it in a specific repo?

Any answer is greatly appreciated.

OpenStruct aliases critical builtin methods to "!" versions

JRuby warns whenever a core method that accesses the caller's frame gets aliased, since we use the name of the method to detect that a frame should be pushed.

However current OpenStruct versions are aliasing several critical builtin methods that it should not, resulting in warnings on JRuby:

$ jruby -we 'require "ostruct"'
/Users/headius/projects/jruby/lib/ruby/stdlib/ostruct.rb:446: warning: OpenStruct#define_singleton_method accesses caller method's state and should not be aliased
/Users/headius/projects/jruby/lib/ruby/stdlib/ostruct.rb:446: warning: OpenStruct#!~ accesses caller method's state and should not be aliased
/Users/headius/projects/jruby/lib/ruby/stdlib/ostruct.rb:446: warning: OpenStruct#=~ accesses caller method's state and should not be aliased
/Users/headius/projects/jruby/lib/ruby/stdlib/ostruct.rb:446: warning: OpenStruct#instance_exec accesses caller method's state and should not be aliased
/Users/headius/projects/jruby/lib/ruby/stdlib/ostruct.rb:446: warning: OpenStruct#instance_eval accesses caller method's state and should not be aliased

We warn because aliasing very frequently involves wrapping the original method with a new pure-Ruby method, breaking the framing requirements of the aliased original.

  • define_singleton_method accesses the caller frame for visibility.
  • !~ and =~ access the caller frame for the backref frame-local $~.
  • instance_exec and instance_eval capture the caller frame for use as a parent frame for the eval.

I believe OpenStruct should not be aliasing these methods.

Breaking change in ostruct 0.3.0

Hi,

just want to report that commit 9cd8895 introduced a breaking change with the following use case:

class MyClass < OpenStruct
  def initialize(hash)
    super({})

    hash.each do |key, value|
      new_ostruct_member!(key)
      self[key] = value
    end
  end
end

puts MyClass.new('id' => 10).id

Ruby 2.7.4 output: 10
Ruby 3.0.2 output:

Included module method are overridden

In ostruct 0.3.2, we're seeing some surprising behavior for subclasses that include methods from other modules. Unlike ostruct 0.2, the included methods are now overridden with the ostruct accessors:

require 'ostruct'

module Bar
  def baz
    'from bar'
  end
end

class Foo < OpenStruct
  include Bar
end

Foo.new(baz: 'a').baz

# in ostruct 0.2.0:
# => 'from bar'

# in ostruct 0.3.2:
# => 'a'

But if the method is defined on the class itself, the method is not overridden:

class Quux < OpenStruct
  def baz
    'from quux'
  end
end

Quux.new(baz: 'a').baz

# in ostruct 0.2.0:
# => 'from quux'

# in ostruct 0.3.2:
# => 'from quux'

We're looking into some possible work-arounds for our usage, but I wanted to confirm that new behavior is working as expected. Thanks.

Incorrect deserialization of some instances serialized by version 0.2.0

In the ruby 2 version of OpenStruct, serialized objects can have two root keys: table and modifiable. The second key only seems to appear after an attribute is assigned post-initialization:

irb(main):012:0> OpenStruct::VERSION
=> "0.2.0"
irb(main):013:0> RUBY_VERSION
=> "2.7.5"
irb(main):014:0> s = OpenStruct.new(foo: 1, bar: 'baz')
=> #<OpenStruct foo=1, bar="baz">
irb(main):015:0> Psych.dump s
=> "--- !ruby/object:OpenStruct\ntable:\n  :foo: 1\n  :bar: baz\n"
irb(main):017:0> s.bar = 'baz'
=> "baz"
irb(main):019:0> Psych.dump s
=> "--- !ruby/object:OpenStruct\ntable:\n  :foo: 1\n  :bar: baz\nmodifiable: true\n"

On newer versions of OpenStruct, serialized objects with that key are not deserialized as expected:

irb(main):010:0> OpenStruct::VERSION
=> "0.3.1"
irb(main):011:0> RUBY_VERSION
=> "3.0.5"
irb(main):012:0> s = Psych.load "--- !ruby/object:OpenStruct\ntable:\n  :foo: 1\n  :bar: baz\nmodifiable: true\n"
=> #<OpenStruct table={:foo=>1, :bar=>"baz"}, modifiable=true>
irb(main):013:0> s.foo
=> nil

The deserialization hook provided by OpenStruct attempts to be backwards compatible, but incorrectly assumes that legacy documents have exactly one root-level key table:
https://github.com/ruby/ostruct/blob/master/lib/ostruct.rb#L449-L454

Marshal load and dump error for `OpenStruct`

person = OpenStruct.new
person.name = 'john smith'
person.age = 70
Marshal.load ( Marshal.dump(person))

NoMethodError: undefined method key?' for nil:NilClass from F:/windows/scoop/persist/ruby/gems/gems/ostruct-0.3.0/lib/ostruct.rb:226:in new_ostruct_member!'

#<NoMethodError: undefined method `encode' for OpenStruct:Class>

I am getting the below error when trying to parse a json object into class object.

#<NoMethodError: undefined method `encode' for OpenStruct:Class>

Code I am using:

# json_string = {"latitude":409146138,"longitude":124678384}
object = JSON.parse(json_string, object_class: OpenStruct)

Target Object looks like this:
<Routeguide::Point: latitude: 409146138, longitude: -746188906>

Could we simplify OpenStruct to not define any methods, just method_missing + Hash and < BasicObject?

See https://bugs.ruby-lang.org/issues/19424#note-11
The idea comes from oracle/truffleruby#2702 (comment)
I think this would be a nice simplification, and would be more intuitive performance-wise.

Defining methods dynamically is extremely expensive (also even just forcing a singleton class is expensive as well), even more so on more optimizing Ruby implementations.
Slightly slower for all access but no hidden costs on defining new members/new instance seems better.

Any thought on that?
I should be able to provide a POC PR, just asking if there is known compatibility issue with that approach.

Usage of Ruby's Safe Navigation Operator (&.) is causing issues when using Ruby versions < 2.3.0

The respond_to_missing function in lib/ostruct.rb is causing issues when using versions of Ruby < 2.3.0

def respond_to_missing?(mid, include_private = false) # :nodoc:
    mname = mid.to_s.chomp("=").to_sym
    @table&.key?(mname) || super
end

Line 196 uses the &. (Safe Navigation Operator) which was introduced in Ruby 2.3.0.

If you want to use the Safe Navigation Operator then please specify the required_ruby_version property in the ostruct.gemspec file

Ruby 2.6.8 ships a version of ostruct that does not exist

This is another case of CRuby's stdlib being updated without an according gem release.

Ruby 2.6.8 as tagged includes a gemspec for ostruct claiming version 0.1.0. However the ostruct.rb alongside it has the following diff:

$ diff -U 5 lib/ostruct.rb ../jruby/lib/ruby/stdlib/ostruct.rb 
--- lib/ostruct.rb	2021-09-01 12:37:44.000000000 -0500
+++ ../jruby/lib/ruby/stdlib/ostruct.rb	2021-09-15 11:34:53.000000000 -0500
@@ -103,32 +103,19 @@
     super
     @table = @table.dup
   end
 
   #
-  # call-seq:
-  #   ostruct.to_h                        -> hash
-  #   ostruct.to_h {|name, value| block } -> hash
-  #
   # Converts the OpenStruct to a hash with keys representing
   # each attribute (as symbols) and their corresponding values.
   #
-  # If a block is given, the results of the block on each pair of
-  # the receiver will be used as pairs.
-  #
   #   require "ostruct"
   #   data = OpenStruct.new("country" => "Australia", :capital => "Canberra")
   #   data.to_h   # => {:country => "Australia", :capital => "Canberra" }
-  #   data.to_h {|name, value| [name.to_s, value.upcase] }
-  #               # => {"country" => "AUSTRALIA", "capital" => "CANBERRA" }
   #
-  def to_h(&block)
-    if block_given?
-      @table.to_h(&block)
-    else
-      @table.dup
-    end
+  def to_h
+    @table.dup
   end
 
   #
   # :call-seq:
   #   ostruct.each_pair {|name, value| block }  -> ostruct

Changes made to gem-based libraries must be released as new versions of the gem before they are shipped with a release of CRuby.

As a result of this change, there is no way that JRuby can ship the exact same ostruct as Ruby 2.6.8, because the code in Ruby 2.6.8's ostruct does not correspond to any released version of ostruct.

CRuby master reports 0.2.0 but seems newer

ruby -v -rostruct -e 'p OpenStruct::VERSION'
ruby 3.0.0dev (2020-10-23T06:26:51Z master f754b42285) [x86_64-linux]
"0.2.0"

Yet the version of OpenStruct in CRuby master seems more recent than 0.2.0, notably it uses the new variant for YAML.dump.

I guess CRuby master is missing some fixes from this repo?
I think CRuby master should use a release of OpenStruct (probably the latest), and not something in between.
cc @marcandre

With bundler, an OpenStruct can't have a field called 'gem' (and it's really hard to troubleshoot -- redefine_method)

After many hours of head-scratching, I can offer this bug report:

OpenStruct.new(gem: 'foobar').gem

The above code works in "bare" ruby, but not with bundler.

Steps to reproduce:

$ mkdir ostruct-bug

$ cd ostruct-bug

$ echo "source 'https://rubygems.org'" > Gemfile

$ bundle install
The Gemfile specifies no dependencies
Resolving dependencies...
Bundle complete! 0 Gemfile dependencies, 1 gem now installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.

$ ruby -v
ruby 2.7.2p137 (2020-10-01 revision 5445e04352) [x86_64-darwin20]

$ bundle -v
Bundler version 2.1.4

$ ruby -e "require 'ostruct' ; puts OpenStruct.new(gem: 'foobar').gem"
foobar

$ bundle exec ruby -e "require 'ostruct' ; puts OpenStruct.new(gem: 'foobar').gem"
Traceback (most recent call last):
	1: from -e:1:in `<main>'
/Users/ikatz/.rbenv/versions/2.7.2/lib/ruby/2.7.0/bundler/rubygems_integration.rb:316:in `block (2 levels) in replace_gem': wrong number of arguments (given 0, expected 1+) (ArgumentError)

$ bundle exec ruby -e "require 'ostruct' ; puts OpenStruct.new(germ: 'foobar').germ"
foobar

Whether or not this is a problem with bundler (vs ostruct), the error is very unhelpful -- gem seems to all appearances like a perfectly valid attribute name and jumping into bundler source is generally not my first thought when debugging my own project.

The relevant bundler code is here:
https://github.com/rubygems/bundler/blob/master/lib/bundler/rubygems_integration.rb#L316

It would seem that the correct behavior in ostruct would be to check whether a method name is available (possibly with self.respond_to?(new_attribute_name) at the time the attribute is being defined , so that it can produce a warning or exception immediately instead of cryptic behavior when the attribute is later accessed.

OpenStruct#respond_to? when true fails with NameError

Hello,

I have a case where I overrode respond_to? on a subclass of OpenStruct because I this class to work as fake object.

class Dep
  def a
    "Hello from Dep"
  end
end
class FakeDep < OpenStruct
  def respond_to?(*args)
    super || Dep.new.respond_to?(*args)
  end  
end

FakeDep.new(a: [])  #=> NameError: undefined method `a' for class `FakeDep'

      owner = method!(name).owner
              ^^^^^^^
from /usr/local/lib/ruby/3.1.0/ostruct.rb:245:in `method'

I didn't fully understand ruby/ruby@f48edc2 and what happens with owner = method!(name).owner. Is the error above a valid case?

TIA

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.