1

Using ruby and sinatra, I need a children's learning dictionary where kids can pick words they want to use, be able to add and delete words, and have multiple definitions for each word that , again, they can add, update and delete.

specifically it is the multiple definitions that is tripping me up.

I tried using a Word class with initialization that has three definition parameters that are initially set to nill so it doesn't necessarily need three (or even one) definition to start.

class Word
  @@words = []
  attr_accessor(:word, :definition)
  def initialize(word,definition1=nil, definition2=nil, 
   definition3=nil)
    @word = word
    @definition1 = definition1
    @definition2 = definition2
    @definition3 = definition3
  end
  def save
    @@words.push(self)
  end
  def self.all
    @@words
  end
  def self.clear
    @@words = []
  end
  def self.add(word, definition1=nil, definition2=nil, 
  definition3=nil)
    @@words.push(Word.new(word,definition1=nil, definition2=nil, 
   definition3=nil))
  end
end


param = Word.new("this", "used to identify a specific person or thing close at hand or being indicated or experienced", "referring to a specific thing or situation just mentioned.")

return param

# <Word:0x0000563bf1b5ba70 @word="this", @definition1=nil, @definition2=nil, @definition3=nil>
ian
  • 12,003
  • 9
  • 51
  • 107
alexanserg
  • 25
  • 4

2 Answers2

2

I suggest you write your class as follows.

class Words
  @words = {}
  # @words is a class instance variable, generally a better choice than a class
  # variable because it cannot be seen from subclasses. It is a hash, with keys
  # being words and values being the associated instances of this class.

  singleton_class.public_send(:attr_reader, :words)
  # This creates a read accessor for the class instance variable @words.

  attr_reader :definitions
  # this creates a read accessor for @definitions, which returns an array
  # of one or more definitions that have been given for the word.

  def initialize(word)
    @definitions = []
    self.class.words[word] = self
    # self.class => Words, Word.words is the hash of words and their instances.
    # The key-value pair `word=>self` is being added to that hash.
  end

  def add_definition(definition)
    @definitions << definition
  end

  def self.remove_definition(word)
    words.delete(word)
  end
  # This is a class method used to remove words from the dictionary.

end

Let's try it.

word = Words.new("bridle")
  #=> #<Words:0x00005d1850124740 @word="bridle", @definitions=[]> 
word.add_definition "the headgear used to control a horse."
word.add_definition "show one's resentment or anger."
word.definitions
  #=> ["the headgear used to control a horse.",
  #    "show one's resentment or anger."] 

Words.words
  #=> {"bridle"=>#<Words:0x000059addb793410 @definitions=[
  #      "the headgear used to control a horse.",
  #      "show one's resentment or anger."]>}

word = Words.new("cheesy")
  #=> #<Words:0x00005d18501a8dd8 @word="cheesy", @definitions=[]> 
word.add_definition "like cheese in taste, smell, or consistency."
word.add_definition "cheap, unpleasant, or blatantly inauthentic."
word.definitions
  #=> ["like cheese in taste, smell, or consistency.",
  #    "cheap, unpleasant, or blatantly inauthentic."] 

Words.words
  #=> {"bridle"=>#<Words:0x000059addb793410 @definitions=[
  #      "the headgear used to control a horse.",
  #      "show one's resentment or anger."]>,
  #      "cheesy"=>#<Words:0x000059addb527be0 @definitions=[
  #        "like cheese in taste, smell, or consistency.",
  #        "cheap, unpleasant, or blatantly inauthentic."]>} 

Words.words.keys
  #=> ["bridle", "cheesy"]
Words.words["cheesy"].definitions
  #=> ["like cheese in taste, smell, or consistency.",
  #    "cheap, unpleasant, or blatantly inauthentic."]  
Words.remove_definition("bridle")
Words.words
  #=> {"cheesy"=>#<Words:0x000059addb527be0 @definitions=[
  #      "like cheese in taste, smell, or consistency.",
  #      "cheap, unpleasant, or blatantly inauthentic."]>} 

As an exercise I suggest you add instance methods remove_definition and change_definition, each with an argument that is an index position in the array of definitions for the given word.

Cary Swoveland
  • 106,649
  • 6
  • 63
  • 100
  • @@words = [] @@words needs to by a class variable because it is a stand in for a database of multiple word objects which we haven't gotten to yet. We are tackling databases next week – alexanserg Jul 28 '19 at 04:27
  • OK, I'll leave that adjustment to you. (I don't think I've ever used a class variable.) – Cary Swoveland Jul 28 '19 at 04:43
0

As an addendum to the comments to Cary Swoveland's answer, even if the @@words class variable stands in for a database it can still be represented by a class instance variable (and should, for good reasons).

I'd use a straightforward class instance variable declaration:

class Words
  class << self
    def words
      @words ||= []
    end
  end
end

When it comes to substituting in the database:

class Words
  class << self
    def words
      @words ||= DB.words # or whatever
    end
  end
end

However, if you're going to be using a database then why create the Words class at all? Use a database model and make that the Words class. For instance, using Sequel's object model:

Sequel.migration do
  up do
    create_table :words do
      primary_key :id
      String :word, null: false
    end

    create_table :definitions do
      primary_key :id
      String :definition, null: false
      foreign_key :word_id, :words, :null=>false
    end
  end

  down do
    drop_table :definitions, :words
  end
end

# run the migrations then…

class Words < Sequel::Model
  one_to_many :definitions
end

class Definitions < Sequel::Model
  many_to_one :words
end

cheesy = Word.create "cheesy"
cheesy.add_definition "like cheese in taste, smell, or consistency."
cheesy.add_definition "cheap, unpleasant, or blatantly inauthentic."
cheesy.save # <- probably not needed here but why not be sure?

cheesy.definitions # etc

Something like that (I haven't run this code!), which you would then go on to extend by adding a users table and model, and decide whether users would have their own lists of words or share the list etc…

In short, don't create a facade to an interface if you're going to just use the original interface anyway!

ian
  • 12,003
  • 9
  • 51
  • 107