Freeze arrays and hashes by default? - ruby

Just wondering if something like:
# frozen_string_literal: true
exists but for Array and Hash?
The goal is not having to .freeze every single of those within the same globals file.

I didn't find any library that monkey patches default ruby classes like Array or Hash. But I found an interesting gem immutable-ruby that may fit your needs
Simple example
require "immutable/hash"
person = Immutable::Hash[name: "Simon", gender: :male]
# => Immutable::Hash[:name => "Simon", :gender => :male]
and you cannot just modify values of it, cause it is immutable. You can perform some actions on that hash, but new copy will be returned to you
friend = person.put(:name, "James") # => Immutable::Hash[:name => "James", :gender => :male]
person # => Immutable::Hash[:name => "Simon", :gender => :male]
friend[:name] # => "James"
person[:name] # => "Simon"

Found a way to handle it without using another gem using only vscode and rubocop :
Install the rubocop extension on vscode
Open your .vscode/settings.json
Append those rules :
{
"editor.formatOnSave": true,
"editor.formatOnSaveTimeout": 5000,
"ruby.format": "rubocop"
}
save
enjoy
Thanks to Tom Lord for the hint.

Related

In which version of ruby appeared ':' instead '=>'?

I mean
some: true
vs
:some => true
I have problem with compatibility my Rails version and Ruby version and I have to know in which version appeared only : instead =>.
I don't know how to find this kind of info by Google.
This is a feature introduced into Ruby 1.9:
{ example: 'key' }
# => { :example => 'key' }
This is similar to how JavaScript and other languages define their dictionary-type structures. The keys generated this way are always Symbol-type.
It's also possible to mix and match:
variable = :foo
{ example: 'key', 'string' => 'stored', variable => 'thing' }
# => {:example=>"key", "string"=>"stored", :foo=>"thing"}
This is a good thing because the x: approach is more limited. If you want dots in your keys, for example, you'll need to use the older style.

Annotating Ruby structures to include anchors/references on #to_yaml

I have some large hashes (>10⁵ keys) with interlocking structures. They're stored on disk as YAML. I'd like to avoid duplication by using anchors and references in the YAML, but I haven't been able to figure out if there's a way to do it implicitly in the hash such that the #to_yaml method will label the anchor nodes properly.
Desired YAML:
---
parent1:
common-element-1: &CE1
complex-structure-goes: here
parent2:
uncomment-element-1:
blah: blah
<<: *CE1
Ruby code:
hsh = {
'parent1' => {
'common-element-1' => {
'complex-structure-goes' => 'here',
},
'parent2' => {
'uncommon-element-1' => {
'blah' => 'blah',
},
'<<' => '*CE1',
},
}
The reference is quite straightforward -- but how to embed the &CE1 anchor in the 'common-element-1' item in the Ruby hash?
I want to work as much as possible with native Ruby primitive types (like Hash) rather than mucking about with builders and emitters and such -- and I definitely don't want to write the YAML manually!
I've looked at Read and write YAML files without destroying anchors and aliases? and its relative, among other places, but haven't found an answer yet -- at least not that I've understood.
Thanks!
If you use the same Ruby object, the YAML library will set up references for you:
> common = {"ohai" => "I am common"}
> doc = {"parent1" => {"id" => 1, "stuff" => common}, "parent2" => {"id" => 2, "stuff" => common}}
> puts doc.to_yaml
---
parent1:
id: 1
stuff: &70133422893680
ohai: I am common
parent2:
id: 2
stuff: *70133422893680
I'm not sure there's a straightforward way of defining Hashes that are subsets of each other, though. Perhaps tweaking your structure a bit would be warranted?

“can't convert Symbol into Integer” weird error

The is the hash I am working on,
a = {
#...
:fares => {
:itinerary_fare => {
:segment_names=>"C",
:free_seats => "6",
:fare_for_one_passenger => {
:free_seats=>"0",
:#currency => "TL",
:#non_refundable => "false",
:#price => "439.0",
:#service_fee => "25.0",
:#tax => "33.0",
:#type => "Y"
},
:#currency => "TL",
:#non_refundable => "false",
:#price => "439.0",
:#service_fee => "25.0",
:#tax => "33.0",
:#type => "C"
},
:#currency => "TL",
:#tax => "33.0"
},
#..
}
also here another example http://pastebin.com/ukTu8GaG.
The code that gives me headhaches,
a[:fares][:itinerary_fare].each do |f|
puts f[:#price]
end
If I write this into console, it gives me "can't convert Symbol into Integer" error. But if I write, a[:fares][:itinerary_fare][:#price] it works pretty fine.
The weirdest part is, if I write the code to a haml file
%tbody
-#flights.each do |a|
%tr.flight
%td
-a[:fares][:itinerary_fare].each do |f|
-puts f[:#price] #Weird stuff happens here
.prices
%input{:type=>"radio",:name=>"selectedfight",:value=>"#{a[:id]}"}
= f[:#price]
%br
It works, it prints the prices to my console, yet it fails at the SAME LINE.
can't convert Symbol into Integer file: flights.haml location: [] line: 18
This is the most disturbing error I have ever seen, thanks for any help.
Most of the time there are more than 1 :itinerary_fare, I have to iterate.
My data can be shown as http://postimage.org/image/6nnbk9l35/
a[:fares][:itinerary_fare] is a Hash. Hash#each yields key-value-pair arrays to the block.
So, f takes e.g. the array [:#price, "439.0"] in the block.
Hence you are using a symbol (:#price) as an array index. An integer is expected.
In a[:fares][:itinerary_fare][:#price] you are giving it as hash key which works of course.
Why did you delete the previous question, that was already correctly answered? You could only just updated it with more information.
As was answered by other user in your previous post you are iterating over the elements in a[:fares][:itinerary_fare]. You can see this with:
a[:fares][:itinerary_fare].each do |f|
puts f
end
And you dont need a loop, you can use:
a[:fares][:itinerary_fare][:#price]
If you have more than one :itinerary_fare it will only consider the last one, since it's a key of the hash :fares. Maybe you need an array like (left to minimal of elements):
a = {:id=>"1",
:fares=>{
:itinerary_fares=>[{:#price=>"439"}, {:#price=>"1000"}]
}
}
and then:
a[:fares][:itinerary_fares].each do |f|
puts f[:#price]
end

With Mongoid, can I "update_all" to push a value onto an array field for multiple entries at once?

Using Mongoid, is it possible to use "update_all" to push a value onto an array field for all entries matching a certain criteria?
Example:
class Foo
field :username
field :bar, :type => Array
def update_all_bars
array_of_names = ['foo','bar','baz']
Foo.any_in(username: foo).each do |f|
f.push(:bar,'my_new_val')
end
end
end
I'm wondering if there's a way to update all the users at once (to push the value 'my_new_val' onto the "foo" field for each matching entry) using "update_all" (or something similar) instead of looping through them to update them one at a time. I've tried everything I can think of and so far no luck.
Thanks
You need call that from the Mongo DB Driver. You can do :
Foo.collection.update(
Foo.any_in(username:foo).selector,
{'$push' => {bar: 'my_new_val'}},
{:multi => true}
)
Or
Foo.collection.update(
{'$in' => {username: foo}},
{'$push' => {bar: 'my_new_val'}},
{:multi => true}
)
You can do a pull_request or a feature request if you want that in Mongoid builtin.

Override id on a ruby object (created using OpenStruct)

I want to convert a hash to an object using OpenStruct that has an id property, however the resultant object#id returns the native object id, e.g.
test = OpenStruct.new({:id => 666})
test.id # => 70262018230400
Is there anyway to override this? As at the moment my workaround isn't so pretty.
OpenStruct uses a combination of define_method calls inside an unless self.respond_to?(name) check and method_missing. This means if the property name conflicts with the name of any existing method on the object then you will encounter this problem.
tokland's answer if good but another alternative is to undefine the id method e.g.
test.instance_eval('undef id')
You could also incorporate this into your own customised version of OpenStruct e.g.
class OpenStruct2 < OpenStruct
undef id
end
irb(main):009:0> test2 = OpenStruct2.new({:id => 666})
=> #<OpenStruct2 id=666>
irb(main):010:0> test2.id
=> 666
This was the classical workaround, I'd be also glad to hear a better way:
>> OpenStruct.send(:define_method, :id) { #table[:id] }
=> #<Proc:0x00007fbd43798990#(irb):1>
>> OpenStruct.new(:id => 666).id
=> 666
I've switched to using Hashery and the BasicStruct (renamed version of OpenObject in latest version, 1.4) as that allows me to do this:
x = BasicStruct.new({:id => 666, :sub => BasicStruct.new({:foo => 'bar', :id => 777})})
x.id # => 666
x.sub.id # => 777
x.sub.foo # => "bar"

Resources