Hello, I have a problem with my function for my project, I discovered that yesterday because I had new files. To explain, my project actually have 3 directories named with the software name, and each directory contain only one directory with one version name.
But every time, when this function finish his work, Every availableSoftware.versions contains 3 items ??? Why ?
def loadSoftwareDatabase
if !Dir.exists?(ISM::Default::Path::SoftwaresDirectory)
Dir.mkdir(ISM::Default::Path::SoftwaresDirectory)
end
softwareDirectories = Dir.children(ISM::Default::Path::SoftwaresDirectory)
softwareDirectories.each do |softwareDirectory|
versionDirectories = Dir.children(ISM::Default::Path::SoftwaresDirectory+softwareDirectory)
availableSoftware = ISM::AvailableSoftware.new(softwareDirectory)
versionDirectories.each do |versionDirectory|
softwareInformation = ISM::SoftwareInformation.new
#
puts versionDirectories.size
if File.exists?(ISM::Default::Path::SettingsSoftwaresDirectory +
softwareDirectory + "/" +
versionDirectory + "/" +
ISM::Default::Filename::SoftwareSettings)
softwareInformation.loadInformationFile(ISM::Default::Path::SettingsSoftwaresDirectory +
softwareDirectory + "/" +
versionDirectory + "/" +
ISM::Default::Filename::SoftwareSettings)
else
softwareInformation.loadInformationFile(ISM::Default::Path::SoftwaresDirectory +
softwareDirectory + "/" +
versionDirectory + "/" +
ISM::Default::Filename::Information)
end
availableSoftware.versions.push(softwareInformation)
end
@softwares.push(availableSoftware)
end
end
If you need more informations, I done this test, look:
def loadSoftwareDatabase
if !Dir.exists?(ISM::Default::Path::SoftwaresDirectory)
Dir.mkdir(ISM::Default::Path::SoftwaresDirectory)
end
softwareDirectories = Dir.children(ISM::Default::Path::SoftwaresDirectory)
softwareDirectories.each do |softwareDirectory|
versionDirectories = Dir.children(ISM::Default::Path::SoftwaresDirectory+softwareDirectory)
availableSoftware = ISM::AvailableSoftware.new(softwareDirectory)
versionDirectories.each do |versionDirectory|
softwareInformation = ISM::SoftwareInformation.new
if File.exists?(ISM::Default::Path::SettingsSoftwaresDirectory +
softwareDirectory + "/" +
versionDirectory + "/" +
ISM::Default::Filename::SoftwareSettings)
softwareInformation.loadInformationFile(ISM::Default::Path::SettingsSoftwaresDirectory +
softwareDirectory + "/" +
versionDirectory + "/" +
ISM::Default::Filename::SoftwareSettings)
else
softwareInformation.loadInformationFile(ISM::Default::Path::SoftwaresDirectory +
softwareDirectory + "/" +
versionDirectory + "/" +
ISM::Default::Filename::Information)
end
availableSoftware.versions << softwareInformation
end
@softwares << availableSoftware
puts "For this directory:" + softwareDirectory
puts "AvailableS name:" + availableSoftware.name
puts "Versions number:" + availableSoftware.versions.size.to_s
puts "\n"
end
puts "__________________________________"
@softwares.each do |sft|
puts sft.name
puts sft.versions.size.to_s
puts sft.versions.last.name
puts sft.versions.last.description
puts "\n"
end
end
I have this result:
zohran master ~ Documents Programmation ISM crystal Main.cr software search binutils
For this directory:Binutils
AvailableS name:Binutils
Versions number:1
For this directory:Binutils-Pass1
AvailableS name:Binutils-Pass1
Versions number:2
For this directory:Binutils-Pass2
AvailableS name:Binutils-Pass2
Versions number:3
__________________________________
Binutils
3
Binutils-Pass2
The GNU collection of binary tools (Temporary Tools).
Binutils-Pass1
3
Binutils-Pass2
The GNU collection of binary tools (Temporary Tools).
Binutils-Pass2
3
Binutils-Pass2
The GNU collection of binary tools (Temporary Tools).
Completely crazy result.
Honestly I’m lost, I don’t understand why this happened
It looks like versions in your class ISM::AvailableSoftware is not unique to each instance of the class, and you are simply appending everything to the one array. Perhaps provide the code of your class?
I changed the implementation and I done that, this work. But honestly, even I solved my problem, I’m not sure to understand why my old code didn’t work:
def loadSoftwareDatabase
if !Dir.exists?(ISM::Default::Path::SoftwaresDirectory)
Dir.mkdir(ISM::Default::Path::SoftwaresDirectory)
end
softwareDirectories = Dir.children(ISM::Default::Path::SoftwaresDirectory)
softwareDirectories.delete("SoftwaresLibrairies.cr")
softwareDirectories.each do |softwareDirectory|
versionDirectories = Dir.children(ISM::Default::Path::SoftwaresDirectory+softwareDirectory)
softwaresInformations = Array(ISM::SoftwareInformation).new
versionDirectories.each do |versionDirectory|
softwareInformation = ISM::SoftwareInformation.new
if File.exists?(ISM::Default::Path::SettingsSoftwaresDirectory +
softwareDirectory + "/" +
versionDirectory + "/" +
ISM::Default::Filename::SoftwareSettings)
softwareInformation.loadInformationFile(ISM::Default::Path::SettingsSoftwaresDirectory +
softwareDirectory + "/" +
versionDirectory + "/" +
ISM::Default::Filename::SoftwareSettings)
else
softwareInformation.loadInformationFile(ISM::Default::Path::SoftwaresDirectory +
softwareDirectory + "/" +
versionDirectory + "/" +
ISM::Default::Filename::Information)
end
softwaresInformations << softwareInformation
end
@softwares << ISM::AvailableSoftware.new(softwareDirectory,
softwaresInformations)
end
end
My class is very small:
module ISM
class AvailableSoftware
property name = ISM::Default::AvailableSoftware::Name
property versions = ISM::Default::AvailableSoftware::Versions
def initialize( name = ISM::Default::AvailableSoftware::Name,
versions = ISM::Default::AvailableSoftware::Versions)
@name = name
@versions = versions
end
end
end
Are you still adding stuff to @versions in AvailableSoftware somewhere? I suspect that what’s happening is the following:
Each loop you make an instance of AvailableSoftware with a directory but a default @versions. The default, ISM::Default::AvailableSoftware::Versions, is a constant instance of a class, and you’re actually getting a reference to it in each AvailableSoftware, not a copy. So that means that you’re actually adding elements to the default versions collection each loop.
If ISM::Default::AvailableSoftware::Versions is actually a standard library collection (e.g. an Array), you can change the default to ISM::Default::AvailableSoftware::Versions.dup. That should make a copy of the default array without you accidentally adding elements to it.
I’m not sure but, I need to correct with something like that ?
I have always an error:
module ISM
class AvailableSoftware
property name : String
property versions : Array(ISM::SoftwareInformation)
def initialize( name : String,
versions : Array(ISM::SoftwareInformation))
@name = name
@versions = versions
end
end
end
If I do that, I have the same error.
I’m lost. It’s not possible to implement a default module like what I done ?
Try doing like versions = ISM::Default::AvailableSoftware::Versions.dup. But in the end a defaults module isn’t very common. I’d just do versions = [] of ISM::SoftwareInformation and call it a day. Also fwiw you can write your AvailableSoftware type as, which is abit cleaner imo:
class ISM::AvailableSoftware
property name : String
property versions : Array(ISM::SoftwareInformation)
def initialize(@name : String, @versions : Array(ISM::SoftwareInformation)); end
end
Something to keep in mind is constants in Crystal just prevent re-assignment, not immutability. E.g. when you have a constant array, you’re assigning a reference to that singleton array, which is most likely not what you want.
Using a constant to avoid duplicating its value it is a good idea, but maybe try and only do that for simple values, like strings or numbers. If you’re mainly wanting to avoid longer names/duplication of that longer name, it might be better to look into using an alias.
E.g.
alias SoftwareInfo = Array(ISM::SoftwareInformation)
From here you could then use that as both a way to type the argument and initialize an empty array as its default:
def initialize(@versions : SoftwareInfo = SoftwareInfo.new); end
I.e. where the scalar string value is a constant, but you use an alias to centralize the type of the array without having it be a constant such that you do not assign everything the same array.
One other way, is it better to remove all of my Default modules and just do something like that ?
module ISM
class AvailableSoftware
@@DefaultName = ""
@@DefaultVersions = Array(ISM::SoftwareInformation).new
property name = @@DefaultName
property versions : @@DefaultVersion
def initialize( name : @@DefaultName,
versions : @@DefaultVersion)
@name = name
@versions = versions
end
end
end
If I initialize manually properties and I just use constants for default value if the user put nothing, I think it’s fine no ?
Like this :
module ISM
class AvailableSoftware
property name : String
property versions : Array(ISM::SoftwareInformation)
def initialize( name = ISM::Default::AvailableSoftware::Name,
versions = Array(ISM::SoftwareInformation)
@name = name
@versions = versions
end
end
end
EDIT: I think no it’s wrong as well because the compiler while infer the type as well. Okay definitely it’s wrong
Can I do that ?
module ISM
class AvailableSoftware
property name : String
property versions : Array(ISM::SoftwareInformation)
def initialize( @name, @versions)
@name = name
@versions = versions
end
end
end