SlideShare une entreprise Scribd logo
1  sur  84
Ohm
Object hash map   Christopher Spring
A little
background
info first...
Ohm runs on redis
Ohm runs on redis
Remote dictionary server
Ohm runs on redis
Remote dictionary server
Store multiple data-structure types
Ohm runs on redis
Remote dictionary server
Store multiple data-structure types
  Lists, Sets, Sorted Sets, Strings, Hashes
Ohm runs on redis
Remote dictionary server
Store multiple data-structure types
  Lists, Sets, Sorted Sets, Strings, Hashes
In memory datastore with persistence strategies
Ohm runs on redis
Remote dictionary server
Store multiple data-structure types
  Lists, Sets, Sorted Sets, Strings, Hashes
In memory datastore with persistence strategies
Super fast & easy to scale
Ohm runs on redis
Remote dictionary server
Store multiple data-structure types
  Lists, Sets, Sorted Sets, Strings, Hashes
In memory datastore with persistence strategies
Super fast & easy to scale
Atomic operations & transactions
Ohm runs on redis
Remote dictionary server
Store multiple data-structure types
  Lists, Sets, Sorted Sets, Strings, Hashes
In memory datastore with persistence strategies
Super fast & easy to scale
Atomic operations & transactions
Key expiry
Ohm runs on redis
Remote dictionary server
Store multiple data-structure types
  Lists, Sets, Sorted Sets, Strings, Hashes
In memory datastore with persistence strategies
Super fast & easy to scale
Atomic operations & transactions
Key expiry
redis-rb
What is Ohm?
Ancient syllable
Ancient syllable
Aaa-ooo-mmm
Ancient syllable
Aaa-ooo-mmm
The beginning, duration and
dissolution of the universe
Ancient syllable
Aaa-ooo-mmm
The beginning, duration and
dissolution of the universe
Brahma, Vishnu & Shiva
or...
symbol = {
  :name => 'Ohm',
  :description =>   'Ancient syllable',
  :phonemes => [
    {
      :sound   =>   'aaa',
      :meaning =>   'beginning of the universe',
      :god     =>   'Brahma'
    }, {
      :sound   =>   'ooo',
      :meaning =>   'duration of the universe',
      :god     =>   'Vishnu'
    }, {
      :sound   =>   'mmm',
      :meaning =>   'dissolution of the universe',
      :god     =>   'Shiva'
    }
  ]
}
Let’s store that in
redis...
require 'redis'

db = Redis.new
# make a bunch of phonemes
# HMSET key field value [field value ...]

phos = %w(pho1 pho2 pho3)

db.hmset phos[1], 'sound', 'aaa',
                  'meaning', 'beginning',
                  'god', 'Brahma'

db.hmset phos[2], 'sound', 'ooo',
                  'meaning', 'duration',
                  'god', 'Vishnu'

db.hmset phos[3], 'sound', 'mmm',
                  'meaning', 'dissolution',
                  'god', 'Shiva'
# ... and create the symbol

db.hmset 'symbol',
         'name', 'Ohm'
         'description', 'Ancient syllable',
         'phonemes', phos.to_json
require 'redis'

db = Redis.new

phos = %w(pho1 pho2 pho3)
db.hmset phos[1], 'sound', 'aaa', 'meaning', 'beginning',
'god', 'Brahma'

db.hmset phos[2], 'sound', 'ooo', 'meaning', 'duration',
'god', 'Vishnu'

db.hmset phos[3], 'sound', 'mmm', 'meaning', 'dissolution',
'god', 'Shiva'

db.hmset 'symbol',
  'name', 'Ohm'
  'description', 'Ancient syllable',
  'alternatives', 'alternatives',
  'phenomes', phos.to_json
Awesome?
I want lot’s of Symbols
I want lot’s of Symbols

      db.hmset 'symbol',
        'name', 'Ohm'
        'description', 'Ancient syllable',
        'alternatives', 'alternatives',
        'phenomes', phos.to_json
I want lot’s of Symbols
I want lot’s of Symbols
I want lot’s of Symbols

 Require a key strategy
I want lot’s of Symbols

 Require a key strategy
 Store the key in redis
I want lot’s of Symbols

 Require a key strategy
 Store the key in redis
 Auto-increment
# "symbol:1", "symbol:2" ... "symbol:n"
def get_symbol_pk
  pk = db.incr 'symbol:pk'
  "symbol:#{pk}"
end

# ...

db.hmset get_symbol_pk, ...
Ok, but...
I wanna find symbols by
name!
I wanna find symbols by
name!
Requires an extra index
I wanna find symbols by
name!
Requires an extra index
Store index in a hash or a set
I wanna find symbols by
name!
Requires an extra index
Store index in a hash or a set
We need an index key strategy
I wanna find symbols by
name!
Requires an extra index
Store index in a hash or a set
We need an index key strategy
...
SYMBOL_NAME_INDEX = 'symbol:index:name'

def store_name_index symbol_key, name
  db.sadd "#{SYMBOL_NAME_INDEX}:#{name}", symbol_key
end

def find_symbol_by_name name
  db.get "#{SYMBOL_NAME_INDEX}:#{name}"
end

# Transaction...
db.multi do
  symbol_key = get_symbol_pk

  db.hmset get_symbol_pk,
    'name', symbol_name,
    ...

  store_name_index symbol_key, symbol_name
end
SYMBOL_NAME_INDEX = 'symbol:index:name'

def store_name_index symbol_key, name
  db.sadd "#{SYMBOL_NAME_INDEX}:#{name}", symbol_key
end

def find_symbol_by_name name
  db.get "#{SYMBOL_NAME_INDEX}:#{name}"
end

# Transaction...
db.multi do
  symbol_key = get_symbol_pk

  db.hmset get_symbol_pk,
    'name', symbol_name,
    ...

  store_name_index symbol_key, symbol_name
end
SYMBOL_NAME_INDEX = 'symbol:index:name'

def store_name_index symbol_key, name
  db.sadd "#{SYMBOL_NAME_INDEX}:#{name}", symbol_key
end

def find_symbol_by_name name
  db.get "#{SYMBOL_NAME_INDEX}:#{name}"
end

# Transaction...
db.multi do
  symbol_key = get_symbol_pk

  db.hmset get_symbol_pk,
    'name', symbol_name,
    ...

  store_name_index symbol_key, symbol_name
end
Fine, but now i want...
Fine, but now i want...

 Validation
Fine, but now i want...

 Validation
 To use this in project X
Fine, but now i want...

 Validation
 To use this in project X
 To be able to find the symbol of a phoneme
Fine, but now i want...

 Validation
 To use this in project X
 To be able to find the symbol of a phoneme
 ... shoot me?
This sucks!!
 Not OO at all
 Validation?
 Referencing containing object
 What other pitfalls have we missed?
 Project/implementation specific
 ...
Let’s build our own object
mapping on top of redis!
or...
Ohm: Object hash map
symbol = {
  :name => 'Ohm',
  :description =>   'Ancient syllable',
  :phonemes => [
    {
      :sound   =>   'aaa',
      :meaning =>   'beginning of the universe',
      :god     =>   'Brahma'
    }, {
      :sound   =>   'ooo',
      :meaning =>   'duration of the universe',
      :god     =>   'Vishnu'
    }, {
      :sound   =>   'mmm',
      :meaning =>   'dissolution of the universe',
      :god     =>   'Shiva'
    }
  ]
}
require 'rubygems'
require 'ohm'

class AncientSymbol < Ohm::Model

  attribute :name
  attribute :description

  index :name

  collection :phonemes, Phoneme

  def validate
    assert_present :name
  end

end
class Phoneme < Ohm::Model

  attribute :sound
  attribute :meaning
  attribute :god

  index :sound
  index :god

  reference :ancient_symbol, AncientSymbol

  def validate
    assert_present :sound
  end

end
Ohm.connect

symbol = AncientSymbol.create

p symbol.valid? # false

p symbol.errors # [[:name, :not_present]]
error_messages = symbol.errors.present do |e|
  e.on [:name, :not_present] do
    "Symbols must have a name."
  end
end
symbol = AncientSymbol.new :name => 'Ohm'

if symbol.save
  symbol.phonemes << Phoneme.create(
      :sound   => 'aaa',
      :meaning => 'beginnging of universe',
      :god     => 'Brahma',
      :ancient_symbol => symbol )

  # ...

end
Cool, so how do find stuff?
Phoneme.all
Phoneme.all
# <Set (Phoneme): ["3", "1", "2"]>
Phoneme.all
# <Set (Phoneme): ["3", "1", "2"]>


Phoneme.all.except :sound => 'ooo'
Phoneme.all
# <Set (Phoneme): ["3", "1", "2"]>


Phoneme.all.except :sound => 'ooo'
# <Set (Phoneme): ["3", "1"]>
Phoneme.all
# <Set (Phoneme): ["3", "1", "2"]>


Phoneme.all.except :sound => 'ooo'
# <Set (Phoneme): ["3", "1"]>


Phoneme.find :sound => 'aaa'
Phoneme.all
# <Set (Phoneme): ["3", "1", "2"]>


Phoneme.all.except :sound => 'ooo'
# <Set (Phoneme): ["3", "1"]>


Phoneme.find :sound => 'aaa'
# <Set (Phoneme): ["1"]>
Phoneme.all
# <Set (Phoneme): ["3", "1", "2"]>


Phoneme.all.except :sound => 'ooo'
# <Set (Phoneme): ["3", "1"]>


Phoneme.find :sound => 'aaa'
# <Set (Phoneme): ["1"]>


pho = Phoneme[1]
Phoneme.all
# <Set (Phoneme): ["3", "1", "2"]>


Phoneme.all.except :sound => 'ooo'
# <Set (Phoneme): ["3", "1"]>


Phoneme.find :sound => 'aaa'
# <Set (Phoneme): ["1"]>


pho = Phoneme[1]
# <Phoneme:1 ancient_symbol_id="1" sound="aaa"
Phoneme.all
# <Set (Phoneme): ["3", "1", "2"]>


Phoneme.all.except :sound => 'ooo'
# <Set (Phoneme): ["3", "1"]>


Phoneme.find :sound => 'aaa'
# <Set (Phoneme): ["1"]>


pho = Phoneme[1]
# <Phoneme:1 ancient_symbol_id="1" sound="aaa"
meaning="beginnging of universe" god="Brahma">
Phoneme.all
# <Set (Phoneme): ["3", "1", "2"]>


Phoneme.all.except :sound => 'ooo'
# <Set (Phoneme): ["3", "1"]>


Phoneme.find :sound => 'aaa'
# <Set (Phoneme): ["1"]>


pho = Phoneme[1]
# <Phoneme:1 ancient_symbol_id="1" sound="aaa"
meaning="beginnging of universe" god="Brahma">


pho.ancient_symbol
Phoneme.all
# <Set (Phoneme): ["3", "1", "2"]>


Phoneme.all.except :sound => 'ooo'
# <Set (Phoneme): ["3", "1"]>


Phoneme.find :sound => 'aaa'
# <Set (Phoneme): ["1"]>


pho = Phoneme[1]
# <Phoneme:1 ancient_symbol_id="1" sound="aaa"
meaning="beginnging of universe" god="Brahma">


pho.ancient_symbol
# <AncientSymbol:1 name="Ohm" description=nil>
Phoneme.all
# <Set (Phoneme): ["3", "1", "2"]>


Phoneme.all.except :sound => 'ooo'
# <Set (Phoneme): ["3", "1"]>


Phoneme.find :sound => 'aaa'
# <Set (Phoneme): ["1"]>


pho = Phoneme[1]
# <Phoneme:1 ancient_symbol_id="1" sound="aaa"
meaning="beginnging of universe" god="Brahma">


pho.ancient_symbol
# <AncientSymbol:1 name="Ohm" description=nil>


symbol.phonemes
Phoneme.all
# <Set (Phoneme): ["3", "1", "2"]>


Phoneme.all.except :sound => 'ooo'
# <Set (Phoneme): ["3", "1"]>


Phoneme.find :sound => 'aaa'
# <Set (Phoneme): ["1"]>


pho = Phoneme[1]
# <Phoneme:1 ancient_symbol_id="1" sound="aaa"
meaning="beginnging of universe" god="Brahma">


pho.ancient_symbol
# <AncientSymbol:1 name="Ohm" description=nil>


symbol.phonemes
# <Set (Phoneme): ["3", "1", "2"]>
We also get sorting...
symbol.phonemes.sort
# [<Phoneme:1>, <Phoneme:2>, <Phoneme:3>]
symbol.phonemes.sort
# [<Phoneme:1>, <Phoneme:2>, <Phoneme:3>]




symbol.phonemes.sort_by( :sound, :order => 'ALPHA DESC' )
# [<Phoneme:2>, <Phoneme:3>, <Phoneme:1>]
symbol.phonemes.sort
# [<Phoneme:1>, <Phoneme:2>, <Phoneme:3>]




symbol.phonemes.sort_by( :sound, :order => 'ALPHA DESC' )
# [<Phoneme:2>, <Phoneme:3>, <Phoneme:1>]




symbol.phonemes.sort_by( :sound, :get => :god )
# ["Shiva", "Brahma", "Vishnu"]
What else can we do?
Attribute types

 attribute
 set
 list
 counter
 reference
 collection
Available validation

 assert_present
 assert_format
 assert_numeric
 assert_unique
ohm-contrib
Boundaries

Callbacks

Timestamping

ToHash

WebValidations

NumberValidations

ExtraValidations

Typecast

Locking
Awesome?
Questions?
Links


www.christopherspring.com
http://ohm.keyvalue.org/
http://www.redis.io/

Contenu connexe

Tendances

Communities - Perl edition (RioJS)
Communities - Perl edition (RioJS)Communities - Perl edition (RioJS)
Communities - Perl edition (RioJS)garux
 
F# delight
F# delightF# delight
F# delightpriort
 
Use cases in the code with AOP
Use cases in the code with AOPUse cases in the code with AOP
Use cases in the code with AOPAndrzej Krzywda
 
The Art, Joy, and Power of Creating Musical Programs (JFugue at SXSW Interact...
The Art, Joy, and Power of Creating Musical Programs (JFugue at SXSW Interact...The Art, Joy, and Power of Creating Musical Programs (JFugue at SXSW Interact...
The Art, Joy, and Power of Creating Musical Programs (JFugue at SXSW Interact...David Koelle
 
Ruby - Uma Introdução
Ruby - Uma IntroduçãoRuby - Uma Introdução
Ruby - Uma IntroduçãoÍgor Bonadio
 
JFugue, Music, and the Future of Java [JavaOne 2016, CON1851]
JFugue, Music, and the Future of Java [JavaOne 2016, CON1851]JFugue, Music, and the Future of Java [JavaOne 2016, CON1851]
JFugue, Music, and the Future of Java [JavaOne 2016, CON1851]David Koelle
 
Haste (Same Language, Multiple Platforms) and Tagless Final Style (Same Synta...
Haste (Same Language, Multiple Platforms) and Tagless Final Style (Same Synta...Haste (Same Language, Multiple Platforms) and Tagless Final Style (Same Synta...
Haste (Same Language, Multiple Platforms) and Tagless Final Style (Same Synta...takeoutweight
 
Python basic
Python basic Python basic
Python basic sewoo lee
 
Scala in a Java 8 World
Scala in a Java 8 WorldScala in a Java 8 World
Scala in a Java 8 WorldDaniel Blyth
 
Template Haskell Tutorial
Template Haskell TutorialTemplate Haskell Tutorial
Template Haskell Tutorialkizzx2
 
CouchDB @ red dirt ruby conference
CouchDB @ red dirt ruby conferenceCouchDB @ red dirt ruby conference
CouchDB @ red dirt ruby conferenceleinweber
 
Desarrollando aplicaciones web en minutos
Desarrollando aplicaciones web en minutosDesarrollando aplicaciones web en minutos
Desarrollando aplicaciones web en minutosEdgar Suarez
 
Functional Pattern Matching on Python
Functional Pattern Matching on PythonFunctional Pattern Matching on Python
Functional Pattern Matching on PythonDaker Fernandes
 
Introduction to Groovy
Introduction to GroovyIntroduction to Groovy
Introduction to GroovyAnton Arhipov
 

Tendances (20)

Communities - Perl edition (RioJS)
Communities - Perl edition (RioJS)Communities - Perl edition (RioJS)
Communities - Perl edition (RioJS)
 
F# delight
F# delightF# delight
F# delight
 
Good Code
Good CodeGood Code
Good Code
 
Use cases in the code with AOP
Use cases in the code with AOPUse cases in the code with AOP
Use cases in the code with AOP
 
The Art, Joy, and Power of Creating Musical Programs (JFugue at SXSW Interact...
The Art, Joy, and Power of Creating Musical Programs (JFugue at SXSW Interact...The Art, Joy, and Power of Creating Musical Programs (JFugue at SXSW Interact...
The Art, Joy, and Power of Creating Musical Programs (JFugue at SXSW Interact...
 
Clojure入門
Clojure入門Clojure入門
Clojure入門
 
Ruby - Uma Introdução
Ruby - Uma IntroduçãoRuby - Uma Introdução
Ruby - Uma Introdução
 
JFugue, Music, and the Future of Java [JavaOne 2016, CON1851]
JFugue, Music, and the Future of Java [JavaOne 2016, CON1851]JFugue, Music, and the Future of Java [JavaOne 2016, CON1851]
JFugue, Music, and the Future of Java [JavaOne 2016, CON1851]
 
Haste (Same Language, Multiple Platforms) and Tagless Final Style (Same Synta...
Haste (Same Language, Multiple Platforms) and Tagless Final Style (Same Synta...Haste (Same Language, Multiple Platforms) and Tagless Final Style (Same Synta...
Haste (Same Language, Multiple Platforms) and Tagless Final Style (Same Synta...
 
Python basic
Python basic Python basic
Python basic
 
Scala in a Java 8 World
Scala in a Java 8 WorldScala in a Java 8 World
Scala in a Java 8 World
 
FPBrno 2018-05-22: Benchmarking in elixir
FPBrno 2018-05-22: Benchmarking in elixirFPBrno 2018-05-22: Benchmarking in elixir
FPBrno 2018-05-22: Benchmarking in elixir
 
Template Haskell Tutorial
Template Haskell TutorialTemplate Haskell Tutorial
Template Haskell Tutorial
 
Ruby 1.9
Ruby 1.9Ruby 1.9
Ruby 1.9
 
PubNative Tracker
PubNative TrackerPubNative Tracker
PubNative Tracker
 
CouchDB @ red dirt ruby conference
CouchDB @ red dirt ruby conferenceCouchDB @ red dirt ruby conference
CouchDB @ red dirt ruby conference
 
PHP 5.4
PHP 5.4PHP 5.4
PHP 5.4
 
Desarrollando aplicaciones web en minutos
Desarrollando aplicaciones web en minutosDesarrollando aplicaciones web en minutos
Desarrollando aplicaciones web en minutos
 
Functional Pattern Matching on Python
Functional Pattern Matching on PythonFunctional Pattern Matching on Python
Functional Pattern Matching on Python
 
Introduction to Groovy
Introduction to GroovyIntroduction to Groovy
Introduction to Groovy
 

Similaire à Ohm

Writing Apps the Google-y Way (Brisbane)
Writing Apps the Google-y Way (Brisbane)Writing Apps the Google-y Way (Brisbane)
Writing Apps the Google-y Way (Brisbane)Pamela Fox
 
Ruby 入門 第一次就上手
Ruby 入門 第一次就上手Ruby 入門 第一次就上手
Ruby 入門 第一次就上手Wen-Tien Chang
 
第二讲 Python基礎
第二讲 Python基礎第二讲 Python基礎
第二讲 Python基礎juzihua1102
 
第二讲 预备-Python基礎
第二讲 预备-Python基礎第二讲 预备-Python基礎
第二讲 预备-Python基礎anzhong70
 
Python tutorial
Python tutorialPython tutorial
Python tutorialRajiv Risi
 
Achieving Parsing Sanity In Erlang
Achieving Parsing Sanity In ErlangAchieving Parsing Sanity In Erlang
Achieving Parsing Sanity In ErlangSean Cribbs
 
Searching ORM: First Why, Then How
Searching ORM: First Why, Then HowSearching ORM: First Why, Then How
Searching ORM: First Why, Then Howsfarmer10
 
Ruby 程式語言入門導覽
Ruby 程式語言入門導覽Ruby 程式語言入門導覽
Ruby 程式語言入門導覽Wen-Tien Chang
 
Refactor like a boss
Refactor like a bossRefactor like a boss
Refactor like a bossgsterndale
 
Ruby 2: some new things
Ruby 2: some new thingsRuby 2: some new things
Ruby 2: some new thingsDavid Black
 
A Taste of Python - Devdays Toronto 2009
A Taste of Python - Devdays Toronto 2009A Taste of Python - Devdays Toronto 2009
A Taste of Python - Devdays Toronto 2009Jordan Baker
 
Class 6: Lists & dictionaries
Class 6: Lists & dictionariesClass 6: Lists & dictionaries
Class 6: Lists & dictionariesMarc Gouw
 
Python tutorial
Python tutorialPython tutorial
Python tutorialnazzf
 
Python tutorial
Python tutorialPython tutorial
Python tutorialShani729
 

Similaire à Ohm (20)

Writing Apps the Google-y Way (Brisbane)
Writing Apps the Google-y Way (Brisbane)Writing Apps the Google-y Way (Brisbane)
Writing Apps the Google-y Way (Brisbane)
 
Ruby 入門 第一次就上手
Ruby 入門 第一次就上手Ruby 入門 第一次就上手
Ruby 入門 第一次就上手
 
第二讲 Python基礎
第二讲 Python基礎第二讲 Python基礎
第二讲 Python基礎
 
第二讲 预备-Python基礎
第二讲 预备-Python基礎第二讲 预备-Python基礎
第二讲 预备-Python基礎
 
Ruby 101
Ruby 101Ruby 101
Ruby 101
 
Poetic APIs
Poetic APIsPoetic APIs
Poetic APIs
 
Python tutorial
Python tutorialPython tutorial
Python tutorial
 
Achieving Parsing Sanity In Erlang
Achieving Parsing Sanity In ErlangAchieving Parsing Sanity In Erlang
Achieving Parsing Sanity In Erlang
 
My First Ruby
My First RubyMy First Ruby
My First Ruby
 
Searching ORM: First Why, Then How
Searching ORM: First Why, Then HowSearching ORM: First Why, Then How
Searching ORM: First Why, Then How
 
1 the ruby way
1   the ruby way1   the ruby way
1 the ruby way
 
Ruby 程式語言入門導覽
Ruby 程式語言入門導覽Ruby 程式語言入門導覽
Ruby 程式語言入門導覽
 
Refactor like a boss
Refactor like a bossRefactor like a boss
Refactor like a boss
 
P3 2018 python_regexes
P3 2018 python_regexesP3 2018 python_regexes
P3 2018 python_regexes
 
Strings and Symbols
Strings and SymbolsStrings and Symbols
Strings and Symbols
 
Ruby 2: some new things
Ruby 2: some new thingsRuby 2: some new things
Ruby 2: some new things
 
A Taste of Python - Devdays Toronto 2009
A Taste of Python - Devdays Toronto 2009A Taste of Python - Devdays Toronto 2009
A Taste of Python - Devdays Toronto 2009
 
Class 6: Lists & dictionaries
Class 6: Lists & dictionariesClass 6: Lists & dictionaries
Class 6: Lists & dictionaries
 
Python tutorial
Python tutorialPython tutorial
Python tutorial
 
Python tutorial
Python tutorialPython tutorial
Python tutorial
 

Plus de Christopher Spring

Plus de Christopher Spring (7)

Steady with ruby
Steady with rubySteady with ruby
Steady with ruby
 
jRuby and TorqueBox
jRuby and TorqueBoxjRuby and TorqueBox
jRuby and TorqueBox
 
jRuby: The best of both worlds
jRuby: The best of both worldsjRuby: The best of both worlds
jRuby: The best of both worlds
 
EventMachine for RubyFuZa 2012
EventMachine for RubyFuZa   2012EventMachine for RubyFuZa   2012
EventMachine for RubyFuZa 2012
 
Ruby Concurrency and EventMachine
Ruby Concurrency and EventMachineRuby Concurrency and EventMachine
Ruby Concurrency and EventMachine
 
Redis overview for Software Architecture Forum
Redis overview for Software Architecture ForumRedis overview for Software Architecture Forum
Redis overview for Software Architecture Forum
 
Redis, Resque & Friends
Redis, Resque & FriendsRedis, Resque & Friends
Redis, Resque & Friends
 

Dernier

Beyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry InnovationBeyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry InnovationSafe Software
 
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure serviceWhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure servicePooja Nehwal
 
Enhancing Worker Digital Experience: A Hands-on Workshop for Partners
Enhancing Worker Digital Experience: A Hands-on Workshop for PartnersEnhancing Worker Digital Experience: A Hands-on Workshop for Partners
Enhancing Worker Digital Experience: A Hands-on Workshop for PartnersThousandEyes
 
Pigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping ElbowsPigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping ElbowsPigging Solutions
 
Breaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountBreaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountPuma Security, LLC
 
Key Features Of Token Development (1).pptx
Key  Features Of Token  Development (1).pptxKey  Features Of Token  Development (1).pptx
Key Features Of Token Development (1).pptxLBM Solutions
 
Understanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitectureUnderstanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitecturePixlogix Infotech
 
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...Neo4j
 
Install Stable Diffusion in windows machine
Install Stable Diffusion in windows machineInstall Stable Diffusion in windows machine
Install Stable Diffusion in windows machinePadma Pradeep
 
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhi
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | DelhiFULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhi
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhisoniya singh
 
AI as an Interface for Commercial Buildings
AI as an Interface for Commercial BuildingsAI as an Interface for Commercial Buildings
AI as an Interface for Commercial BuildingsMemoori
 
Slack Application Development 101 Slides
Slack Application Development 101 SlidesSlack Application Development 101 Slides
Slack Application Development 101 Slidespraypatel2
 
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024BookNet Canada
 
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...shyamraj55
 
Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...Alan Dix
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreternaman860154
 
The Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxMalak Abu Hammad
 
Benefits Of Flutter Compared To Other Frameworks
Benefits Of Flutter Compared To Other FrameworksBenefits Of Flutter Compared To Other Frameworks
Benefits Of Flutter Compared To Other FrameworksSoftradix Technologies
 
Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)Allon Mureinik
 
SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024Scott Keck-Warren
 

Dernier (20)

Beyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry InnovationBeyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
 
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure serviceWhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
WhatsApp 9892124323 ✓Call Girls In Kalyan ( Mumbai ) secure service
 
Enhancing Worker Digital Experience: A Hands-on Workshop for Partners
Enhancing Worker Digital Experience: A Hands-on Workshop for PartnersEnhancing Worker Digital Experience: A Hands-on Workshop for Partners
Enhancing Worker Digital Experience: A Hands-on Workshop for Partners
 
Pigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping ElbowsPigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping Elbows
 
Breaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountBreaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path Mount
 
Key Features Of Token Development (1).pptx
Key  Features Of Token  Development (1).pptxKey  Features Of Token  Development (1).pptx
Key Features Of Token Development (1).pptx
 
Understanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitectureUnderstanding the Laravel MVC Architecture
Understanding the Laravel MVC Architecture
 
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
 
Install Stable Diffusion in windows machine
Install Stable Diffusion in windows machineInstall Stable Diffusion in windows machine
Install Stable Diffusion in windows machine
 
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhi
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | DelhiFULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhi
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhi
 
AI as an Interface for Commercial Buildings
AI as an Interface for Commercial BuildingsAI as an Interface for Commercial Buildings
AI as an Interface for Commercial Buildings
 
Slack Application Development 101 Slides
Slack Application Development 101 SlidesSlack Application Development 101 Slides
Slack Application Development 101 Slides
 
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
 
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
 
Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreter
 
The Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptx
 
Benefits Of Flutter Compared To Other Frameworks
Benefits Of Flutter Compared To Other FrameworksBenefits Of Flutter Compared To Other Frameworks
Benefits Of Flutter Compared To Other Frameworks
 
Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)
 
SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024
 

Ohm

  • 1. Ohm Object hash map Christopher Spring
  • 3. Ohm runs on redis
  • 4. Ohm runs on redis Remote dictionary server
  • 5. Ohm runs on redis Remote dictionary server Store multiple data-structure types
  • 6. Ohm runs on redis Remote dictionary server Store multiple data-structure types Lists, Sets, Sorted Sets, Strings, Hashes
  • 7. Ohm runs on redis Remote dictionary server Store multiple data-structure types Lists, Sets, Sorted Sets, Strings, Hashes In memory datastore with persistence strategies
  • 8. Ohm runs on redis Remote dictionary server Store multiple data-structure types Lists, Sets, Sorted Sets, Strings, Hashes In memory datastore with persistence strategies Super fast & easy to scale
  • 9. Ohm runs on redis Remote dictionary server Store multiple data-structure types Lists, Sets, Sorted Sets, Strings, Hashes In memory datastore with persistence strategies Super fast & easy to scale Atomic operations & transactions
  • 10. Ohm runs on redis Remote dictionary server Store multiple data-structure types Lists, Sets, Sorted Sets, Strings, Hashes In memory datastore with persistence strategies Super fast & easy to scale Atomic operations & transactions Key expiry
  • 11. Ohm runs on redis Remote dictionary server Store multiple data-structure types Lists, Sets, Sorted Sets, Strings, Hashes In memory datastore with persistence strategies Super fast & easy to scale Atomic operations & transactions Key expiry redis-rb
  • 13.
  • 16. Ancient syllable Aaa-ooo-mmm The beginning, duration and dissolution of the universe
  • 17. Ancient syllable Aaa-ooo-mmm The beginning, duration and dissolution of the universe Brahma, Vishnu & Shiva
  • 18. or...
  • 19. symbol = { :name => 'Ohm', :description => 'Ancient syllable', :phonemes => [ { :sound => 'aaa', :meaning => 'beginning of the universe', :god => 'Brahma' }, { :sound => 'ooo', :meaning => 'duration of the universe', :god => 'Vishnu' }, { :sound => 'mmm', :meaning => 'dissolution of the universe', :god => 'Shiva' } ] }
  • 20. Let’s store that in redis...
  • 21. require 'redis' db = Redis.new
  • 22. # make a bunch of phonemes # HMSET key field value [field value ...] phos = %w(pho1 pho2 pho3) db.hmset phos[1], 'sound', 'aaa', 'meaning', 'beginning', 'god', 'Brahma' db.hmset phos[2], 'sound', 'ooo', 'meaning', 'duration', 'god', 'Vishnu' db.hmset phos[3], 'sound', 'mmm', 'meaning', 'dissolution', 'god', 'Shiva'
  • 23. # ... and create the symbol db.hmset 'symbol', 'name', 'Ohm' 'description', 'Ancient syllable', 'phonemes', phos.to_json
  • 24. require 'redis' db = Redis.new phos = %w(pho1 pho2 pho3) db.hmset phos[1], 'sound', 'aaa', 'meaning', 'beginning', 'god', 'Brahma' db.hmset phos[2], 'sound', 'ooo', 'meaning', 'duration', 'god', 'Vishnu' db.hmset phos[3], 'sound', 'mmm', 'meaning', 'dissolution', 'god', 'Shiva' db.hmset 'symbol', 'name', 'Ohm' 'description', 'Ancient syllable', 'alternatives', 'alternatives', 'phenomes', phos.to_json
  • 26. I want lot’s of Symbols
  • 27. I want lot’s of Symbols db.hmset 'symbol', 'name', 'Ohm' 'description', 'Ancient syllable', 'alternatives', 'alternatives', 'phenomes', phos.to_json
  • 28. I want lot’s of Symbols
  • 29. I want lot’s of Symbols
  • 30. I want lot’s of Symbols Require a key strategy
  • 31. I want lot’s of Symbols Require a key strategy Store the key in redis
  • 32. I want lot’s of Symbols Require a key strategy Store the key in redis Auto-increment
  • 33. # "symbol:1", "symbol:2" ... "symbol:n" def get_symbol_pk pk = db.incr 'symbol:pk' "symbol:#{pk}" end # ... db.hmset get_symbol_pk, ...
  • 35. I wanna find symbols by name!
  • 36. I wanna find symbols by name! Requires an extra index
  • 37. I wanna find symbols by name! Requires an extra index Store index in a hash or a set
  • 38. I wanna find symbols by name! Requires an extra index Store index in a hash or a set We need an index key strategy
  • 39. I wanna find symbols by name! Requires an extra index Store index in a hash or a set We need an index key strategy ...
  • 40. SYMBOL_NAME_INDEX = 'symbol:index:name' def store_name_index symbol_key, name db.sadd "#{SYMBOL_NAME_INDEX}:#{name}", symbol_key end def find_symbol_by_name name db.get "#{SYMBOL_NAME_INDEX}:#{name}" end # Transaction... db.multi do symbol_key = get_symbol_pk db.hmset get_symbol_pk, 'name', symbol_name, ... store_name_index symbol_key, symbol_name end
  • 41. SYMBOL_NAME_INDEX = 'symbol:index:name' def store_name_index symbol_key, name db.sadd "#{SYMBOL_NAME_INDEX}:#{name}", symbol_key end def find_symbol_by_name name db.get "#{SYMBOL_NAME_INDEX}:#{name}" end # Transaction... db.multi do symbol_key = get_symbol_pk db.hmset get_symbol_pk, 'name', symbol_name, ... store_name_index symbol_key, symbol_name end
  • 42. SYMBOL_NAME_INDEX = 'symbol:index:name' def store_name_index symbol_key, name db.sadd "#{SYMBOL_NAME_INDEX}:#{name}", symbol_key end def find_symbol_by_name name db.get "#{SYMBOL_NAME_INDEX}:#{name}" end # Transaction... db.multi do symbol_key = get_symbol_pk db.hmset get_symbol_pk, 'name', symbol_name, ... store_name_index symbol_key, symbol_name end
  • 43. Fine, but now i want...
  • 44. Fine, but now i want... Validation
  • 45. Fine, but now i want... Validation To use this in project X
  • 46. Fine, but now i want... Validation To use this in project X To be able to find the symbol of a phoneme
  • 47. Fine, but now i want... Validation To use this in project X To be able to find the symbol of a phoneme ... shoot me?
  • 48. This sucks!! Not OO at all Validation? Referencing containing object What other pitfalls have we missed? Project/implementation specific ...
  • 49. Let’s build our own object mapping on top of redis!
  • 50. or...
  • 52. symbol = { :name => 'Ohm', :description => 'Ancient syllable', :phonemes => [ { :sound => 'aaa', :meaning => 'beginning of the universe', :god => 'Brahma' }, { :sound => 'ooo', :meaning => 'duration of the universe', :god => 'Vishnu' }, { :sound => 'mmm', :meaning => 'dissolution of the universe', :god => 'Shiva' } ] }
  • 53. require 'rubygems' require 'ohm' class AncientSymbol < Ohm::Model attribute :name attribute :description index :name collection :phonemes, Phoneme def validate assert_present :name end end
  • 54. class Phoneme < Ohm::Model attribute :sound attribute :meaning attribute :god index :sound index :god reference :ancient_symbol, AncientSymbol def validate assert_present :sound end end
  • 55. Ohm.connect symbol = AncientSymbol.create p symbol.valid? # false p symbol.errors # [[:name, :not_present]]
  • 56. error_messages = symbol.errors.present do |e| e.on [:name, :not_present] do "Symbols must have a name." end end
  • 57. symbol = AncientSymbol.new :name => 'Ohm' if symbol.save symbol.phonemes << Phoneme.create( :sound => 'aaa', :meaning => 'beginnging of universe', :god => 'Brahma', :ancient_symbol => symbol ) # ... end
  • 58. Cool, so how do find stuff?
  • 59.
  • 61. Phoneme.all # <Set (Phoneme): ["3", "1", "2"]>
  • 62. Phoneme.all # <Set (Phoneme): ["3", "1", "2"]> Phoneme.all.except :sound => 'ooo'
  • 63. Phoneme.all # <Set (Phoneme): ["3", "1", "2"]> Phoneme.all.except :sound => 'ooo' # <Set (Phoneme): ["3", "1"]>
  • 64. Phoneme.all # <Set (Phoneme): ["3", "1", "2"]> Phoneme.all.except :sound => 'ooo' # <Set (Phoneme): ["3", "1"]> Phoneme.find :sound => 'aaa'
  • 65. Phoneme.all # <Set (Phoneme): ["3", "1", "2"]> Phoneme.all.except :sound => 'ooo' # <Set (Phoneme): ["3", "1"]> Phoneme.find :sound => 'aaa' # <Set (Phoneme): ["1"]>
  • 66. Phoneme.all # <Set (Phoneme): ["3", "1", "2"]> Phoneme.all.except :sound => 'ooo' # <Set (Phoneme): ["3", "1"]> Phoneme.find :sound => 'aaa' # <Set (Phoneme): ["1"]> pho = Phoneme[1]
  • 67. Phoneme.all # <Set (Phoneme): ["3", "1", "2"]> Phoneme.all.except :sound => 'ooo' # <Set (Phoneme): ["3", "1"]> Phoneme.find :sound => 'aaa' # <Set (Phoneme): ["1"]> pho = Phoneme[1] # <Phoneme:1 ancient_symbol_id="1" sound="aaa"
  • 68. Phoneme.all # <Set (Phoneme): ["3", "1", "2"]> Phoneme.all.except :sound => 'ooo' # <Set (Phoneme): ["3", "1"]> Phoneme.find :sound => 'aaa' # <Set (Phoneme): ["1"]> pho = Phoneme[1] # <Phoneme:1 ancient_symbol_id="1" sound="aaa" meaning="beginnging of universe" god="Brahma">
  • 69. Phoneme.all # <Set (Phoneme): ["3", "1", "2"]> Phoneme.all.except :sound => 'ooo' # <Set (Phoneme): ["3", "1"]> Phoneme.find :sound => 'aaa' # <Set (Phoneme): ["1"]> pho = Phoneme[1] # <Phoneme:1 ancient_symbol_id="1" sound="aaa" meaning="beginnging of universe" god="Brahma"> pho.ancient_symbol
  • 70. Phoneme.all # <Set (Phoneme): ["3", "1", "2"]> Phoneme.all.except :sound => 'ooo' # <Set (Phoneme): ["3", "1"]> Phoneme.find :sound => 'aaa' # <Set (Phoneme): ["1"]> pho = Phoneme[1] # <Phoneme:1 ancient_symbol_id="1" sound="aaa" meaning="beginnging of universe" god="Brahma"> pho.ancient_symbol # <AncientSymbol:1 name="Ohm" description=nil>
  • 71. Phoneme.all # <Set (Phoneme): ["3", "1", "2"]> Phoneme.all.except :sound => 'ooo' # <Set (Phoneme): ["3", "1"]> Phoneme.find :sound => 'aaa' # <Set (Phoneme): ["1"]> pho = Phoneme[1] # <Phoneme:1 ancient_symbol_id="1" sound="aaa" meaning="beginnging of universe" god="Brahma"> pho.ancient_symbol # <AncientSymbol:1 name="Ohm" description=nil> symbol.phonemes
  • 72. Phoneme.all # <Set (Phoneme): ["3", "1", "2"]> Phoneme.all.except :sound => 'ooo' # <Set (Phoneme): ["3", "1"]> Phoneme.find :sound => 'aaa' # <Set (Phoneme): ["1"]> pho = Phoneme[1] # <Phoneme:1 ancient_symbol_id="1" sound="aaa" meaning="beginnging of universe" god="Brahma"> pho.ancient_symbol # <AncientSymbol:1 name="Ohm" description=nil> symbol.phonemes # <Set (Phoneme): ["3", "1", "2"]>
  • 73. We also get sorting...
  • 74.
  • 76. symbol.phonemes.sort # [<Phoneme:1>, <Phoneme:2>, <Phoneme:3>] symbol.phonemes.sort_by( :sound, :order => 'ALPHA DESC' ) # [<Phoneme:2>, <Phoneme:3>, <Phoneme:1>]
  • 77. symbol.phonemes.sort # [<Phoneme:1>, <Phoneme:2>, <Phoneme:3>] symbol.phonemes.sort_by( :sound, :order => 'ALPHA DESC' ) # [<Phoneme:2>, <Phoneme:3>, <Phoneme:1>] symbol.phonemes.sort_by( :sound, :get => :god ) # ["Shiva", "Brahma", "Vishnu"]
  • 78. What else can we do?
  • 79. Attribute types attribute set list counter reference collection
  • 80. Available validation assert_present assert_format assert_numeric assert_unique

Notes de l'éditeur

  1. \n
  2. \n
  3. \n
  4. \n
  5. \n
  6. \n
  7. \n
  8. \n
  9. \n
  10. \n
  11. We&amp;#x2019;re going to build up to why we need Ohm with an example of the pitfalls of trying to roll your own solution to interact with redis as a datastore.\n
  12. \n
  13. \n
  14. \n
  15. \n
  16. \n
  17. \n
  18. \n
  19. Get reference to database\n
  20. \n
  21. \n
  22. Awesome - we&amp;#x2019;re storing our information in redis!\nSo what&amp;#x2019;s wrong with this approach?\n
  23. \n
  24. The key we&amp;#x2019;re using for symbols isn&amp;#x2019;t adequate for multiple symbols...\n
  25. The key we&amp;#x2019;re using for symbols isn&amp;#x2019;t adequate for multiple symbols...\n
  26. \n
  27. \n
  28. \n
  29. \n
  30. \n
  31. \n
  32. \n
  33. \n
  34. \n
  35. Using a set because each element will be unique\nOrder isn&amp;#x2019;t important\n
  36. \n
  37. \n
  38. \n
  39. \n
  40. \n
  41. \n
  42. \n
  43. \n
  44. \n
  45. \n
  46. \n
  47. index: creates an index for an attribute you want to be able to search\ncollection: similar to has_many\n
  48. \n
  49. Validation behaves similarly to what we see in AR\nErrors are returned as an array of attributes and the associated failure\n
  50. Error messages are not defined within the model, but with a presenter\n\nerror_messages is just an array of errors\n
  51. Notice that the phoneme is added to the symbols phonemes\nAND a phoneme gets a reference to ancient_symbol\n
  52. \n
  53. All sets are created on the fly\n
  54. All sets are created on the fly\n
  55. All sets are created on the fly\n
  56. All sets are created on the fly\n
  57. All sets are created on the fly\n
  58. All sets are created on the fly\n
  59. All sets are created on the fly\n
  60. All sets are created on the fly\n
  61. All sets are created on the fly\n
  62. All sets are created on the fly\n
  63. All sets are created on the fly\n
  64. All sets are created on the fly\n
  65. All sets are created on the fly\n
  66. \n
  67. \n
  68. \n
  69. \n
  70. \n
  71. attribute-&gt;string set-&gt;unordered array list-&gt;array (great for queue) reference-&gt;foreign key collection-&gt;has_many\n
  72. \n
  73. Ohm contrib is a collection of drop-in modules\n
  74. \n
  75. \n
  76. \n