ruby-hyperloop / hyper-react

The project has moved to Hyperstack!!
https://hyperstack.org/
MIT License
285 stars 14 forks source link

add `to_key` method to object #244

Open catmando opened 6 years ago

catmando commented 6 years ago

Components can be provided a key, via the special key parameter that ensures when rendering lists of components the ordering does not change, and speeds up the rendering of the list.

...
  UL do
    MyModel.each do |record|
       DisplayRecord(record: record, key: record.id)
    end
  end
...

typically you can use some data associated with the values being displayed (like record.id above) or the ruby object_id, or some other unique identifier depending on the contents of the list item.

For HyperModels (and probably other places too) this can be difficult. The above example for instance only works if all the records are saved (i.e. have ids). You can't use record.object_id since there is no guarantee that on the next rendering the record instance will be the same object_id, so you have use record.backing_record.object_id which is gross and relies on knowing the inards of HyperModel.

The solution is to add a to_key method to the base ruby Object class and to have the key parameter automatically have to_key applied to it. The default to_key method will return self.object_id, but this can be overwritten as needed. I.e. ActiveRecord::Base can override as self.backing_record.object_id.

Now we can write DisplayRecord(record: record, key: record) shorter, sweeter, and works.

Also in keeping with POLS, strings, symbols, booleans and number objects will return themselves.

class Object
  def to_key
    object_id
  end
end

class String
  def to_key
    self
  end
end

class Number
  def to_key
    self
  end
end

class Boolean
  def to_key
    self
  end
end

class Foo
end

class Bar
  def self.new_bar_counter
    @bar_counter = (@bar_counter && @bar_counter+1) || 0
  end
  def initialize
    @to_key = "Bar ##{Bar.new_bar_counter}"
  end
  attr_reader :to_key
end

puts Object.new.to_key
puts "hello".to_key
puts 12.to_key
puts true.to_key
puts Foo.new.to_key
puts Bar.new.to_key
puts Bar.new.to_key

Produces

5206
hello
12
true
5208
Bar #0
Bar #1

run it!

As well as adding the method to the classes per above, you also have to add these lines here

        elsif key == "key"
          props["key"] = value.to_key
       ...
sfcgeorge commented 6 years ago

This is nice. The way it's laid out is also backwards compatible because record.id or record.object_id in existing code returns a number, and Number#to_key == self.

Donnadieu commented 6 years ago

Hi, I think I can help with this issue.