Skip to content

Commit

Permalink
Test object allocation
Browse files Browse the repository at this point in the history
  • Loading branch information
shioyama committed Jul 24, 2017
1 parent a61e52b commit 4bcd37d
Show file tree
Hide file tree
Showing 13 changed files with 107 additions and 17 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ env:
- DB=postgres ORM=sequel SEQUEL_VERSION=4.46
- DB=mysql ORM=sequel SEQUEL_VERSION=4.46
- DB=sqlite3 ORM=sequel SEQUEL_VERSION=4.46
- DB=sqlite3 ORM=
- DB=sqlite3 ORM= TEST_PERFORMANCE=true
before_script:
- bundle exec rake db:create db:up
before_install: gem install bundler -v 1.12.5
Expand Down
2 changes: 2 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ group :development, :test do
end
end

gem 'allocation_stats' if ENV['TEST_PERFORMANCE']

platforms :ruby do
gem 'guard-rspec'
gem 'pry-byebug'
Expand Down
3 changes: 2 additions & 1 deletion lib/mobility/backend.rb
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,8 @@ def self.included(base)
# @param [String] attribute
# @return [String] name of backend reader method
def self.method_name(attribute)
"#{attribute}_backend".freeze
@backend_method_names ||= {}
@backend_method_names[attribute] ||= "#{attribute}_backend".freeze
end

# Defines setup hooks for backend to customize model class.
Expand Down
6 changes: 3 additions & 3 deletions lib/mobility/backend/cache.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def self.apply(attributes, option_value)
# @!macro backend_reader
# @option options [Boolean] cache
# *false* to disable cache.
def read(locale, **options)
def read(locale, options = {})
return super if options.delete(:cache) == false
if write_to_cache? || cache.has_key?(locale)
cache[locale]
Expand All @@ -56,7 +56,7 @@ def read(locale, **options)
# @!macro backend_writer
# @option options [Boolean] cache
# *false* to disable cache.
def write(locale, value, **options)
def write(locale, value, options = {})
return super if options.delete(:cache) == false
cache[locale] = write_to_cache? ? value : super
end
Expand All @@ -69,7 +69,7 @@ def write(locale, value, **options)
module Setup
# @param model_class Model class
# @param [Array<String>] attributes Backend attributes
def setup_model(model_class, attributes, **_)
def setup_model(model_class, attributes, _options = {})
super
model_class.include BackendResetter.for(model_class).new(attributes) { clear_cache }
end
Expand Down
4 changes: 2 additions & 2 deletions lib/mobility/backend/default.rb
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,9 @@ def self.apply(attributes, option)
end

def initialize(default_option)
define_method :read do |locale, **options|
define_method :read do |locale, options = {}|
default = options.has_key?(:default) ? options.delete(:default) : default_option
if (value = super(locale, **options)).nil?
if (value = super(locale, options)).nil?
default.is_a?(Proc) ? default.call(model: model, attribute: attribute) : default
else
value
Expand Down
6 changes: 3 additions & 3 deletions lib/mobility/backend/fallbacks.rb
Original file line number Diff line number Diff line change
Expand Up @@ -90,14 +90,14 @@ def initialize(fallbacks_option)
private

def define_read(fallbacks)
define_method :read do |locale, **options|
define_method :read do |locale, options = {}|
fallback = options.delete(:fallback)

if fallback == false || (fallback.nil? && fallbacks.nil?)
super(locale, **options)
super(locale, options)
else
(fallback ? [locale, *fallback] : fallbacks[locale]).detect do |fallback_locale|
value = super(fallback_locale, **options)
value = super(fallback_locale, options)
break value if value.present?
end
end
Expand Down
6 changes: 3 additions & 3 deletions lib/mobility/backend/null.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@ class Null

# @!group Backend Accessors
# @return [NilClass]
def read(*); end
def read(_locale, _options = {}); end

# @return [NilClass]
def write(*); end
def write(_locale, _value, _options = {}); end
# @!endgroup

# @!group Backend Configuration
def self.configure(*); end
def self.configure(_options); end
# @!endgroup
end
end
Expand Down
6 changes: 3 additions & 3 deletions lib/mobility/backend/presence.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def self.apply(attributes, option)
# @!macro backend_reader
# @option options [Boolean] presence
# *false* to disable presence filter.
def read(locale, **options)
def read(locale, options = {})
return super if options.delete(:presence) == false
value = super
value == false ? value : value.presence
Expand All @@ -28,9 +28,9 @@ def read(locale, **options)
# @!macro backend_writer
# @option options [Boolean] presence
# *false* to disable presence filter.
def write(locale, value, **options)
def write(locale, value, options = {})
return super if options.delete(:presence) == false
super(locale, value == false ? value : value.presence, **options)
super(locale, value == false ? value : value.presence, options)
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/mobility/core_ext/string.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
class String
# paraphrased from activesupport
def camelize
sub(/^[a-z\d]*/) { $&.capitalize }.gsub(/(?:_|(\/))([a-z\d]*)/) { "#{$1}#{$2.capitalize}" }.gsub('/', '::')
sub(/^[a-z\d]*/) { $&.capitalize }.gsub(/(?:_|(\/))([a-z\d]*)/) { "#{$1}#{$2.capitalize}" }.gsub('/'.freeze, '::'.freeze)
end

def present?
Expand Down
43 changes: 43 additions & 0 deletions spec/performance/attributes_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
require "spec_helper"

describe Mobility::Attributes do
describe "initializing" do
specify {
expect { described_class.new(:accessor, backend: :null) }.to allocate_under(9).objects
}
end

describe "including into a class" do
specify {
klass = Class.new do
include Mobility
end
attributes = described_class.new(:accessor, backend: :null)
expect { klass.include attributes }.to allocate_under(30).objects
}
end

describe "accessors" do
let(:klass) do
Class.new do
include Mobility
translates :title, backend: :null
end
end

describe "calling attribute getter" do
specify {
instance = klass.new
expect { 3.times { instance.title } }.to allocate_under(5).objects
}
end

describe "calling attribute setter" do
specify {
instance = klass.new
title = "foo"
expect { 3.times { instance.title = title } }.to allocate_under(5).objects
}
end
end
end
9 changes: 9 additions & 0 deletions spec/performance/configuration_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
require "spec_helper"

describe Mobility::Configuration do
context "when initializing" do
specify {
expect { described_class.new }.to allocate_under(7).objects
}
end
end
1 change: 1 addition & 0 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
require 'pry-byebug'
require 'i18n'
require 'rspec'
require 'allocation_stats' if ENV['TEST_PERFORMANCE']
require 'json'

require 'mobility'
Expand Down
34 changes: 34 additions & 0 deletions spec/support/matchers/allocate_under.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# borrowed from: https://blog.honeybadger.io/testing-object-allocations/
begin
require 'allocation_stats'
rescue LoadError
puts 'Skipping AllocationStats.'
end

RSpec::Matchers.define :allocate_under do |expected|
match do |actual|
return skip('AllocationStats is not available: skipping.') unless defined?(AllocationStats)
@trace = actual.is_a?(Proc) ? AllocationStats.new(burn: 1).trace(&actual) : actual
@trace.new_allocations.size < expected
end

def objects
self
end

def supports_block_expectations?
true
end

def output_trace_info(trace)
trace.allocations(alias_paths: true).group_by(:sourcefile, :sourceline, :class).to_text
end

failure_message do |actual|
"expected under #{ expected } objects to be allocated; got #{ @trace.new_allocations.size }:\n\n" << output_trace_info(@trace)
end

description do
"allocates under #{ expected } objects"
end
end

0 comments on commit 4bcd37d

Please sign in to comment.