Ruby: Syntax for defining a constant inside a Struct
This happens because the constant is defined in the current namespace. The class
and module
keywords define namespaces, but Struct.new
(just like Class.new
) does not.
In order to define the constant under the Struct's scope, you have to use self::
class Outer
Inner = Struct.new(:dummy) do
self::CONST = 'abce'
end
end
Outer::Inner::CONST
#=> 'abce'
Outer::CONST
#=> NameError uninitialized constant Outer::CONST
Using structs in Ruby on Rails gives dynamic constant assignment (SyntaxError)
The error explains what the problem is - you have a constant being assigned in a context that's too dynamic - i.e. inside the index method.
The solution is to define it outside:
DashItem = Struct.new(:name, :amount, :moderated)
def index
@dashboard_items = []
...
Defining a Struct during initialize
You don't have to use a constant. Use an instance variable.
class SomeClass
def initialize(csvfile)
@csv = CSV.open(csvfile, options...)
...
headers = @csv.headers
@record = Struct.new(headers)
load_data
end
def load_data
@records = []
@csv.each do |r|
@records << @record.new(r.fields)
end
end
end
Ruby accessing constants from inner-class
What am I doing wrong?
You're trying to refer a constant from a wrong context. Constants are defined in class objects, not in instances. This works:
human = Mammal::Human.new
human.class.const_get(:H) # => "Human"
Dynamic constant assignment main.rb:6: Ruby
When you define a Capitalized variable in Ruby, that is a constant - it is a special kind of variable that is not allowed to change value (well, technically you can change it with const_set
, but that's not really relevant here).
Because of this limitation, Ruby won't allow you to change constants from within functions. It assumes the function will be called many times, which would cause the constant to change value, which as I just mentioned is illegal.
So, quick fix, just replace your Odd
and Even
with the lowercase versions odd
and even
. That way they're regular variables and not constants.
Dynamic constant assignment
Your problem is that each time you run the method you are assigning a new value to the constant. This is not allowed, as it makes the constant non-constant; even though the contents of the string are the same (for the moment, anyhow), the actual string object itself is different each time the method is called. For example:
def foo
p "bar".object_id
end
foo #=> 15779172
foo #=> 15779112
Perhaps if you explained your use case—why you want to change the value of a constant in a method—we could help you with a better implementation.
Perhaps you'd rather have an instance variable on the class?
class MyClass
class << self
attr_accessor :my_constant
end
def my_method
self.class.my_constant = "blah"
end
end
p MyClass.my_constant #=> nil
MyClass.new.my_method
p MyClass.my_constant #=> "blah"
If you really want to change the value of a constant in a method, and your constant is a String or an Array, you can 'cheat' and use the #replace
method to cause the object to take on a new value without actually changing the object:
class MyClass
BAR = "blah"
def cheat(new_bar)
BAR.replace new_bar
end
end
p MyClass::BAR #=> "blah"
MyClass.new.cheat "whee"
p MyClass::BAR #=> "whee"
How to define a Ruby Struct which accepts its initialization arguments as a hash?
Cant you just do:
def initialize(hash)
hash.each do |key, value|
send("#{key}=", value)
end
end
UPDATE:
To specify default values you can do:
def initialize(hash)
default_values = {
first_name: ''
}
default_values.merge(hash).each do |key, value|
send("#{key}=", value)
end
end
If you want to specify that given attribute is required, but has no default value you can do:
def initialize(hash)
requried_keys = [:id, :username]
default_values = {
first_name: ''
}
raise 'Required param missing' unless (required_keys - hash.keys).empty?
default_values.merge(hash).each do |key, value|
send("#{key}=", value)
end
end
How to namespace constants inside anonymous class definitions?
When creating anonymous classes through Class.new, they don't seem to have their own namespace for constants
Sure, by the definition of the word “anonymous.” Compare two following snippets:
class C1; puts "|#{name}|"; end
#⇒ |C1|
C2 = Class.new { puts "|#{name}|" }
#⇒ ||
Unless assigned to the constant, the class has no name and hence all constants defined inside go to Object
namespace. That said, the warning here is actually pointing out to error and Object::FOO = "bar"
overrides Object::FOO = "foo"
constant.
That said, one cannot use constants in this scenario. Use class-level instance variables instead, or construct unique constant names manually (I would advise avoiding polluting Object
class with a bunch of unrelated constants, though.)
Ruby on Rails: Where to define global constants?
If your model is really "responsible" for the constants you should stick them there. You can create class methods to access them without creating a new object instance:
class Card < ActiveRecord::Base
def self.colours
['white', 'blue']
end
end
# accessible like this
Card.colours
Alternatively, you can create class variables and an accessor. This is however discouraged as class variables might act kind of surprising with inheritance and in multi-thread environments.
class Card < ActiveRecord::Base
@@colours = ['white', 'blue'].freeze
cattr_reader :colours
end
# accessible the same as above
Card.colours
The two options above allow you to change the returned array on each invocation of the accessor method if required. If you have true a truly unchangeable constant, you can also define it on the model class:
class Card < ActiveRecord::Base
COLOURS = ['white', 'blue'].freeze
end
# accessible as
Card::COLOURS
You could also create global constants which are accessible from everywhere in an initializer like in the following example. This is probably the best place, if your colours are really global and used in more than one model context.
# put this into config/initializers/my_constants.rb
COLOURS = ['white', 'blue'].freeze
# accessible as a top-level constant this time
COLOURS
Note: when we define constants above, often we want to freeze
the array. That prevents other code from later (inadvertently) modifying the array by e.g. adding a new element. Once an object is frozen, it can't be changed anymore.
Related Topics
What's the Difference Between the Ruby Irb Prompt Modes
Routing Error No Route Matches [Get] "/Static_Pages/Home", Tutorial
Rmagick Error After Installing Os X Mavericks
Ruby or Rails Sort on Two/Multiple Date Fields
Rails 3 and PDFkit. How to Specify Page Size
Is There a Groovy Equivalent of the Ruby Timeout Module
Activerecord_Postgis_Adapter: Undefined Method 'Point' for Nil:Nilclass
Rails 7 Signup Form Doesn't Show Error Messages
Ruby on Rails - $ Rails Server Fails Because Uglifier Gem Could Not Be Found
How to Make a Ruby Script Using Trollop for Command Line Parsing
Error Creating Rails Db Using Rake Db:Create
Rails - Multi Tenant Application with Customization Framework
Having Difficulty Accessing Validation Errors in Sinatra
Net-Ssh and Remote Environment
Accessing the Child Instance in a Rabl Template
Exec the Cd Command in a Ruby Script