|
require "virtus" |
|
require "benchmark" |
|
require 'active_support/core_ext/hash/indifferent_access' |
|
require "rake" |
|
require "pp" |
|
|
|
class GeoLocation |
|
include Virtus.value_object |
|
|
|
attribute :lat, BigDecimal |
|
attribute :lng, BigDecimal |
|
end |
|
|
|
class RawGeoLocation |
|
attr_reader :lat, :lng |
|
def initialize(lat: lat, lng: lng) |
|
@lat = BigDecimal(lat, 12) |
|
@lng = BigDecimal(lng, 12) |
|
end |
|
end |
|
|
|
class CoercedWithVirtus |
|
|
|
include Virtus.model |
|
|
|
attribute :hotel_id, String |
|
attribute :name, String |
|
attribute :address_line1, String |
|
attribute :address_line2, String |
|
attribute :zip, String |
|
attribute :city, String |
|
attribute :country_code, String |
|
attribute :lat_lng, GeoLocation |
|
attribute :destinations, Array[Integer] |
|
attribute :amenities, String |
|
attribute :badges, Array |
|
attribute :price_min, Float |
|
attribute :review_count, Integer |
|
attribute :review_score, Float |
|
attribute :rating, String |
|
attribute :cover_picture, String |
|
attribute :pictures_count, Integer |
|
attribute :nb_pictures, Integer |
|
attribute :selling_partners, Array |
|
attribute :chains, Array |
|
attribute :kinds, Array |
|
attribute :hh_score, Float |
|
attribute :fb_likes, Integer |
|
attribute :distance, Float |
|
|
|
end |
|
|
|
class CustomCoercedWithVirtus |
|
|
|
include Virtus.model |
|
|
|
attribute :hotel_id |
|
attribute :name |
|
attribute :address_line1 |
|
attribute :address_line2 |
|
attribute :zip |
|
attribute :city |
|
attribute :country_code |
|
attribute :lat_lng, RawGeoLocation |
|
attribute :destinations, Array[Integer] |
|
attribute :amenities |
|
attribute :badges |
|
attribute :price_min, Float |
|
attribute :review_count, Integer |
|
attribute :review_score, Float |
|
attribute :rating |
|
attribute :cover_picture |
|
attribute :pictures_count, Integer |
|
attribute :nb_pictures, Integer |
|
attribute :selling_partner |
|
attribute :chains |
|
attribute :kinds |
|
attribute :hh_score, Float |
|
attribute :fb_likes, Integer |
|
attribute :distance, Float |
|
|
|
end |
|
|
|
class RawWithVirtus |
|
|
|
include Virtus.model(:coerce => false) |
|
|
|
attribute :hotel_id |
|
attribute :name |
|
attribute :address_line1 |
|
attribute :address_line2 |
|
attribute :zip |
|
attribute :city |
|
attribute :country_code |
|
attribute :lat_lng |
|
attribute :destinations |
|
attribute :amenities |
|
attribute :badges |
|
attribute :price_min |
|
attribute :review_count |
|
attribute :review_score |
|
attribute :rating |
|
attribute :cover_picture |
|
attribute :pictures_count |
|
attribute :nb_pictures |
|
attribute :selling_partners |
|
attribute :chains |
|
attribute :kinds |
|
attribute :hh_score |
|
attribute :fb_likes |
|
attribute :distance |
|
|
|
end |
|
|
|
class CoercedWithoutVirtus |
|
|
|
attr_accessor :hotel_id, :name, :address_line1, :address_line2, |
|
:zip, :city, :country_code, :lat_lng, :destinations, |
|
:amenities, :badges, :price_min, :review_count, :review_score, |
|
:rating, :cover_picture, :pictures_count, :nb_pictures, |
|
:selling_partners, :chains, :kinds, :hh_score, :fb_likes, :distance |
|
|
|
def initialize(attr = {}) |
|
|
|
@hotel_id = attr[:hotel_id] # String |
|
@name = attr[:name] # String |
|
@address_line1 = attr[:address_line1] # String |
|
@address_line2 = attr[:address_line2] # String |
|
@zip = attr[:zip] # String |
|
@city = attr[:city] # String |
|
@country_code = attr[:country_code] # String |
|
@lat_lng = GeoLocation.new(attr[:lat_lng]) # GeoLocation |
|
@destinations = Array(attr[:destinations]).map{|d| Integer(d)} # Array[Integer] |
|
@amenities = Array(attr[:amenities]) # String |
|
@badges = Array(attr[:badges]) # Array |
|
@price_min = Float(attr[:price_min]) if attr[:price_min] # Float |
|
@review_count = Integer(attr[:review_count]) if attr[:review_count] # Integer |
|
@review_score = Float(attr[:review_score]) if attr[:review_score] # Float |
|
@rating = attr[:rating] # String |
|
@cover_picture = attr[:cover_picture] # String |
|
@pictures_count = Integer(attr[:pictures_count]) if attr[:pictures_count] # Integer |
|
@nb_pictures = Integer(attr[:nb_pictures]) if attr[:nb_pictures] # Integer |
|
@selling_partners = Array(attr[:selling_partners]) # Array |
|
@chains = Array(attr[:chains]) # Array |
|
@kinds = Array(attr[:kinds]) # Array |
|
@hh_score = Float(attr[:hh_score]) if attr[:hh_score] # Float |
|
@fb_likes = Integer(attr[:fb_likes]) if # Integer |
|
@distance = Float(attr[:distance]) if attr[:distance] # Float |
|
end |
|
|
|
end |
|
|
|
class IndifferentCoercedWithoutVirtus |
|
|
|
attr_reader :attributes |
|
|
|
def initialize(attr = {}) |
|
attributes = ActiveSupport::HashWithIndifferentAccess.new(attr) |
|
|
|
@attributes = {} |
|
|
|
@attributes[:hotel_id] = attributes[:hotel_id] # String |
|
@attributes[:name] = attributes[:name] # String |
|
@attributes[:address_line1] = attributes[:address_line1] # String |
|
@attributes[:address_line2] = attributes[:address_line2] # String |
|
@attributes[:zip] = attributes[:zip] # String |
|
@attributes[:city] = attributes[:city] # String |
|
@attributes[:country_code] = attributes[:country_code] # String |
|
@attributes[:lat_lng] = RawGeoLocation.new(lat: attributes[:lat_lng][:lat], lng: attributes[:lat_lng][:lng]) # GeoLocation |
|
@attributes[:destinations] = Array(attributes[:destinations]).map{|d| Integer(d)} # Array[Integer] |
|
@attributes[:amenities] = Array(attributes[:amenities]) # String |
|
@attributes[:badges] = Array(attributes[:badges]) # Array |
|
@attributes[:price_min] = Float(attributes[:price_min]) if attributes[:price_min] # Float |
|
@attributes[:review_count] = Integer(attributes[:review_count]) if attributes[:review_count] # Integer |
|
@attributes[:review_score] = Float(attributes[:review_score]) if attributes[:review_score] # Float |
|
@attributes[:rating] = attributes[:rating] # String |
|
@attributes[:cover_picture] = attributes[:cover_picture] # String |
|
@attributes[:pictures_count] = Integer(attributes[:pictures_count]) if attributes[:pictures_count] # Integer |
|
@attributes[:nb_pictures] = Integer(attributes[:nb_pictures]) if attributes[:nb_pictures] # Integer |
|
@attributes[:selling_partners] = Array(attributes[:selling_partners]) # Array |
|
@attributes[:chains] = Array(attributes[:chains]) # Array |
|
@attributes[:kinds] = Array(attributes[:kinds]) # Array |
|
@attributes[:hh_score] = Float(attributes[:hh_score]) if attributes[:hh_score] # Float |
|
@attributes[:fb_likes] = Integer(attributes[:fb_likes]) if attributes[:fb_likes] # Integer |
|
@attributes[:distance] = Float(attributes[:distance]) if attributes[:distance] # Float |
|
|
|
end |
|
|
|
def method_missing(method, *args, &block) |
|
attributes.fetch(method.to_sym) { |
|
super |
|
} |
|
end |
|
|
|
def respond_to_missing?(method, *) |
|
attributes.has_key?(method.to_sym) || super |
|
end |
|
|
|
end |
|
|
|
class CuratorStyle |
|
|
|
attr_accessor :hotel_id, :name, :address_line1, :address_line2, |
|
:zip, :city, :country_code, :lat_lng, :destinations, |
|
:amenities, :badges, :price_min, :review_count, :review_score, |
|
:rating, :cover_picture, :pictures_count, :nb_pictures, |
|
:selling_partners, :chains, :kinds, :hh_score, :fb_likes, :distance |
|
|
|
def initialize(args = {}) |
|
method_strings = methods.map(&:to_s) |
|
args.each do |attribute, value| |
|
send("#{attribute}=", value) if method_strings.include?("#{attribute}=") |
|
# instance_variable_set("@#{attribute}", value) if method_strings.include?(attribute.to_s) |
|
end |
|
end |
|
|
|
def lat_lng=(value) |
|
@lat_lng = RawGeoLocation.new(lat: value["lat"], lng: value["lng"]) |
|
end |
|
def lat_lng |
|
@lat_lng |
|
end |
|
def destinations=(value) |
|
@destinations = Array(value).map{|d| Integer(d)} |
|
end |
|
def amenities=(value) |
|
@amenities = Array(value) |
|
end |
|
def badges=(value) |
|
@badges = Array(value) |
|
end |
|
def price_min=(value) |
|
@price_min = Float(value) if value |
|
end |
|
def review_count=(value) |
|
@review_count = Integer(value) if value |
|
end |
|
def review_score=(value) |
|
@review_score = Float(value) if value |
|
end |
|
def pictures_count=(value) |
|
@pictures_count = Integer(value) if value |
|
end |
|
def nb_pictures=(value) |
|
@nb_pictures = Integer(value) if value |
|
end |
|
def selling_partners=(value) |
|
@selling_partners = Array(value) |
|
end |
|
def chains=(value) |
|
@chains = Array(value) |
|
end |
|
def kinds=(value) |
|
@kinds = Array(value) |
|
end |
|
def hh_score=(value) |
|
@hh_score = Float(value) if value |
|
end |
|
def fb_likes=(value) |
|
@fb_likes = Integer(value) if value |
|
end |
|
def distance=(value) |
|
@distance = Float(value) if value |
|
end |
|
end |
|
|
|
|
|
class RawWithoutVirtus |
|
|
|
attr_accessor :hotel_id, :name, :address_line1, :address_line2, |
|
:zip, :city, :country_code, :lat_lng, :destinations, |
|
:amenities, :badges, :price_min, :review_count, :review_score, |
|
:rating, :cover_picture, :pictures_count, :nb_pictures, |
|
:selling_partners, :chains, :kinds, :hh_score, :fb_likes, :distance |
|
|
|
def initialize(attr = {}) |
|
|
|
@hotel_id = attr[:hotel_id] |
|
@name = attr[:name] |
|
@address_line1 = attr[:address_line1] |
|
@address_line2 = attr[:address_line2] |
|
@zip = attr[:zip] |
|
@city = attr[:city] |
|
@country_code = attr[:country_code] |
|
@lat_lng = attr[:lat_lng] |
|
@destinations = attr[:destinations] |
|
@amenities = attr[:amenities] |
|
@badges = attr[:badges] |
|
@price_min = attr[:price_min] |
|
@review_count = attr[:review_count] |
|
@review_score = attr[:review_score] |
|
@rating = attr[:rating] |
|
@cover_picture = attr[:cover_picture] |
|
@pictures_count = attr[:pictures_count] |
|
@nb_pictures = attr[:nb_pictures] |
|
@selling_partners = attr[:selling_partners] |
|
@chains = attr[:chains] |
|
@kinds = attr[:kinds] |
|
@hh_score = attr[:hh_score] |
|
@fb_likes = attr[:fb_likes] |
|
@distance = attr[:distance] |
|
end |
|
|
|
end |
|
|
|
task :env do |
|
puts Time.now, RUBY_DESCRIPTION |
|
puts |
|
end |
|
|
|
task :init => :env do |
|
@init_hash = { |
|
:hotel_id => "123456", |
|
:name => "name", |
|
:address_line1 => "address_line1", |
|
:address_line2 => "address_line2", |
|
:zip => "zip", |
|
:city => "city", |
|
:country_code => "FR", |
|
:lat_lng => {"lat" => "42.123456", "lng" => "5.123456"}, |
|
:destinations => %w(1 2 3 4 5 6), |
|
:amenities => %w(toto tata titi), |
|
:badges => %w(toto tata titi), |
|
:price_min => "50", |
|
:review_count => "195", |
|
:review_score => "8.78", |
|
:rating => "3", |
|
:cover_picture => "cover_picture", |
|
:pictures_count => "12", |
|
:nb_pictures => "13", |
|
:selling_partners => %w(foo bar baz), |
|
:chains => ["Hilton", "Carlton"], |
|
:kinds => %w(FOO BAR), |
|
:hh_score => "115", |
|
:fb_likes => "45678", |
|
:distance => "1.456", |
|
} |
|
end |
|
|
|
task :verify => :init do |
|
pp object = CuratorStyle.new(@init_hash) |
|
# object = IndifferentCoercedWithoutVirtus.new(@init_hash) |
|
# pp begin |
|
# object.method("example") |
|
# rescue NameError => ex |
|
# ex |
|
# end |
|
# pp object.method("hotel_id") |
|
# pp object.hotel_id |
|
end |
|
|
|
task :benchmark_virtus => :init do |
|
n = 1_000 |
|
|
|
Benchmark.bmbm do |x| |
|
x.report("coerced_with_virtus") { n.times { CoercedWithVirtus.new(@init_hash) } } |
|
x.report("custom_coerced_with_virtus") { n.times { CustomCoercedWithVirtus.new(@init_hash) } } |
|
x.report("raw_with_virtus") { n.times { RawWithVirtus.new(@init_hash) } } |
|
x.report("indifferent_coerced_without_virtus") { n.times { IndifferentCoercedWithoutVirtus.new(@init_hash) } } |
|
x.report("coerced_without_virtus") { n.times { CoercedWithoutVirtus.new(@init_hash) } } |
|
x.report("raw_without_virtus") { n.times { RawWithoutVirtus.new(@init_hash) } } |
|
x.report("curator_style") { n.times { CuratorStyle.new(@init_hash) } } |
|
end |
|
end |
|
|
|
# Rake::Task['benchmark_virtus'].invoke |
|
Rake::Task['verify'].invoke |