efo_nelfo 1.2.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
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