Module: Arstotzka

Extended by:
ActiveSupport::Concern, Sinclair::Configurable
Defined in:
lib/arstotzka.rb,
lib/arstotzka/base.rb,
lib/arstotzka/config.rb,
lib/arstotzka/reader.rb,
lib/arstotzka/crawler.rb,
lib/arstotzka/fetcher.rb,
lib/arstotzka/options.rb,
lib/arstotzka/version.rb,
lib/arstotzka/wrapper.rb,
lib/arstotzka/exception.rb,
lib/arstotzka/type_cast.rb,
lib/arstotzka/key_reader.rb,
lib/arstotzka/hash_reader.rb,
lib/arstotzka/key_changer.rb,
lib/arstotzka/class_methods.rb,
lib/arstotzka/fetcher/cache.rb,
lib/arstotzka/method_builder.rb,
lib/arstotzka/post_processor.rb,
lib/arstotzka/fetcher_builder.rb

Overview

Concern that enables the translation of domain though the usage of the class method expose

Examples:

json = <<-JSON
  {
    "person": {
      "fullName": "Kelly Khan",
      "age": 32,
      "gender": "woman"
    },
    "collections": {
      "cars": [{
        "maker": "Volkswagen",
        "model": "Jetta",
        "units": [{
          "nickName": "Betty",
          "year": 2013
        }, {
          "nickName": "Roger",
          "year": 2014
        }]
      }, {
        "maker": "Ferrari",
        "model": "Ferrari X1",
        "units": [{
          "nickName": "Geronimo",
          "year": 2014
        }]
      }, {
        "maker": "Fiat",
        "model": "Uno",
        "units": [{
          "year": 1998
        }]
      }],
      "games":[{
        "producer": "Lucas Pope",
        "titles": [{
          "name": "papers, please",
          "played": "10.2%"
        }, {
          "name": "TheNextBigThing",
          "played": "100%"
        }]
      }, {
        "producer": "Nintendo",
        "titles": [{
          "name": "Zelda",
          "played": "90%"
        }]
      }, {
        "producer": "BadCompany"
      }]
    }
  }
JSON

hash = JSON.parse(json)
class Collector
  include Arstotzka

  MALE  = 'male'
  FEMALE= 'female'

  attr_reader :hash

  expose :full_name, :age, path: :person, json: :hash
  expose :gender, path: :person, type: :gender, cached: true, json: :hash

  def initialize(hash = {})
    @hash = hash
  end
end

module Arstotzka
  module TypeCast
    def to_gender(value)
      case value
      when 'man'
        Collector::MALE
      when 'woman'
        Collector::FEMALE
      else
        raise 'invalid gender'
      end
    end
  end
end

collector = Collector.new(hash)
collector.full_name # returns 'Kelly Khan'
collector.age       # returns 32
collector.gender    # returns Collector::FEMALE

hash['person']['fullName'] = 'Robert'
collector.full_name # returns 'Robert'

hash['person']['gender'] = 'man'
collector.gender    # returns Collector::FEMALE as it was cached
class Collector
  expose :car_names, flatten: true, compact: false, json: :hash,
         default: 'MissingName',
         full_path: 'collections.cars.units.nick_name'
end

collector = Collector.new(hash)
collector.car_names # returns [
                    #   'Betty', 'Roger',
                    #   'Geronimo', 'MissingName'
                    # ]
class Collector
  class Game
    include Arstotzka

    attr_reader :json

    expose :name
    expose :played, type: :float

    def initialize(json)
      @json = json
    end

    def finished?
      played > 85.0
    end
  end
end

class Collector
  expose :finished_games, json: :hash,
         flatten: true, klass: Collector::Game,
         after: :filter_finished, compact: true,
         full_path: 'collections.games.titles'

  private

  def filter_finished(games)
    games.select(&:finished?)
  end
end

collector = Collector.new(hash)
collector.finished_games # returns [
                         #   Collector::Game.new(name: "TheNextBigThing", played: 100.0),
                         #   Collector::Game.new(name: "Zelda", played: 90.0)
                         # ]

Using options klass and after

class Customer
  attr_reader :name, :age

  def initialize(name:, age:)
    @name = name
    @age  = age
  end

  def adult?
    age >= 18
  end
end

class Store
  include Arstotzka

  expose :customers, klass: Customer, after: :filter_adults

  def initialize(json)
    @json = json
  end

  private

  attr_reader :json

  def filter_adults(values)
    values.select(&:adult?)
  end
end

hash = {
  customers: [{
    name: 'John', age: 21
  }, {
    name: 'Julia', age: 15
  }, {
    name: 'Carol', age: 22
  }, {
    name: 'Bobby', age: 12
  }]
}

instance = Store.new(hash)

instance.customers # returns [
                   #   Customer.new(name: 'John', age: 21),
                   #   Customer.new(name: 'Carol', age: 22)
                   # ]

Using type with klass and after_each

module Arstotzka::TypeCast
  def to_symbolized_hash(value)
    value.symbolize_keys
  end
end

class Drink
  attr_reader :name, :price

  def initialize(name:, price:)
    @name  = name
    @price = price
  end

  def inflate(inflation)
    @price = (price * (1 + inflation)).round(2)
  end
end

class Bar
  include Arstotzka

  expose :drinks, type: :symbolized_hash,
    klass: Drink, after_each: :add_inflation

  def initialize(json)
    @json = json
  end

  private

  attr_reader :json

  def add_inflation(drink)
    drink.inflate(0.1)
    drink
  end
end

json = '{"drinks":[{"name":"tequila","price":7.50},{ "name":"vodka","price":5.50}]}'

hash = JSON.parse(hash)

instance = Bar.new(hash)

instance.drinks # returns [
                #   Drink.new(name: 'tequila', price: 8.25),
                #   Drink.new(name: 'vodka', price: 6.05)
                # ]

Using cached, compact, after and full_path

class Person
  attr_reader :name

  def initialize(name)
    @name = name
  end
end

class Application
  include Arstotzka

  expose :users, full_path: 'users.first_name',
                 compact: true, cached: true,
                 after: :create_person

  def initialize(json)
    @json = json
  end

  private

  attr_reader :json

  def create_person(names)
    names.map do |name|
      warn "Creating person #{name}"
      Person.new(name)
    end
  end
end

# Keys are on camel case (lower camel case)
hash = {
  users: [
    { firstName: 'Lucy',  email: '[email protected]' },
    { firstName: 'Bobby', email: '[email protected]' },
    { email: '[email protected]' },
    { firstName: 'Arthur', email: '[email protected]' }
  ]
}

instance = Application.new(hash)

instance.users # trigers the warn "Creating person <name>" 3 times
               # returns [
               #   Person.new('Lucy'),
               #   Person.new('Bobby'),
               #   Person.new('Arthur')
               # ]
instance.users # returns the same value, without triggering warn

Working with snake case hash

class JobSeeker
  include Arstotzka

  expose :applicants, case: :snake, default: 'John Doe',
                      full_path: 'applicants.full_name',
                      compact: true, json: :@hash

  def initialize(hash)
    @hash = hash
  end
end

hash = {
  'applicants' => [
    {
      'full_name' => 'Robert Hatz',
      'email' => '[email protected]'
    }, {
      'full_name' => 'Marina Wantz',
      'email' => '[email protected]'
    }, {
      'email' => '[email protected]'
    }
  ]
}

instance = JobSeeker.new(hash)

instance.applicants # returns [
                    #   'Robert Hatz',
                    #   'Marina Wantz',
                    #   'John Doe'
                    # ]

Deep path with flatten option

class ShoppingMall
  include Arstotzka

  expose :customers, path: 'floors.stores',
                     flatten: true, json: :hash

  def initialize(hash)
    @hash = hash
  end

  private

  attr_reader :hash
end

hash = {
  floors: [{
    name: 'ground', stores: [{
      name: 'Starbucks', customers: %w[
        John Bobby Maria
      ]
    }, {
      name: 'Pizza Hut', customers: %w[
        Danny LJ
      ]
    }]
  }, {
    name: 'first', stores: [{
      name: 'Disney', customers: %w[
        Robert Richard
      ]
    }, {
      name: 'Comix', customers: %w[
        Linda Ariel
      ]
    }]
  }]
}

instance = ShoppingMall.new(hash)

instance.customers # returns %w[
                   #   John Bobby Maria
                   #   Danny LJ Robert Richard
                   #   Linda Ariel
                   # ]

See Also:

Defined Under Namespace

Modules: Base, ClassMethods, Exception, TypeCast Classes: Config, Crawler, Fetcher, FetcherBuilder, HashReader, KeyChanger, KeyReader, MethodBuilder, Options, PostProcessor, Reader, Wrapper

Constant Summary collapse

VERSION =
'1.6.2'