efo_nelfo 1.2.0 → 1.3.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1f389f65b4a9eb9dd8dc8c08c7a35d503681d80c
4
- data.tar.gz: 49a45cedbc30524de77097fdcd8e404417358333
3
+ metadata.gz: 64cdaf33e1a391067ac19395f549997d497faa45
4
+ data.tar.gz: 247909b6de770da10400d1ba2ff6ac8ad16b2a2e
5
5
  SHA512:
6
- metadata.gz: f976078206c2b97b86a93919996ba2ba1986120b0ed844833dda3b326947bbef7fd73315f5fd94c2c775160dac8e7f3fc46e7e41bd01980d4e3fa5b5b6351b28
7
- data.tar.gz: e0fecdcd62acf5815e5c6271977948857059fda3bfdcacdc58df828842d2cc2ddc04070bba59f3339ec64bbb6d5dca1e090cf261202325edf15a4b4537ce72af
6
+ metadata.gz: d3dbe7d16a9b78ee13b12a6ee47657481376a9bc73017f1d83caa11e22759f6f3e838d9b0878f7615ecd10b9cfe58af7355716863d215a53cdfbe247b82890a4
7
+ data.tar.gz: 035aaa3183fd806377ccc9ca7bcabb6a6e5fbd0f174d5625ef831ae89fb0e9054b78a5af7c55c58282bf5ff3f41c0d8bdc003bd3432a58d958eb2988f5ad3e2a
data/README.md CHANGED
@@ -2,14 +2,15 @@
2
2
 
3
3
  Gem for parsing and writing EfoNelfo documents.
4
4
 
5
- Supported versions:
5
+ Supported EfoNelfo versions:
6
6
 
7
7
  * 4.0
8
8
 
9
9
  Supported formats:
10
10
 
11
- * Bestilling
12
-
11
+ * Bestilling (BH, BL, BT)
12
+ * Vareformat (VH, VL, VX, VA)
13
+ * Rabatt (RH, RL)
13
14
 
14
15
  ## Usage
15
16
 
@@ -19,8 +20,8 @@ Importing a CSV file:
19
20
 
20
21
  Exporting CSV:
21
22
 
22
- # order = EfoNelfo::V40::Order.new
23
- # order.add EfoNelfo::V40::Order::Line.new item_number: '442', order_number: 'abc'
23
+ # order = EfoNelfo::V40::VH.new
24
+ # order.add EfoNelfo::V40::VL.new item_number: '442', order_number: 'abc'
24
25
  # order.to_csv
25
26
 
26
27
 
@@ -31,7 +32,6 @@ Exporting CSV:
31
32
  * Support more versions
32
33
  * Support XML
33
34
 
34
-
35
35
  ## Resources
36
36
 
37
37
  * http://www.efo.no/Portals/5/docs/ImplementasjonsGuide%20EFONELFO%204.0.pdf
@@ -3,4 +3,5 @@ module EfoNelfo
3
3
  UnsupportedPostType = Class.new(StandardError)
4
4
  DuplicateProperty = Class.new(StandardError)
5
5
  UnknownPropertyOption = Class.new(StandardError)
6
+ InvalidPropertyType = Class.new(StandardError)
6
7
  end
@@ -8,6 +8,10 @@ module EfoNelfo
8
8
  base.send :property, :version, alias: :Versjon, limit: 3, default: base.version
9
9
  end
10
10
 
11
+ def [](index)
12
+ lines[index]
13
+ end
14
+
11
15
  def add(post_type)
12
16
  if has_association? post_type
13
17
  find_association(post_type) << post_type
@@ -18,13 +22,5 @@ module EfoNelfo
18
22
  end
19
23
  end
20
24
 
21
- def to_csv
22
- CSV.generate EfoNelfo::Reader::CSV::CSV_OPTIONS do |csv|
23
- to_a.each do |row|
24
- csv << row unless row.empty?
25
- end
26
- end
27
- end
28
-
29
25
  end
30
26
  end
@@ -1,7 +1,7 @@
1
1
  module EfoNelfo
2
2
 
3
3
  class PostType
4
- include EfoNelfo::Property
4
+ include EfoNelfo::Properties
5
5
  include EfoNelfo::HasMany
6
6
 
7
7
  class << self
@@ -49,6 +49,14 @@ module EfoNelfo
49
49
  self.class.version
50
50
  end
51
51
 
52
+ def to_csv
53
+ CSV.generate EfoNelfo::Reader::CSV::CSV_OPTIONS do |csv|
54
+ to_a.each do |row|
55
+ csv << row unless row.empty?
56
+ end
57
+ end
58
+ end
59
+
52
60
  end
53
61
 
54
62
  end
@@ -0,0 +1,121 @@
1
+ module EfoNelfo
2
+
3
+ module Properties
4
+
5
+ def self.included(base)
6
+ base.send :extend, ClassMethods
7
+ end
8
+
9
+ def initialize_attributes(*args)
10
+ if args && args.first.is_a?(Hash)
11
+ args.first.each do |attr, value|
12
+ send "#{attr}=", value
13
+ end
14
+ end
15
+ end
16
+
17
+ # Returns all properties
18
+ def properties
19
+ @properties ||= initialize_properties
20
+ end
21
+ alias :attributes :properties
22
+
23
+ # Returns true if the given property exists
24
+ def has_property?(name)
25
+ properties.include? name
26
+ end
27
+
28
+ # Returns all property values as array formatted for csv
29
+ def to_a
30
+ properties.values.map &:to_csv
31
+ end
32
+
33
+ private
34
+
35
+ def initialize_properties
36
+ self.class.properties.inject({}) do |hash,(name,options)|
37
+ hash[name] = EfoNelfo::Property.new(name, options)
38
+ hash
39
+ end
40
+ end
41
+
42
+ module ClassMethods
43
+ # Creates an attribute with given name.
44
+ #
45
+ # Options
46
+ # - type String, Integer etc. Default is String
47
+ # - required whether attribute is required. Default is false
48
+ # - limit Length the attribute can be. Default is nil
49
+ # - alias Norwegian alias name for the attribute
50
+ #
51
+ def property(name, options={})
52
+ options = {
53
+ type: :string,
54
+ required: false,
55
+ }.update options
56
+
57
+ name = name.to_sym
58
+ alias_name = options.delete(:alias)
59
+
60
+ # ensure all options are valid
61
+ EfoNelfo::Property.validate_options! options
62
+
63
+ # ensure property is unique
64
+ raise EfoNelfo::DuplicateProperty if properties.has_key?(name)
65
+
66
+ # setup getters and setters
67
+ create_reader_for(name, options)
68
+ create_setter_for(name, options) unless options[:read_only]
69
+ create_question_for(name) if options[:type] == :boolean
70
+ create_alias_for(name, alias_name, options) if alias_name
71
+
72
+ properties[name] = options
73
+ end
74
+
75
+ # Returns all properties defined for the class
76
+ def properties
77
+ @_properties ||= {}
78
+ end
79
+
80
+ private
81
+
82
+ # Creates an attribute accessor for the property
83
+ def create_reader_for(name, options)
84
+ define_method name do
85
+ attributes[name].value
86
+ end
87
+ end
88
+
89
+ # Creates an attribute setter for the property
90
+ def create_setter_for(name, options)
91
+ define_method "#{name}=" do |value|
92
+ attributes[name].value = value
93
+ end
94
+ end
95
+
96
+ # Creates a name? accessor for the property
97
+ # Only applies to boolean types
98
+ def create_question_for(name)
99
+ define_method "#{name}?" do
100
+ attributes[name].value == true
101
+ end
102
+ end
103
+
104
+ # Create an alias getter/setter for the property
105
+ def create_alias_for(name, alias_name, options)
106
+ define_method(alias_name) do
107
+ send name
108
+ end
109
+
110
+ unless options[:read_only]
111
+ define_method("#{alias_name}=") do |val|
112
+ send "#{name}=", val
113
+ end
114
+ end
115
+ end
116
+
117
+ end
118
+
119
+ end
120
+
121
+ end
@@ -1,155 +1,94 @@
1
- module EfoNelfo
1
+ using EfoNelfo::PropertyTypes
2
2
 
3
- module Property
3
+ module EfoNelfo
4
4
 
5
- def self.included(base)
6
- base.send :extend, ClassMethods
7
- end
5
+ class Property
6
+ VALID_OPTIONS = [:type, :required, :limit, :read_only, :default, :decimals]
7
+ VALID_TYPES = [:string, :integer, :boolean, :date]
8
8
 
9
- def initialize_attributes(*args)
10
- if args && args.first.is_a?(Hash)
11
- args.first.each do |attr, value|
12
- send "#{attr}=", value
13
- end
14
- end
15
- end
16
-
17
- def attributes
18
- @attributes ||= initialize_default_attributes
19
- end
9
+ attr_reader :name, :options, :value
20
10
 
21
- def properties
22
- self.class.properties
11
+ def self.validate_options!(options)
12
+ invalid_options = options.keys - VALID_OPTIONS
13
+ raise EfoNelfo::UnknownPropertyOption.new("Invalid options: #{invalid_options.join(',')}") if invalid_options.any?
14
+ raise EfoNelfo::InvalidPropertyType.new("Valid types are #{VALID_TYPES.join(',')}") unless VALID_TYPES.include?(options[:type])
23
15
  end
24
16
 
25
- def has_property?(property)
26
- properties.include? property
27
- end
17
+ def initialize(name, defaults={})
28
18
 
29
- def to_a
30
- properties.keys.map { |prop| formatted_for_csv(prop) }
31
- end
19
+ options = {
20
+ type: :string,
21
+ required: false,
22
+ default: nil,
23
+ read_only: false,
24
+ decimals: nil,
25
+ limit: 100
26
+ }
27
+ options.update(defaults) if defaults.is_a?(Hash)
32
28
 
33
- private
29
+ self.class.validate_options! options
34
30
 
35
- def initialize_default_attributes
36
- properties.inject({}) { |h,(name,options)| h[name] = options[:default]; h }
31
+ @name = name
32
+ @options = options
33
+ @value = options[:default]
37
34
  end
38
35
 
39
- def format_value(value, type)
40
- case type
41
- when :integer
42
- value.nil? ? nil : value.to_i
43
- when :date
44
- if value.nil?
45
- nil
46
- elsif value.kind_of? String
47
- Date.parse value
36
+ # Assign a value to the property
37
+ # The value is converted to whatever specified by options[:type]
38
+ # Examples:
39
+ # (boolean) value = 'J' # => true
40
+ # (boolean) value = false # => false
41
+ # (date) value = '20120101' # => Date.new(2012,1,1)
42
+ # (integer) value = '2' # => 2
43
+ # (string) value = 'foo' # => 'foo'
44
+ def value=(new_value)
45
+ return nil if readonly?
46
+
47
+ @value = case
48
+ when boolean?
49
+ new_value.nil? || new_value == true || new_value == 'J' || new_value == 'j' || new_value == '' ? true : false
50
+ when date?
51
+ new_value.is_a?(Date) ? new_value : Date.parse(new_value) rescue nil
52
+ when integer?
53
+ new_value.nil? ? nil : new_value.to_i
48
54
  else
49
- value
50
- end
51
- when :boolean
52
- value.nil? || value == true || value == 'J' || value == '' ? true : false
53
- else
54
- value
55
+ new_value
55
56
  end
56
57
  end
57
58
 
58
- # Format value for given attribute
59
- def formatted_for_csv(attr)
60
-
61
- if respond_to?("format_#{attr}")
62
- value = send "format_#{attr}"
63
- else
64
- value = send attr
65
-
66
- type = properties[attr][:type]
67
-
68
- value = case type
69
- when :date
70
- value ? value.strftime("%Y%m%d") : nil
71
- when :boolean
72
- value == true ? "J" : nil
73
- else
74
- value
75
- end
76
- end
77
- value.respond_to?(:encode) ? value.encode(Encoding::ISO_8859_1) : value
59
+ # returns formatted value suitable for csv output
60
+ def to_csv
61
+ output = value.to_csv
78
62
  end
79
63
 
80
- module ClassMethods
81
- # Creates an attribute with given name.
82
- #
83
- # Options
84
- # - type String, Integer etc. Default is String
85
- # - required whether attribute is required. Default is false
86
- # - limit Length the attribute can be. Default is nil
87
- # - alias Norwegian alias name for the attribute
88
- #
89
- def property(name, options={})
90
-
91
- options = {
92
- type: :string,
93
- required: false,
94
- }.update options
95
-
96
- name = name.to_sym
97
-
98
- # ensure all options are valid
99
- valid_options = [:type, :required, :limit, :read_only, :alias, :default]
100
- invalid_options = options.keys - valid_options
101
- raise EfoNelfo::UnknownPropertyOption.new("Invalid option for #{name}: #{invalid_options.join(',')}") if invalid_options.any?
102
-
103
- # Store property info in @properties
104
- raise EfoNelfo::DuplicateProperty if properties.has_key?(name)
105
- properties[name] = options
106
-
107
- create_reader_for(name, options)
108
- create_setter_for(name, options) unless options[:read_only]
109
- create_question_for(name) if options[:type] == :boolean
110
- create_alias_for(name, options) if options[:alias]
111
- end
112
-
113
- # Returns all properties
114
- def properties
115
- @_properties ||= {}
116
- end
117
-
118
- private
119
-
120
- # Creates an attribute accessor for name
121
- def create_reader_for(name, options)
122
- define_method name do
123
- attributes[name]
124
- end
125
- end
126
-
127
- # Creates an attribute setter for name
128
- def create_setter_for(name, options)
129
- define_method "#{name}=" do |value|
130
- attributes[name] = format_value(value, options[:type])
131
- end
132
- end
64
+ # Returns integer to floating point based on specified decimals
65
+ # Example:
66
+ # If decimal is set to 4, and the value is 4000
67
+ # then to_f returns 0.4
68
+ def to_f
69
+ return nil if value.nil?
70
+ value.to_f.with_decimals decimals
71
+ end
72
+ alias :to_decimal :to_f
133
73
 
134
- # Creates a name? accessor
135
- def create_question_for(name)
136
- define_method "#{name}?" do
137
- attributes[name] == true
138
- end
139
- end
74
+ # Returns true if the property is read only
75
+ def readonly?
76
+ options[:read_only]
77
+ end
140
78
 
141
- def create_alias_for(name, options)
142
- define_method(options[:alias]) do
143
- send name
144
- end
79
+ # Returns true if the property is required
80
+ # Note: this is not in use yet
81
+ def required?
82
+ options[:required]
83
+ end
145
84
 
146
- unless options[:read_only]
147
- define_method("#{options[:alias]}=") do |val|
148
- send "#{name}=", val
149
- end
150
- end
151
- end
85
+ def boolean?; type == :boolean; end
86
+ def date?; type == :date; end
87
+ def integer?; type == :integer; end
88
+ def string?; type == :string; end
152
89
 
90
+ def method_missing(*args)
91
+ options.has_key?(args.first) ? options[args.first] : super
153
92
  end
154
93
 
155
94
  end
@@ -0,0 +1,47 @@
1
+ module EfoNelfo
2
+ module PropertyTypes
3
+ refine NilClass do
4
+ def to_csv
5
+ nil
6
+ end
7
+ end
8
+
9
+ refine TrueClass do
10
+ def to_csv
11
+ 'J'
12
+ end
13
+ end
14
+
15
+ refine FalseClass do
16
+ def to_csv
17
+ 'N'
18
+ end
19
+ end
20
+
21
+ refine String do
22
+ def to_csv
23
+ to_s.encode EfoNelfo::Reader::CSV::ENCODING
24
+ end
25
+ end
26
+
27
+ refine Date do
28
+ def to_csv
29
+ strftime("%Y%m%d")
30
+ end
31
+ end
32
+
33
+ refine Fixnum do
34
+ def to_csv
35
+ to_i
36
+ end
37
+ end
38
+
39
+ refine Float do
40
+ def with_decimals(decimals)
41
+ decimals.nil? ? self : self * (1.0/10**decimals.to_i)
42
+ end
43
+ end
44
+
45
+ end
46
+
47
+ end
@@ -4,6 +4,8 @@ module EfoNelfo
4
4
  module Reader
5
5
 
6
6
  class CSV
7
+ ENCODING = Encoding::ISO_8859_1
8
+
7
9
  CSV_OPTIONS = {
8
10
  col_sep: ';',
9
11
  headers: false,
@@ -17,7 +19,7 @@ module EfoNelfo
17
19
 
18
20
  def initialize(options)
19
21
  if options[:filename]
20
- @data = File.read(options[:filename], encoding: Encoding::ISO_8859_1)
22
+ @data = File.read(options[:filename], encoding: ENCODING)
21
23
  else
22
24
  @data = options[:data]
23
25
  end
@@ -33,6 +33,23 @@ module EfoNelfo
33
33
  info.map(&:image).compact
34
34
  end
35
35
 
36
+ [ :weight, :dimension, :volume, :fdv, :hms ].each do |key|
37
+ define_method key do
38
+ pick key
39
+ end
40
+ end
41
+
42
+ # Returns array of urls extracted from VX lines
43
+ def urls
44
+ info.map(&:value).select { |u| u.match %r{\Ahttps?://} }
45
+ end
46
+
47
+ private
48
+
49
+ def pick(method)
50
+ vx = info.select(&"#{method}?".to_sym).compact.first
51
+ vx && vx.send(method)
52
+ end
36
53
  end
37
54
  end
38
55
  end
@@ -5,12 +5,25 @@ module EfoNelfo
5
5
  property :field, alias: :FeltId, limit: 10, required: true
6
6
  property :value, alias: :Verdi, limit: 70, required: true
7
7
 
8
- def image?
9
- field == 'BILDE'
10
- end
8
+ FIELD_MAPPINGS = {
9
+ image: "BILDE",
10
+ weight: "VEKT",
11
+ dimension: "DIMENSJON",
12
+ fdv: "FDV",
13
+ hms: "HMS",
14
+ volume: "VOLUM"
15
+ }
16
+
17
+ FIELD_MAPPINGS.each do |key, field_value|
18
+ # Add methods like def image?; field == 'BILDE'; end
19
+ define_method "#{key}?" do
20
+ field == field_value
21
+ end
11
22
 
12
- def image
13
- value if image?
23
+ # Add methods like: def image; value if image?; end
24
+ define_method "#{key}" do
25
+ self.value if send("#{key}?")
26
+ end
14
27
  end
15
28
 
16
29
  end
@@ -1,3 +1,3 @@
1
1
  module EfoNelfo
2
- VERSION = "1.2.0"
2
+ VERSION = "1.3.0"
3
3
  end
data/lib/efo_nelfo.rb CHANGED
@@ -3,7 +3,9 @@
3
3
  # Common stuff
4
4
  require 'efo_nelfo/version'
5
5
  require 'efo_nelfo/errors'
6
+ require 'efo_nelfo/property_types'
6
7
  require 'efo_nelfo/property'
8
+ require 'efo_nelfo/properties'
7
9
  require 'efo_nelfo/collection'
8
10
  require 'efo_nelfo/has_many'
9
11
  require 'efo_nelfo/post_type'
@@ -29,7 +29,7 @@ describe EfoNelfo::V40::BH do
29
29
  csv.must_match /;EFONELFO;/
30
30
  csv.must_match /;4.0;/
31
31
  csv.must_match /;123;/
32
- csv.must_match /;600;/
32
+ csv.must_match /;456;/
33
33
  csv.must_match /;J;/ # for fixing sublime syntax highlighting: /
34
34
  end
35
35
 
@@ -4,15 +4,15 @@ describe EfoNelfo::PostType do
4
4
 
5
5
  module EfoNelfo
6
6
  module V21
7
- class Foo < EfoNelfo::PostType
8
- property :post_type
9
- property :version
7
+ class AB < EfoNelfo::PostType
8
+ property :post_type, default: 'AB'
9
+ property :version, default: '21'
10
10
  property :seller_id
11
- has_many :lines, :post_type => "Bar"
11
+ has_many :lines, :post_type => "XY"
12
12
  end
13
13
 
14
- class Bar < EfoNelfo::PostType
15
- property :post_type
14
+ class XY < EfoNelfo::PostType
15
+ property :post_type, default: 'XY'
16
16
  property :order_number
17
17
  end
18
18
  end
@@ -21,27 +21,35 @@ describe EfoNelfo::PostType do
21
21
  describe "versioning" do
22
22
 
23
23
  it ".version_from_class returns the version" do
24
- EfoNelfo::V21::Foo.version_from_class.must_equal "21"
24
+ EfoNelfo::V21::AB.version_from_class.must_equal "21"
25
25
  end
26
26
 
27
27
  it ".version returns the version as a number" do
28
- EfoNelfo::V21::Foo.version.must_equal "2.1"
28
+ EfoNelfo::V21::AB.version.must_equal "2.1"
29
29
  end
30
30
 
31
31
  it ".version_to_namespace converts version to module name" do
32
- EfoNelfo::V21::Foo.version_to_namespace("2.1").must_equal "21"
32
+ EfoNelfo::V21::AB.version_to_namespace("2.1").must_equal "21"
33
33
  end
34
34
 
35
35
  end
36
36
 
37
+ describe "to_csv" do
38
+ it "converts the posttype to csv" do
39
+ pt = EfoNelfo::V21::AB.new seller_id: '123'
40
+ pt.lines << EfoNelfo::V21::XY.new(order_number: '41')
41
+ pt.to_csv.must_match /AB;21;123.+XY;41/m
42
+ end
43
+ end
44
+
37
45
  describe ".from" do
38
46
  let(:hash) {
39
47
  {
40
- post_type: "Foo",
48
+ post_type: "AB",
41
49
  version: "2.1",
42
50
  seller_id: "123",
43
51
  lines: [
44
- { post_type: "Bar", order_number: "666-2" }
52
+ { post_type: "XY", order_number: "666-2" }
45
53
  ]
46
54
  }
47
55
  }
@@ -49,7 +57,7 @@ describe EfoNelfo::PostType do
49
57
  let(:pt) { EfoNelfo::PostType.from hash }
50
58
 
51
59
  it "converts the hash into a valid posttype" do
52
- pt.must_be_instance_of EfoNelfo::V21::Foo
60
+ pt.must_be_instance_of EfoNelfo::V21::AB
53
61
  pt.seller_id.must_equal "123"
54
62
  end
55
63
 
@@ -0,0 +1,149 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ describe EfoNelfo::Properties do
5
+
6
+ class Foo
7
+ include EfoNelfo::Properties
8
+ property :foo, alias: "Føbar", limit: 3, default: 'I am foo'
9
+ property :bar
10
+ property :date, type: :date
11
+ property :number, type: :integer
12
+ property :doable, type: :boolean, default: false
13
+ property :immutable, read_only: true, default: 'NO'
14
+ end
15
+
16
+ let(:obj) { Foo.new }
17
+
18
+ it "raises an error if trying to add duplicate property" do
19
+ lambda {
20
+ class Bar
21
+ include EfoNelfo::Properties
22
+ property :foo
23
+ property :foo
24
+ end
25
+ }.must_raise EfoNelfo::DuplicateProperty
26
+ end
27
+
28
+ it "raises an error if trying to assign an unsupported option" do
29
+ lambda {
30
+ class Bar
31
+ include EfoNelfo::Properties
32
+ property :lame, funny: true
33
+ end
34
+ }.must_raise EfoNelfo::UnknownPropertyOption
35
+ end
36
+
37
+ it "adds a getter method for :foo" do
38
+ obj.foo.must_equal 'I am foo'
39
+ end
40
+
41
+ it "adds a setter method for :foo" do
42
+ obj.foo = 'Test'
43
+ obj.foo.must_equal 'Test'
44
+ end
45
+
46
+ it "adds a question method for boolean types" do
47
+ obj.doable?.must_equal false
48
+ obj.doable = true
49
+ obj.doable?.must_equal true
50
+ end
51
+
52
+ it "won't do any encoding conversions" do
53
+ obj.foo = 'Sjøhest'
54
+ obj.foo.encoding.name.must_equal 'UTF-8'
55
+ end
56
+
57
+ it "adds an alias getter and setter for foo" do
58
+ obj.foo = 'Test'
59
+ obj.Føbar.must_equal 'Test'
60
+ obj.Føbar = 'Hacked'
61
+ obj.Føbar.must_equal 'Hacked'
62
+ obj.foo.must_equal 'Hacked'
63
+ end
64
+
65
+ it "assigns default values" do
66
+ obj.bar.must_be_nil
67
+ obj.foo.must_equal 'I am foo'
68
+ end
69
+
70
+ it "can be assigned nil values" do
71
+ obj.number = nil
72
+ obj.number.must_be_nil
73
+ end
74
+
75
+ it "readonly attributes cannot be changed" do
76
+ lambda { obj.immutable = 'test' }.must_raise NoMethodError
77
+ obj.immutable.must_equal 'NO'
78
+ end
79
+
80
+ describe "property types" do
81
+ describe ":date" do
82
+ it "converts strings to dates" do
83
+ obj.date = "20100504"
84
+ obj.date.must_be_kind_of Date
85
+ end
86
+
87
+ it "accepts a Date" do
88
+ obj.date = Date.new 2010, 5, 4
89
+ obj.date.must_be_kind_of Date
90
+ end
91
+
92
+ end
93
+
94
+ it "handles :integer" do
95
+ obj.number = "2"
96
+ obj.number.must_equal 2
97
+ end
98
+
99
+ describe ":boolean" do
100
+ it "returns true when assigning blank string" do
101
+ obj.doable = ''
102
+ obj.doable?.must_equal true
103
+ end
104
+
105
+ it "returns true when assigning nil" do
106
+ obj.doable = nil
107
+ obj.doable?.must_equal true
108
+ end
109
+
110
+ it "returns true when assigning 'J'" do
111
+ obj.doable = 'J'
112
+ obj.doable?.must_equal true
113
+ end
114
+
115
+ it "returns false" do
116
+ obj.doable = false
117
+ obj.doable?.must_equal false
118
+
119
+ obj.doable = 'N'
120
+ obj.doable?.must_equal false
121
+ end
122
+
123
+ end
124
+
125
+ end
126
+
127
+ it ".properties contains hash of options" do
128
+ Foo.properties[:foo].must_be_kind_of Hash
129
+ end
130
+
131
+ it "#properties contains hash of EfoNelfo::Property objects" do
132
+ Foo.new.properties[:foo].must_be_kind_of EfoNelfo::Property
133
+ end
134
+
135
+ it "#has_property? returns true when property exists" do
136
+ obj.has_property?(:foo).must_equal true
137
+ obj.has_property?(:bullshit).must_equal false
138
+ end
139
+
140
+ describe "#to_a" do
141
+ it "returns array of all attributes in correct order" do
142
+ obj.date = Date.new 2012, 5, 30
143
+ obj.number = 3
144
+ obj.doable = true
145
+ obj.to_a.must_equal ["I am foo", nil, "20120530", 3, "J", "NO"]
146
+ end
147
+ end
148
+
149
+ end
@@ -1,144 +1,193 @@
1
- # encoding: utf-8
2
1
  require 'spec_helper'
3
2
 
4
3
  describe EfoNelfo::Property do
5
-
6
- class Foo
7
- include EfoNelfo::Property
8
- property :foo, alias: "Føbar", limit: 3, default: 'I am foo'
9
- property :bar
10
- property :date, type: :date
11
- property :number, type: :integer
12
- property :doable, type: :boolean, default: false
13
- property :immutable, read_only: true, default: 'NO'
4
+ subject { EfoNelfo::Property.new :foo, options }
5
+ let(:options) { nil }
6
+
7
+ describe "initializing with options" do
8
+ let(:options) { { type: :integer, limit: 15, decimals: 4, default: 5, required: true } }
9
+
10
+ it { subject.name.must_equal :foo }
11
+ it { subject.default.must_equal 5 }
12
+ it { subject.limit.must_equal 15 }
13
+ it { subject.type.must_equal :integer }
14
+ it { subject.required.must_equal true }
15
+ it { subject.decimals.must_equal 4 }
16
+ it { subject.value.must_equal 5 }
17
+
18
+ it "raises an error when assigning to unknown type" do
19
+ lambda {
20
+ EfoNelfo::Property.new :name, type: :get_lost
21
+ }.must_raise EfoNelfo::InvalidPropertyType
22
+ end
14
23
  end
15
24
 
16
- let(:obj) { Foo.new }
25
+ describe "initializing without options" do
26
+ let(:options) { nil }
17
27
 
18
- it "raises an error if trying to add duplicate property" do
19
- lambda {
20
- class Bar
21
- include EfoNelfo::Property
22
- property :foo
23
- property :foo
24
- end
25
- }.must_raise EfoNelfo::DuplicateProperty
28
+ it { subject.name.must_equal :foo }
29
+ it { subject.default.must_be_nil }
30
+ it { subject.limit.must_equal 100 }
31
+ it { subject.type.must_equal :string }
32
+ it { subject.required.must_equal false }
33
+ it { subject.decimals.must_be_nil }
34
+ it { subject.value.must_be_nil }
26
35
  end
27
36
 
28
- it "raises an error if trying to assign an unsupported option" do
29
- lambda {
30
- class Bar
31
- include EfoNelfo::Property
32
- property :lame, funny: true
33
- end
34
- }.must_raise EfoNelfo::UnknownPropertyOption
35
- end
37
+ describe "setter" do
38
+ let(:property) { EfoNelfo::Property.new :name, type: type }
36
39
 
37
- it "adds a getter and setter for foo" do
38
- obj.foo = 'Test'
39
- obj.foo.must_equal 'Test'
40
- end
40
+ describe "when type is string" do
41
+ let(:type) { :string }
42
+ it "is just a string" do
43
+ property.value = 'New value'
44
+ property.value.must_equal 'New value'
45
+ end
46
+ end
41
47
 
42
- it "won't do any encoding conversions" do
43
- obj.foo = 'Sjøhest'
44
- obj.foo.encoding.name.must_equal 'UTF-8'
45
- end
48
+ describe "when type is boolean" do
49
+ let(:type) { :boolean }
46
50
 
47
- it "adds an alias getter and setter for foo" do
48
- obj.foo = 'Test'
49
- obj.Føbar.must_equal 'Test'
50
- obj.Føbar = 'Hacked'
51
- obj.Føbar.must_equal 'Hacked'
52
- obj.foo.must_equal 'Hacked'
53
- end
51
+ it "accepts boolean" do
52
+ property.value = true
53
+ property.value.must_equal true
54
54
 
55
- it "assigns default values" do
56
- obj.bar.must_be_nil
57
- obj.foo.must_equal 'I am foo'
58
- end
55
+ property.value = false
56
+ property.value.must_equal false
57
+ end
59
58
 
60
- it "can be assigned nil values" do
61
- obj.number = nil
62
- obj.number.must_be_nil
63
- end
59
+ it "accepts J/N" do
60
+ property.value = 'J'
61
+ property.value.must_equal true
64
62
 
65
- it "readonly attributes cannot be changed" do
66
- lambda { obj.immutable = 'test' }.must_raise NoMethodError
67
- obj.immutable.must_equal 'NO'
68
- end
63
+ property.value = 'j'
64
+ property.value.must_equal true
69
65
 
70
- describe "property types" do
71
- describe ":date" do
72
- it "converts strings to dates" do
73
- obj.date = "20100504"
74
- obj.date.must_be_kind_of Date
66
+ property.value = 'N'
67
+ property.value.must_equal false
75
68
  end
69
+ end
70
+
71
+ describe "when type is date" do
72
+ let(:type) { :date }
76
73
 
77
74
  it "accepts a Date" do
78
- obj.date = Date.new 2010, 5, 4
79
- obj.date.must_be_kind_of Date
75
+ property.value = Date.new(2013, 11, 21)
76
+ property.value.must_equal Date.new(2013, 11, 21)
80
77
  end
81
78
 
82
- end
79
+ it "accepts a string" do
80
+ property.value = "20131121"
81
+ property.value.must_equal Date.new(2013, 11, 21)
82
+ end
83
+
84
+ it "invalid dates becomes nil" do
85
+ property.value = 'invalid date'
86
+ property.value.must_be_nil
87
+ end
83
88
 
84
- it "handles :integer" do
85
- obj.number = "2"
86
- obj.number.must_equal 2
87
89
  end
88
90
 
89
- describe ":boolean" do
90
- it "returns true when assigning blank string" do
91
- obj.doable = ''
92
- obj.doable?.must_equal true
93
- end
91
+ describe "when type is integer" do
92
+ let(:type) { :integer }
94
93
 
95
- it "returns true when assigning nil" do
96
- obj.doable = nil
97
- obj.doable?.must_equal true
94
+ it "accepts a number" do
95
+ property.value = '2'
96
+ property.value.must_equal 2
98
97
  end
99
98
 
100
- it "returns true when assigning 'J'" do
101
- obj.doable = 'J'
102
- obj.doable?.must_equal true
99
+ it "floats are rounded" do
100
+ property.value = 200.5
101
+ property.value.must_equal 200
103
102
  end
104
103
 
105
- it "returns false" do
106
- obj.doable = false
107
- obj.doable?.must_equal false
108
-
109
- obj.doable = 'N'
110
- obj.doable?.must_equal false
104
+ it "nil becomes nil" do
105
+ property.value = nil
106
+ property.value.must_be_nil
111
107
  end
112
108
 
113
109
  end
114
110
 
111
+ end
115
112
 
113
+ it "#value= is ignored if the property is readonly" do
114
+ prop = EfoNelfo::Property.new(:name, read_only: true, default: 'untouchable')
115
+ prop.value = 'hey'
116
+ prop.value.must_equal 'untouchable'
116
117
  end
117
118
 
118
- describe ".properties" do
119
- let(:props) { Foo.properties }
119
+ it "#value= converts booleans to proper value" do
120
+ prop = EfoNelfo::Property.new(:name, type: :boolean)
121
+ prop.value = 'J'
122
+ prop.value.must_equal true
120
123
 
121
- it "includes the properties" do
122
- props[:foo].must_be_kind_of Hash
123
- end
124
+ prop.value = 'N'
125
+ prop.value.must_equal false
126
+ end
124
127
 
125
- it "includes the property options" do
126
- props[:foo][:limit].must_equal 3
127
- end
128
+ it "#to_f returns the value with decimals" do
129
+ prop = EfoNelfo::Property.new(:name, decimals: 4)
130
+ prop.value = 4000
131
+ prop.to_f.must_equal 0.4
132
+ end
133
+
134
+ it "#to_f doesn't fail when decimals is nil" do
135
+ subject.value = "100"
136
+ subject.to_f.must_equal 100.0
128
137
  end
129
138
 
130
- it "#has_property? returns true when property exists" do
131
- obj.has_property?(:foo).must_equal true
132
- obj.has_property?(:bullshit).must_equal false
139
+ it "#to_f returns nil when value is NaN" do
140
+ subject.value = nil
141
+ subject.to_f.must_be_nil
133
142
  end
134
143
 
135
- describe "#to_a" do
136
- it "returns array of all attributes in correct order" do
137
- obj.date = Date.new 2012, 5, 30
138
- obj.number = 3
139
- obj.doable = true
140
- obj.to_a.must_equal ["I am foo", nil, "20120530", 3, "J", "NO"]
144
+ describe "#to_csv" do
145
+ def make_property(value, options={})
146
+ p = EfoNelfo::Property.new :name, options.merge(type: type)
147
+ p.value = value
148
+ p.to_csv
149
+ end
150
+
151
+ describe "type is :string" do
152
+ let(:type) { :string }
153
+ it { make_property("A string").must_equal "A string" }
154
+ it { make_property(nil).must_equal nil }
155
+ it { make_property("Sjøhest").encoding.must_equal Encoding::ISO_8859_1 }
141
156
  end
157
+
158
+ describe "type is :integer" do
159
+ let(:type) { :integer }
160
+ it { make_property(2).must_equal 2 }
161
+ it { make_property(100, type: :integer, decimals: 4).must_equal 100 }
162
+ it { make_property(nil, type: :integer, decimals: 2).must_equal nil }
163
+ end
164
+
165
+ describe "type is :boolean" do
166
+ let(:type) { :boolean }
167
+ it { make_property(true).must_equal 'J' }
168
+ it { make_property(false).must_equal 'N' }
169
+ it { make_property(nil).must_equal 'J' }
170
+ end
171
+
172
+ describe "type is :date" do
173
+ let(:type) { :date }
174
+ it { make_property(Date.new(2013, 11, 21)).must_equal '20131121' }
175
+ it { make_property("20131121").must_equal "20131121" }
176
+ it { make_property("not a date").must_be_nil }
177
+ it { make_property(nil).must_be_nil }
178
+ end
179
+
142
180
  end
143
181
 
182
+ it "raises error when initializing with unknown make_property" do
183
+ lambda {
184
+ EfoNelfo::Property.new :name, { foo: 'illegal' }
185
+ }.must_raise EfoNelfo::UnknownPropertyOption
186
+ end
187
+
188
+ it ".validate_options! raises error when passing invalid options" do
189
+ lambda {
190
+ EfoNelfo::Property.validate_options! foo: 'bar'
191
+ }.must_raise EfoNelfo::UnknownPropertyOption
192
+ end
144
193
  end
data/spec/vl_spec.rb CHANGED
@@ -4,6 +4,42 @@ describe EfoNelfo::V40::VL do
4
4
 
5
5
  let(:vl) { EfoNelfo::V40::VL.new }
6
6
 
7
+ it "#weight returns the weight in gram" do
8
+ vl.info << { field: 'VEKT', value: 250 }
9
+ vl.weight.must_equal 250
10
+ end
11
+
12
+ it "#weight returns nil when weight is not present" do
13
+ vl.weight.must_be_nil
14
+ end
15
+
16
+ it "#dimension returns nil when dimension isn't present" do
17
+ vl.dimension.must_be_nil
18
+ end
19
+
20
+ it "#dimension returns the dimension of the object" do
21
+ vl.info << { field: 'VEKT', value: '10' }
22
+ vl.info << { field: 'DIMENSJON', value: '2x10x20' }
23
+ vl.dimension.must_equal '2x10x20'
24
+ end
25
+
26
+ it "#volume returns the volume" do
27
+ vl.info << { field: 'VOLUM', value: '1'}
28
+ vl.info << { field: 'DIMENSJON', value: '2x10x20' }
29
+ vl.volume.must_equal '1'
30
+ end
31
+
32
+ it "#volume returns nil when object doesn't have volume" do
33
+ vl.volume.must_be_nil
34
+ end
35
+
36
+ it "#urls returns list of vx lines containing a url" do
37
+ vl.info << { field: 'FOO', value: 'http://localhost/one?two'}
38
+ vl.info << { field: 'BAR', value: 'http://foo.example.com/one/two/three'}
39
+ vl.info << { field: 'FOO', value: 'http-not-a-url' }
40
+ vl.urls.must_equal [ 'http://localhost/one?two', 'http://foo.example.com/one/two/three']
41
+ end
42
+
7
43
  describe "#images" do
8
44
  it "returns nil when there are no images available" do
9
45
  vl.images.must_be_empty
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: efo_nelfo
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gudleik Rasch
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-06-21 00:00:00.000000000 Z
11
+ date: 2013-07-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -128,7 +128,9 @@ files:
128
128
  - lib/efo_nelfo/has_many.rb
129
129
  - lib/efo_nelfo/post_head_type.rb
130
130
  - lib/efo_nelfo/post_type.rb
131
+ - lib/efo_nelfo/properties.rb
131
132
  - lib/efo_nelfo/property.rb
133
+ - lib/efo_nelfo/property_types.rb
132
134
  - lib/efo_nelfo/reader/csv.rb
133
135
  - lib/efo_nelfo/v40/bh.rb
134
136
  - lib/efo_nelfo/v40/bl.rb
@@ -144,6 +146,7 @@ files:
144
146
  - spec/csv_writer_spec.rb
145
147
  - spec/efo_nelfo_spec.rb
146
148
  - spec/post_type_spec.rb
149
+ - spec/properties_spec.rb
147
150
  - spec/property_spec.rb
148
151
  - spec/samples/B028579.594.csv
149
152
  - spec/samples/B028579.595.csv
@@ -179,7 +182,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
179
182
  version: '0'
180
183
  requirements: []
181
184
  rubyforge_project:
182
- rubygems_version: 2.0.2
185
+ rubygems_version: 2.0.3
183
186
  signing_key:
184
187
  specification_version: 4
185
188
  summary: Parser for EFONELFO format
@@ -188,6 +191,7 @@ test_files:
188
191
  - spec/csv_writer_spec.rb
189
192
  - spec/efo_nelfo_spec.rb
190
193
  - spec/post_type_spec.rb
194
+ - spec/properties_spec.rb
191
195
  - spec/property_spec.rb
192
196
  - spec/samples/B028579.594.csv
193
197
  - spec/samples/B028579.595.csv