Skip to content

Commit

Permalink
unravel
Browse files Browse the repository at this point in the history
  • Loading branch information
seamusabshere committed May 30, 2012
1 parent 5f5dd43 commit f868882
Show file tree
Hide file tree
Showing 19 changed files with 96 additions and 119 deletions.
2 changes: 1 addition & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ source :rubygems

gemspec

gem 'alchemist', :path => '~/alchemist'
gem 'alchemist', :git => 'https://github.com/dkastner/alchemist.git', :branch => 'helpers'
gem 'conversions'

if RUBY_VERSION >= '1.9'
Expand Down
12 changes: 11 additions & 1 deletion lib/data_miner.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,19 @@ def compress_whitespace(str)
str.gsub(INNER_SPACE, ' ').strip
end

# Set the unit converter.
#
# @note As of 2012-05-30, there are problems with the alchemist gem and the use of the conversions gem instead is recommended.
#
# @param [Symbol,nil] conversion_library Either +:alchemist+ or +:conversions+
#
# @return [nil]
def unit_converter=(conversion_library)
@unit_converter = conversion_library ? DataMiner::UnitConverter.load(conversion_library) : nil
@unit_converter = UnitConverter.load conversion_library
nil
end

# @return [#convert,nil] The user-selected unit converter or nil.
def unit_converter
@unit_converter
end
Expand Down
68 changes: 30 additions & 38 deletions lib/data_miner/attribute.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,6 @@ class DataMiner
# @see DataMiner::Step::Import#store Telling an import step to store a column with DataMiner::Step::Import#store
# @see DataMiner::Step::Import#key Telling an import step to key on a column with DataMiner::Step::Import#key
class Attribute
class NoConverterSet < StandardError
def message
'You must set DataMiner.unit_converter to one of [:alchemist, :conversions] if you wish to convert units'
end
end

class << self
# @private
def check_options(options)
Expand All @@ -21,7 +15,11 @@ def check_options(options)
if (invalid_option_keys = options.keys - VALID_OPTIONS).any?
errors << %{Invalid options: #{invalid_option_keys.map(&:inspect).to_sentence}}
end
if (units_options = options.select { |k, _| k.to_s.include?('units') }).any? and VALID_UNIT_DEFINITION_SETS.none? { |d| d.all? { |required_option| options[required_option].present? } }
units_options = options.select { |k, _| k.to_s.include?('units') }
if units_options.any? and DataMiner.unit_converter.nil?
errors << %{You must set DataMiner.unit_converter to :alchemist or :conversions if you wish to convert units}
end
if units_options.any? and VALID_UNIT_DEFINITION_SETS.none? { |d| d.all? { |required_option| options[required_option].present? } }
errors << %{#{units_options.inspect} is not a valid set of units definitions. Please supply a set like #{VALID_UNIT_DEFINITION_SETS.map(&:inspect).to_sentence}".}
end
errors
Expand Down Expand Up @@ -51,12 +49,12 @@ def check_options(options)
]

VALID_UNIT_DEFINITION_SETS = [
[:units],
[:from_units, :to_units],
[:units_field_name],
[:units_field_name, :to_units],
[:units_field_number],
[:units_field_number, :to_units],
[:units], # no conversion
[:from_units, :to_units], # yes
[:units_field_name], # no
[:units_field_name, :to_units], # yes
[:units_field_number], # no
[:units_field_number, :to_units], # yes
]

DEFAULT_SPLIT_PATTERN = /\s+/
Expand Down Expand Up @@ -191,6 +189,8 @@ def initialize(step, name, options = {})
@overwrite = options.fetch :overwrite, DEFAULT_OVERWRITE
@units_field_name = options[:units_field_name]
@units_field_number = options[:units_field_number]
@convert_boolean = (@from_units.present? or (@to_units.present? and (@units_field_name.present? or @units_field_number.present?)))
@persist_units_boolean = (@to_units.present? or @units_field_name.present? or @units_field_number.present?)
@dictionary_mutex = ::Mutex.new
end

Expand All @@ -216,7 +216,7 @@ def set_from_row(local_record, remote_row)
currently_nil = new_value.nil?
end

if not currently_nil and units? and (final_to_units = (to_units || read_units(remote_row)))
if not currently_nil and persist_units? and (final_to_units = (to_units || read_units(remote_row)))
local_record.send "#{name}_units=", final_to_units
end
end
Expand Down Expand Up @@ -257,14 +257,16 @@ def read(row)
keep = split.fetch :keep, DEFAULT_SPLIT_KEEP
value = value.to_s.split(pattern)[keep].to_s
end
if value.blank? and (not stringlike_column? or nullify_blank_strings)
if value.blank? and (not text_column? or nullify_blank_strings)
return
end
value = DataMiner.compress_whitespace value
if upcase
value = DataMiner.upcase value
end
value = convert_units value, row
if convert?
value = convert_units value, row
end
if sprintf
if sprintf.end_with?('f')
value = value.to_f
Expand All @@ -281,14 +283,12 @@ def read(row)

# @private
def convert_units(value, row)
enforce_conversion_options
if convert?
final_from_units = from_units || read_units(row)
final_to_units = to_units || read_units(row)
DataMiner.unit_converter.convert(value, final_from_units, final_to_units)
else
value
final_from_units = from_units || read_units(row)
final_to_units = to_units || read_units(row)
unless final_from_units and final_to_units
raise RuntimeError, "[data_miner] Missing units: from=#{final_from_units.inspect}, to=#{final_to_units.inspect}"
end
DataMiner.unit_converter.convert value, final_from_units, final_to_units
end

# @private
Expand All @@ -302,10 +302,10 @@ def model
step.model
end

def stringlike_column?
return @stringlike_column_query[0] if @stringlike_column_query.is_a?(::Array)
@stringlike_column_query = [model.columns_hash[name.to_s].type == :string]
@stringlike_column_query[0]
def text_column?
return @text_column_query[0] if @text_column_query.is_a?(Array)
@text_column_query = [model.columns_hash[name.to_s].text?]
@text_column_query[0]
end

def static?
Expand All @@ -316,20 +316,12 @@ def dictionary?
@dictionary_boolean
end

def enforce_conversion_options
raise NoConverterSet if DataMiner.unit_converter.nil? and convert_options_present?
end

def convert?
!DataMiner.unit_converter.nil? and convert_options_present?
end

def convert_options_present?
from_units.present? or units_field_name.present? or units_field_number.present?
@convert_boolean
end

def units?
to_units.present? or units_field_name.present? or units_field_number.present?
def persist_units?
@persist_units_boolean
end

def read_units(row)
Expand Down
14 changes: 6 additions & 8 deletions lib/data_miner/unit_converter.rb
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
class DataMiner
class UnitConverter
def self.load(type)
require "data_miner/unit_converter/#{type}"
const_get(type.to_s.camelize).new
end

def convert(value, from, to)
if from.blank? or to.blank?
raise ::RuntimeError, "[data_miner] Missing units (from=#{final_from_units.inspect}, to=#{final_to_units.inspect}"
class << self
def load(type)
if type
require "data_miner/unit_converter/#{type}"
const_get(type.to_s.camelcase).new
end
end
end
end
Expand Down
1 change: 0 additions & 1 deletion lib/data_miner/unit_converter/alchemist.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ class DataMiner
class UnitConverter
class Alchemist < UnitConverter
def convert(value, from, to)
super
value.to_f.send(from).to.send(to)
end
end
Expand Down
3 changes: 1 addition & 2 deletions lib/data_miner/unit_converter/conversions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ class DataMiner
class UnitConverter
class Conversions < UnitConverter
def convert(value, from, to)
super
value.to_f.send(from).to(to)
value.to_f.convert from, to
end
end
end
Expand Down
54 changes: 18 additions & 36 deletions test/data_miner/test_attribute.rb
Original file line number Diff line number Diff line change
@@ -1,54 +1,36 @@
require_relative '../helper'
require 'helper'

describe DataMiner::Attribute do
before do
DataMiner.unit_converter = :alchemist
end

describe '#enforce_conversion_options' do
it 'raises an error if no converter is set but options warrant conversion' do
DataMiner.unit_converter = nil
attribute = DataMiner::Attribute.new :foo, 'bar', :from_units => :pounds, :to_units => :kilograms
assert_raise DataMiner::Attribute::NoConverterSet do
attribute.send(:enforce_conversion_options)
end
end
it 'does not raise an error if a converter is set and options warrant conversion' do
attribute = DataMiner::Attribute.new :foo, 'bar', :from_units => :pounds, :to_units => :kilograms
assert_nothing_raised do
attribute.send(:enforce_conversion_options)
end
end
it 'does not raise an error if a converter is not set and no conversion options' do
DataMiner.unit_converter = nil
attribute = DataMiner::Attribute.new :foo, 'bar'
assert_nothing_raised do
attribute.send(:enforce_conversion_options)
end
end
end

describe '#convert?' do
it 'returns true if a converter and from_units are set' do
it 'returns true if from_units is set' do
attribute = DataMiner::Attribute.new :foo, 'bar', :from_units => :pounds, :to_units => :kilograms
assert attribute.send(:convert?)
end
it 'returns true if a converter and units_field_name are set' do
attribute = DataMiner::Attribute.new :foo, 'bar', :units_field_name => 'bar'
it 'returns true if to_units and units_field_name are set' do
attribute = DataMiner::Attribute.new :foo, 'bar', :units_field_name => 'bar', :to_units => :kilograms
assert attribute.send(:convert?)
end
it 'returns true if a converter and units_field_number are set' do
attribute = DataMiner::Attribute.new :foo, 'bar', :units_field_number => 3
it 'returns true if to_units and units_field_number are set' do
attribute = DataMiner::Attribute.new :foo, 'bar', :units_field_number => 3, :to_units => :kilograms
assert attribute.send(:convert?)
end
it 'returns false if there is no converter' do
DataMiner.unit_converter = nil
attribute = DataMiner::Attribute.new :foo, 'bar', :from_units => :pounds, :to_units => :kilograms
assert !attribute.send(:convert?)
it 'returns false if units_field_name only is set' do
attribute = DataMiner::Attribute.new :foo, 'bar', :units_field_name => 'bar'
refute attribute.send(:convert?)
end
it 'returns false if units_field_number only is set' do
attribute = DataMiner::Attribute.new :foo, 'bar', :units_field_number => 'bar'
refute attribute.send(:convert?)
end
it 'returns false if there is a converter but no units' do
attribute = DataMiner::Attribute.new :foo, 'bar'
assert !attribute.send(:convert?)
it 'raises if no converter and units are used' do
DataMiner.unit_converter = nil
lambda {
DataMiner::Attribute.new :foo, 'bar', :from_units => :pounds, :to_units => :kilograms
}.must_raise ArgumentError, /unit_converter/
end
end
end
2 changes: 1 addition & 1 deletion test/data_miner/unit_converter/test_alchemist.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
require_relative '../../helper'
require 'helper'

describe 'DataMiner::UnitConverter::Alchemist' do
before do
Expand Down
2 changes: 1 addition & 1 deletion test/data_miner/unit_converter/test_conversions.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
require_relative '../../helper'
require 'helper'

describe 'DataMiner::UnitConverter::Conversions' do
before do
Expand Down
16 changes: 7 additions & 9 deletions test/helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,8 @@
require 'minitest/spec'
require 'minitest/autorun'
require 'minitest/reporters'
require 'test/unit/assertions'
MiniTest::Unit.runner = MiniTest::SuiteRunner.new
MiniTest::Unit.runner.reporters << MiniTest::Reporters::SpecReporter.new
include Test::Unit::Assertions

require 'active_record'
require 'logger'
Expand All @@ -29,7 +27,7 @@

require 'data_miner'

def init_database(unit_converter = :alchemist)
def init_database(unit_converter = :conversions)
cmd = %{mysql -u root -ppassword -e "DROP DATABASE data_miner_test; CREATE DATABASE data_miner_test CHARSET utf8"}
$stderr.puts "Running `#{cmd}`..."
system cmd
Expand All @@ -40,14 +38,14 @@ def init_database(unit_converter = :alchemist)
DataMiner::Run.clear_locks

DataMiner.unit_converter = unit_converter
end

def init_models
require 'support/breed'
require 'support/pet'
Pet.auto_upgrade!

ActiveRecord::Base.descendants.each do |model|
model.attr_accessible nil
end
end

def init_pet
require_relative './support/breed'
require_relative './support/pet'
Pet.auto_upgrade!
end
4 changes: 2 additions & 2 deletions test/support/data_miner_with_alchemist.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
require_relative '../helper'
require 'helper'

describe 'DataMiner with Alchemist' do
before do
init_database(:alchemist)
init_pet
init_models
Pet.run_data_miner!
end

Expand Down
4 changes: 2 additions & 2 deletions test/support/data_miner_with_conversions.rb
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
require_relative '../helper'
require 'helper'

require 'conversions'
Conversions.register :years, :years, 1

describe 'DataMiner with Conversions' do
before do
init_database(:conversions)
init_pet
init_models
Pet.run_data_miner!
end

Expand Down
5 changes: 2 additions & 3 deletions test/support/data_miner_without_unit_converter.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
require_relative '../helper'
require 'helper'

class MyPet < ActiveRecord::Base
PETS = File.expand_path('../pets.csv', __FILE__)
Expand Down Expand Up @@ -30,7 +30,6 @@ class MyPet < ActiveRecord::Base
end
end


describe 'DataMiner with Conversions' do
it 'happens when DataMiner.unit_converter is nil' do
DataMiner.unit_converter.must_be_nil
Expand All @@ -45,7 +44,7 @@ class MyPet < ActiveRecord::Base
it 'raises an error if conversions are attempted' do
init_database(nil)
lambda do
init_pet
init_models
Pet.run_data_miner!
end.must_raise DataMiner::Attribute::NoConverterSet
end
Expand Down
Loading

1 comment on commit f868882

@seamusabshere
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"unravel" is a bad name for this commit - it's because i git rebased everything onto a single commit that happened to be called "unravel"

Please sign in to comment.