MemoryRecord

Build Status Gem Version Dependency Status

Introduction

A simple library that handles a few records easily. With this library can flexibly managed immutable data.

Installation

Install as a standalone gem

$ gem install memory_record

Or install within application using Gemfile

$ bundle add memory_record
$ bundle install

Examples

Basic usage

class Language
  include MemoryRecord
  memory_record [
    {key: :lisp, author: "John McCarthy"      },
    {key: :c,    author: "Dennis Ritchie"     },
    {key: :ruby, author: "Yukihiro Matsumoto" },
  ]

  def mr_author
    "Mr. #{author}"
  end
end

Language[:ruby].key        # => :ruby
Language[:ruby].code       # => 2
Language[:ruby].author     # => "Yukihiro Matsumoto"
Language[:ruby].mr_author  # => "Mr. Yukihiro Matsumoto"

Language.keys              # => [:lisp, :c, :ruby]
Language.collect(&:author) # => ["John McCarthy", "Dennis Ritchie", "Yukihiro Matsumoto"]

How to turn as an array?

Enumerable extended, so that each method is available

Foo.each { |e| ... }
Foo.collect { |e| ... }

How do I submit a form to select in Rails?

form.collection_select(:selection_code, Foo, :code, :name)

Is the reference in subscripts slow?

Since it has a hash internally using the key value as a key, it can be acquired with O (1).

Foo[1].name  # => "A"
Foo[:a].name # => "A"

Instances always react to code and key

object = Foo.first
object.key  # => :a
object.code # => 1

How do I add a method to an instance?

For that, I am creating a new class so I need to define it normally

name method is special?

If name is not defined, it defines a name method that returns key.to_s

to_s method is defined?

Alias of name, to_s is defined.

If there is no key, use fetch to get an error

Foo.fetch(:xxx)              # => <KeyError: ...>

The following are all the same

Foo[:xxx] || :default        # => :default
Foo.fetch(:xxx, :default}    # => :default
Foo.fetch(:xxx) { :default } # => :default

Use fetch_if to ignore if the key is nil

Foo.fetch_if(nil)            # => nil
Foo.fetch_if(:a)             # => #<Foo:... @attributes={...}>
Foo.fetch_if(:xxx)           # => <KeyError: ...>

How to refer to other keys

class Foo
  include MemoryRecord
  memory_record [
    {key: :a, other_key: :x},
    {key: :b, other_key: :y},
    {key: :c, other_key: :z},
  ]

  class << self
    def lookup(v)
      super || invert_table[v]
    end

    private

    def invert_table
      @invert_table ||= inject({}) {|a, e| a.merge(e.other_key => e) }
    end
  end
end

Foo[:a] == Foo[:x]                  # => true
Foo[:b] == Foo[:y]                  # => true
Foo[:c] == Foo[:z]                  # => true

How can I prohibit the hash key from being attr_reader automatically?

attr_reader: false

I think that it is better to use it when you want to make it difficult to access easily.

class Foo
  include MemoryRecord
  memory_record attr_reader: false do
    [
      {x: 1, y: 1, z: 1},
    ]
  end
end

Foo.first.x rescue $! # => #<NoMethodError: undefined method `x' for #<Foo:0x007fb2c710eda8>>
Foo.first.y rescue $! # => #<NoMethodError: undefined method `y' for #<Foo:0x007fb2c710eda8>>
Foo.first.z rescue $! # => #<NoMethodError: undefined method `z' for #<Foo:0x007fb2c710eda8>>

attr_reader: :y

class Foo
  include MemoryRecord
  memory_record attr_reader: {only: :y} do
    [
      {x: 1, y: 1, z: 1},
    ]
  end
end

Foo.first.x rescue $! # => #<NoMethodError: undefined method `x' for #<Foo:0x007fcc861ff108>>
Foo.first.y rescue $! # => 1
Foo.first.z rescue $! # => #<NoMethodError: undefined method `z' for #<Foo:0x007fcc861ff108>>

attr_reader: :y

class Foo
  include MemoryRecord
  memory_record attr_reader: {except: :y} do
    [
      {x: 1, y: 1, z: 1},
    ]
  end
end

Foo.first.x rescue $! # => 1
Foo.first.y rescue $! # => #<NoMethodError: undefined method `y' for #<Foo:0x007ff033895e88>>
Foo.first.z rescue $! # => 1

*** How to decide code yourself?

class Foo
  include MemoryRecord
  memory_record [
    {code: 1, key: :a, name: "A"},
    {code: 2, key: :b, name: "B"},
    {code: 3, key: :c, name: "C"},
  ]
end

Foo.collect(&:code) # => [1, 2, 3]

It is not recommended to specify it explicitly. It is useful only when refactoring legacy code with compatibility in mind.