THS
THS

Reputation: 11

Ruby object factory loop seems to overwrite some instances, not all

I'm struggling with a ruby loop. I have a set of 102 items pulled from a website captured in strings. In those strings are listed characteristics about that item. I want to parse the string and put some of the item characteristics into a hash. Each hash is meant to be an instance of my Item Class. The code below kinda works (meaning it doesn't), in that it does create SOME Item class objects in the manner expected. But it doesn't capture all 102. Instead it seems to capture only a subset. I think it is overwriting some earlier iterations. I'm stumped. I'm a novice and this is the hardest project I've taken on, so all advice welcome and appreciated!

Here's my class definition, kept in a separate file pulled in by require_relative:

class Item
    attr_accessor :char1, :char2, :char4, :char6, :char8, :char10, :char11 
    def initialize(args)
        @char2  = args[:char2]
        @char1  = args[:char1]
        @char4  = args[:char4]
        @char6  = args[:char6] || 4
        @char8  = args[:char8]
        @char10 = args[:char10]
end

Running my script file in IRB, I get:

2.6.3 :028 > @item_cards.count
 => 102
2.6.3 :030 > @item_cards.first.text
 => "42\Characteristic1\Characteristic2\Characteristic3\Characteristic4\Characteristic1\Characteristic5\Characteristic6\Characteristic7\Characteristic8\Characteristic9\Characteristic10\"

Then I parse that string for the characteristics I care about, put them into an array, and then try use that array to build an object in my Item class.

def factory 
     @item_cards.each do |x|  
            s = x.text
            arry = {
            :char2 => s.split(%'\')[2].strip,
            :char1 => s.split(%'\')[1].strip,
            :char4 => s.split(%'\')[4].strip,
            :char6 => s.split(%'\')[6].strip,
            :char8 => s.split(%'\')[8].strip,
            :char10 => s.split(%'\')[10].strip,
                }   

     @item = Item.new(char2: arry[:char2], char1: arry[:char1], char4: 
     arry[:char4], char6: arry[:char6], sqft: char8[:char8], char10: 
     arry[:char10])

            puts "** here is the new Item CLASS object #{@item.inspect}" 
            puts "*** there are #{Item.count} Item Objects now"
            puts "*** Against this many @item_cards #{@item_cards.count}"
            puts "*** end of this iteration of loop ***"
     end

    puts "********** OUT OF THE LOOP NOW **********" 
    puts "There are this many Item objects: #{Item.all.count}"

end

During each iteration of the loop, it looks to me that (1) the text is parsing correctly, and (2) that data is properly captured in the @item variable. However, sometimes the #{Item.count} ticker doesn't increase. E.g., it will create the third Item object with the characteristics of the that item, but then seems to overwrite the third Item object with the fourth. Yet other Item objects are created correctly. The final result is a smaller number of Item objects than would be suggested by the number of @item_cards originally captured (i.e, less than 102).

When I run the factory command in IRB, this is what I get at the end, having skipped or overwritten 30 items by the time it gets to the end:

 here is the initial array {#parsing correctly for item 71}
*** And here is the New Item CLASS object #<Item:0x00007f96c25162c8 @char2="Item 71's Char2", @char1="Item 71's Char1", "#and so on...">
*** there are 71 Item Objects now
*** Against this many @item_cards 102
*** end of this iteration of loop ***
Return value is: nil
 here is the initial array {#parsing correctly for item 72}
*** And here is the New Item CLASS object #<Item:0x00007f96c454a6c0 @char2="Item 72's Char 2", @char1="Item 72's Char 1", #and so on...>
*** there are 71 Item Objects now
*** Against this many @item_cards 102
*** end of this iteration of loop ***
Return value is: nil

#then process repeats, working correctly to create new Item object.

*** there are 72 Item Objects now
*** Against this many @item_cards 102
*** end of this iteration of loop ***
Return value is: nil

********** OUT OF THE LOOP NOW **********

There are this many Item objects: 72
Return value is: nil

I hope that is clear, if not concise. Very much a novice so all input/correction is welcome.

Upvotes: 1

Views: 117

Answers (2)

Aleksei Matiushkin
Aleksei Matiushkin

Reputation: 121000

Array#each just iterates the collection. You, on contrary, need to map the input to some data, returned by your parsing. That said, something like the below would work.

def factory 
  @items = # an array of items!
    @item_cards.map do |x|  
      ...
      Item.new(...) # should be the last line in a loop
    end
  # @items contains the array of `Item` objects now
end

Upvotes: 0

Mark
Mark

Reputation: 6445

Your issue is you're only initializing a new Item object with certain attributes, you're not saving it to the database. Every time you do @item = Item.new , you're overwriting the previous version of @item. Changing new to create should solve your issue, assuming your Item passes validation:

def factory 
  @item_cards.each do |x|  
    s = x.text
    arry = {
      :char2 => s.split(%'\')[2].strip,
      :char1 => s.split(%'\')[1].strip,
      :char4 => s.split(%'\')[4].strip,
      :char6 => s.split(%'\')[6].strip,
      :char8 => s.split(%'\')[8].strip,
      :char10 => s.split(%'\')[10].strip,
    }   

    @item = Item.create(                         # <-- Change this bit here
      char2: arry[:char2],
      char1: arry[:char1],
      char4: arry[:char4],
      char6: arry[:char6],
      sqft: char8[:char8],
      char10: arry[:char10]
    )

    puts "** here is the new Item CLASS object #{@item.inspect}" 
    puts "*** there are #{Item.count} Item Objects now"
    puts "*** Against this many @item_cards #{@item_cards.count}"
  end

  puts "********** OUT OF THE LOOP NOW **********" 
  puts "There are this many Item objects: #{Item.all.count}"
end

Upvotes: -1

Related Questions