conformist 0.0.3 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/{CHANGELOG.markdown → CHANGELOG.md} +10 -0
- data/README.md +299 -0
- data/Rakefile +5 -17
- data/conformist.gemspec +19 -11
- data/lib/conformist.rb +17 -19
- data/lib/conformist/base.rb +2 -47
- data/lib/conformist/builder.rb +10 -0
- data/lib/conformist/column.rb +14 -17
- data/lib/conformist/hash_struct.rb +33 -0
- data/lib/conformist/schema.rb +66 -0
- data/lib/conformist/version.rb +1 -1
- data/test/helper.rb +2 -11
- data/test/schemas/acma.rb +15 -0
- data/test/{definitions → schemas}/fcc.rb +2 -4
- data/test/unit/conformist/base_test.rb +9 -0
- data/test/unit/conformist/builder_test.rb +12 -0
- data/test/{conformist → unit/conformist}/column_test.rb +4 -0
- data/test/unit/conformist/hash_struct_test.rb +48 -0
- data/test/unit/conformist/schema_test.rb +74 -0
- data/test/unit/conformist_test.rb +20 -0
- data/test/unit/integration_test.rb +72 -0
- metadata +66 -74
- data/README.markdown +0 -157
- data/lib/conformist/row.rb +0 -26
- data/test/conformist/base_test.rb +0 -49
- data/test/conformist/conformist_test.rb +0 -21
- data/test/conformist/row_test.rb +0 -21
- data/test/definitions/acma.rb +0 -24
data/lib/conformist/column.rb
CHANGED
@@ -1,26 +1,23 @@
|
|
1
1
|
module Conformist
|
2
2
|
class Column
|
3
|
-
|
3
|
+
attr_accessor :name, :indexes, :preprocessor
|
4
4
|
|
5
5
|
def initialize name, *indexes, &preprocessor
|
6
|
-
|
6
|
+
self.name = name
|
7
|
+
self.indexes = indexes
|
8
|
+
self.preprocessor = preprocessor
|
7
9
|
end
|
8
10
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
# Returns preprocessed value.
|
20
|
-
def values_in row
|
21
|
-
values = indexes.map { |index| row[index] ? row[index].strip : nil }
|
22
|
-
values = values.first if values.size == 1
|
23
|
-
if preprocessor.respond_to? :call
|
11
|
+
def values_in array
|
12
|
+
values = array.values_at(*indexes).map do |value|
|
13
|
+
if value.respond_to? :strip
|
14
|
+
value.strip
|
15
|
+
else
|
16
|
+
value
|
17
|
+
end
|
18
|
+
end
|
19
|
+
values = values.first if values.size == 1
|
20
|
+
if preprocessor
|
24
21
|
preprocessor.call values
|
25
22
|
else
|
26
23
|
values
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Conformist
|
2
|
+
class HashStruct
|
3
|
+
extend Forwardable
|
4
|
+
|
5
|
+
attr_accessor :attributes
|
6
|
+
|
7
|
+
def_delegators :attributes, :[], :[]=, :fetch, :key?
|
8
|
+
|
9
|
+
def initialize attributes = {}
|
10
|
+
self.attributes = attributes
|
11
|
+
end
|
12
|
+
|
13
|
+
def merge other
|
14
|
+
self.class.new.tap do |instance|
|
15
|
+
instance.attributes = attributes.merge other
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def == other
|
20
|
+
other.class == self.class && attributes == other.attributes
|
21
|
+
end
|
22
|
+
|
23
|
+
protected
|
24
|
+
|
25
|
+
def respond_to_missing? method, include_private
|
26
|
+
key?(method) || super
|
27
|
+
end
|
28
|
+
|
29
|
+
def method_missing method, *args, &block
|
30
|
+
fetch(method) { super }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module Conformist
|
2
|
+
module Schema
|
3
|
+
def self.included base
|
4
|
+
base.send :include, InstanceExtensions
|
5
|
+
base.send :include, Methods
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.extended base
|
9
|
+
base.extend ClassExtensions
|
10
|
+
base.extend Methods
|
11
|
+
end
|
12
|
+
|
13
|
+
module ClassExtensions
|
14
|
+
def inherited base
|
15
|
+
base.builder = builder.dup
|
16
|
+
base.columns = columns.dup
|
17
|
+
end
|
18
|
+
|
19
|
+
def load *args
|
20
|
+
raise "``#{self.class}.load` has been removed, use something like `#{self.class}.conform(file).each(&block)` instead (#{caller.first})"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
module InstanceExtensions
|
25
|
+
def initialize super_schema = nil, &block
|
26
|
+
if super_schema
|
27
|
+
self.builder = super_schema.builder.dup
|
28
|
+
self.columns = super_schema.columns.dup
|
29
|
+
end
|
30
|
+
if block
|
31
|
+
instance_eval &block
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
module Methods
|
37
|
+
def builder
|
38
|
+
@builder ||= Builder
|
39
|
+
end
|
40
|
+
|
41
|
+
def builder= value
|
42
|
+
@builder = value
|
43
|
+
end
|
44
|
+
|
45
|
+
def column *args, &block
|
46
|
+
columns << Column.new(*args, &block)
|
47
|
+
end
|
48
|
+
|
49
|
+
def columns
|
50
|
+
@columns ||= []
|
51
|
+
end
|
52
|
+
|
53
|
+
def columns= value
|
54
|
+
@columns = value
|
55
|
+
end
|
56
|
+
|
57
|
+
def conform enumerables
|
58
|
+
Enumerator.new do |yielder|
|
59
|
+
enumerables.each do |enumerable|
|
60
|
+
yielder.yield builder.call(self, enumerable)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
data/lib/conformist/version.rb
CHANGED
data/test/helper.rb
CHANGED
@@ -1,13 +1,4 @@
|
|
1
|
-
require 'minitest/autorun'
|
2
|
-
|
3
1
|
require 'conformist'
|
4
|
-
require '
|
5
|
-
require 'definitions/fcc'
|
6
|
-
|
7
|
-
def stub_row
|
8
|
-
('a'..'d').to_a
|
9
|
-
end
|
2
|
+
require 'minitest/autorun'
|
10
3
|
|
11
|
-
|
12
|
-
File.expand_path "../fixtures/#{file}", __FILE__
|
13
|
-
end
|
4
|
+
include Conformist
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class BuilderTest < MiniTest::Unit::TestCase
|
4
|
+
def test_call
|
5
|
+
column = MiniTest::Mock.new
|
6
|
+
column.expect :name, :a
|
7
|
+
column.expect :values_in, [1], [Array]
|
8
|
+
definition = MiniTest::Mock.new
|
9
|
+
definition.expect :columns, [column]
|
10
|
+
assert_equal HashStruct.new({:a => [1]}), Builder.call(definition, [])
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class HashStructTest < MiniTest::Unit::TestCase
|
4
|
+
def test_initialize
|
5
|
+
assert_equal({:a => 1}, HashStruct.new({:a => 1}).attributes)
|
6
|
+
assert_empty HashStruct.new.attributes
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_delegates
|
10
|
+
hash = HashStruct.new
|
11
|
+
assert hash.respond_to?(:[])
|
12
|
+
assert hash.respond_to?(:[]=)
|
13
|
+
assert hash.respond_to?(:fetch)
|
14
|
+
assert hash.respond_to?(:key?)
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_equality
|
18
|
+
hash1 = HashStruct.new :a => 1
|
19
|
+
hash2 = HashStruct.new :a => 1
|
20
|
+
hash3 = MiniTest::Mock.new
|
21
|
+
hash3.expect :attributes, {:a => 1}
|
22
|
+
hash3.expect :class, MiniTest::Mock
|
23
|
+
assert_equal hash1, hash2
|
24
|
+
refute_equal hash1, hash3
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_merge
|
28
|
+
hash1 = HashStruct.new
|
29
|
+
hash2 = hash1.merge :a => 1
|
30
|
+
refute_equal hash1.object_id, hash2.object_id
|
31
|
+
refute_equal 1, hash1[:a]
|
32
|
+
assert_equal 1, hash2[:a]
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_readers_with_method_missing
|
36
|
+
hash = HashStruct.new :a => 1, :c_d => 1
|
37
|
+
assert_equal 1, hash.a
|
38
|
+
assert_equal 1, hash.c_d
|
39
|
+
end
|
40
|
+
|
41
|
+
if respond_to? :respond_to_missing? # Compatible with 1.9
|
42
|
+
def test_readers_with_respond_to_missing
|
43
|
+
hash = HashStruct.new :a => 1, :c_d => 1
|
44
|
+
assert hash.respond_to?(:a)
|
45
|
+
assert hash.respond_to?(:c_d)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class SchemaTest < MiniTest::Unit::TestCase
|
4
|
+
def test_initialize_with_instance
|
5
|
+
parent = Class.new { include Schema }.new.tap { |d| d.columns = [0] }
|
6
|
+
child1 = Class.new { include Schema }.new(parent).tap { |d| d.columns << 1 }
|
7
|
+
child2 = Class.new { include Schema }.new(parent).tap { |d| d.columns << 2 }
|
8
|
+
assert_equal [0], parent.columns
|
9
|
+
assert_equal [0, 1], child1.columns
|
10
|
+
assert_equal [0, 2], child2.columns
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_initialize_with_block
|
14
|
+
anonymous = Class.new { include Schema }.new do
|
15
|
+
column :a, 0
|
16
|
+
column :b, 1
|
17
|
+
end
|
18
|
+
assert_equal 2, anonymous.columns.size
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_builder_reader
|
22
|
+
assert_equal Builder, Class.new { extend Schema }.builder
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_builder_writer
|
26
|
+
definition = Class.new { extend Schema }
|
27
|
+
definition.builder = Object
|
28
|
+
assert_equal Object, definition.builder
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_columns_reader
|
32
|
+
assert_empty Class.new { extend Schema }.columns
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_columns_writer
|
36
|
+
definition = Class.new { extend Schema }
|
37
|
+
definition.columns = [1]
|
38
|
+
assert_equal [1], definition.columns
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_column
|
42
|
+
definition = Class.new { extend Schema }
|
43
|
+
definition.column :a, 0
|
44
|
+
definition.column :b, 1
|
45
|
+
assert_equal 2, definition.columns.size
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_conform_returns_enumerable
|
49
|
+
definition = Class.new { extend Schema }
|
50
|
+
assert definition.conform([]).respond_to?(:each)
|
51
|
+
assert definition.conform([]).respond_to?(:map)
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_conform_calls_builders_call_method
|
55
|
+
definition = Class.new { extend Schema }
|
56
|
+
definition.builder = lambda { |definition, value| value }
|
57
|
+
assert_equal [2, 4], definition.conform([1, 2]).map { |value| value * 2 }
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_inheritance
|
61
|
+
parent = Class.new { extend Schema }.tap { |d| d.columns = [0] }
|
62
|
+
child1 = Class.new(parent).tap { |d| d.columns << 1 }
|
63
|
+
child2 = Class.new(parent).tap { |d| d.columns << 2 }
|
64
|
+
assert_equal [0], parent.columns
|
65
|
+
assert_equal [0, 1], child1.columns
|
66
|
+
assert_equal [0, 2], child2.columns
|
67
|
+
end
|
68
|
+
|
69
|
+
def test_load
|
70
|
+
assert_raises RuntimeError do
|
71
|
+
Class.new { extend Schema }.load
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class ConformistTest < MiniTest::Unit::TestCase
|
4
|
+
def test_extended
|
5
|
+
definition = Class.new { extend Conformist }
|
6
|
+
assert definition.respond_to?(:builder)
|
7
|
+
assert definition.respond_to?(:columns)
|
8
|
+
assert definition.respond_to?(:conform)
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_foreach
|
12
|
+
assert_raises RuntimeError do
|
13
|
+
Conformist.foreach
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_new
|
18
|
+
assert Conformist.new.class.include?(Schema)
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'csv'
|
2
|
+
require 'helper'
|
3
|
+
require 'schemas/acma'
|
4
|
+
require 'schemas/fcc'
|
5
|
+
|
6
|
+
class IntegrationTest < MiniTest::Unit::TestCase
|
7
|
+
def open_csv filename, options = {}
|
8
|
+
path = File.expand_path "../../fixtures/#{filename}", __FILE__
|
9
|
+
if CSV.method(:open).arity == -3 # 1.8 CSV
|
10
|
+
CSV.open path, 'r', options[:col_sep]
|
11
|
+
else
|
12
|
+
CSV.open path, options
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_class_with_csv
|
17
|
+
enumerable = ACMA.conform open_csv('acma.csv')
|
18
|
+
last = enumerable.to_a.last
|
19
|
+
assert_equal HashStruct.new(:name=>'CRAFERS', :callsign=>'ADS10', :latitude=>'34 58 57S'), last
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_inherited_class_with_csv
|
23
|
+
enumerable = ACMA::Digital.conform open_csv('acma.csv')
|
24
|
+
last = enumerable.to_a.last
|
25
|
+
assert_equal HashStruct.new(:name=>'CRAFERS', :callsign=>'ADS10', :latitude=>'34 58 57S', :signal_type => 'digital'), last
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_class_with_psv
|
29
|
+
enumerable = FCC.conform open_csv('fcc.txt', :col_sep => '|')
|
30
|
+
last = enumerable.to_a.last
|
31
|
+
assert_equal HashStruct.new(:name => 'LOS ANGELES, CA', :callsign => 'KVTU-LP', :latitude => '34 13 38.00 N', :signtal_type => 'digital'), last
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_instance_with_array_of_arrays
|
35
|
+
data = Array.new.tap do |d|
|
36
|
+
d << ['NSW', 'New South Wales', 'Sydney']
|
37
|
+
d << ['VIC', 'Victoria', 'Melbourne']
|
38
|
+
d << ['QLD', 'Queensland', 'Brisbane']
|
39
|
+
end
|
40
|
+
definition = Conformist.new do
|
41
|
+
column :state, 0, 1 do |values|
|
42
|
+
"#{values.first}, #{values.last}"
|
43
|
+
end
|
44
|
+
column :capital, 2
|
45
|
+
end
|
46
|
+
enumerable = definition.conform data
|
47
|
+
last = enumerable.to_a.last
|
48
|
+
assert_equal HashStruct.new(:state => 'QLD, Queensland', :capital => 'Brisbane'), last
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_inherited_instance_with_array_of_arrays
|
52
|
+
data = Array.new.tap do |d|
|
53
|
+
d << ['NSW', 'New South Wales', 'Sydney']
|
54
|
+
d << ['VIC', 'Victoria', 'Melbourne']
|
55
|
+
d << ['QLD', 'Queensland', 'Brisbane']
|
56
|
+
end
|
57
|
+
parent = Conformist.new do
|
58
|
+
column :state, 0, 1 do |values|
|
59
|
+
"#{values.first}, #{values.last}"
|
60
|
+
end
|
61
|
+
column :capital, 2
|
62
|
+
end
|
63
|
+
child = Conformist.new parent do
|
64
|
+
column :country do
|
65
|
+
'Australia'
|
66
|
+
end
|
67
|
+
end
|
68
|
+
enumerable = child.conform data
|
69
|
+
last = enumerable.to_a.last
|
70
|
+
assert_equal HashStruct.new(:state => 'QLD, Queensland', :capital => 'Brisbane', :country => 'Australia'), last
|
71
|
+
end
|
72
|
+
end
|