foraneus 0.0.1 → 0.0.2

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 ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ YjUyZWQ0OTM3OTA4MWE0MzBlNmEyZmUzYWI5ZGM2ZTdlYzBiMTJhOQ==
5
+ data.tar.gz: !binary |-
6
+ NTcyZjkzYjM3NWE3N2QzYjQ3MzYyNjMyOGQ3ZTBiZjE1Mjc5Y2VmOQ==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ ZTMzMzNhYjg4MmJkYTZkODhmZTllYTgwN2ZhZDQ0NjA5OTM1NWUxNDU5YjA5
10
+ ZjQ5YzY1NjZhMDc4OThlNmJkN2YxNWE4MDQ0MzQ5YjBjMjMwODQ1MTczZTJl
11
+ NjExNmI0OWQyMWNlOTNmYjA5NzE0YzUwOWViYjQwZWY2NGU4Mzk=
12
+ data.tar.gz: !binary |-
13
+ ZjllNzU3NzQxNWUzZGEzOTk2OWMwNWUwZTY1OGQ4NTkxYWNiZWI1Mjg1N2Jh
14
+ YzMxZjc3YTI1OGM1MWViNzI4ZDg2ZTdjYzYxOWE0YTM1Yzc0ODMxNDI2MDJh
15
+ MzcxMzVlYWE0ZTM0OThiYmI1NGY1YmUxZmJlOTc1OTI1MmVkZDM=
data/README.md CHANGED
@@ -1,5 +1,65 @@
1
- == .
2
- Copyright (c) 2012 "snmgian", released under LGPL v3 license.
1
+ # Foraneus
3
2
 
4
- == .
5
- Contact at: snmgian at gmail dot com
3
+ ## Usage
4
+
5
+ ``` ruby
6
+ class MyForm < Foraneus
7
+ integer :delay
8
+ float :duration
9
+ end
10
+ ```
11
+
12
+ - From the outside:
13
+
14
+ ``` ruby
15
+ form = MyForm.parse(:delay => '5', :duration => '2.14')
16
+ ```
17
+
18
+ ``` ruby
19
+ form.delay # => 5
20
+ form[:delay] # => '5'
21
+ ```
22
+
23
+ ``` ruby
24
+ form.data # => { :delay => 5, :duration => 2.14 }
25
+ form[] # => { :delay => '5', :duration => '2.14' }
26
+ ```
27
+
28
+ ``` ruby
29
+ form.valid? # => true
30
+ form.errors # => {}
31
+ ```
32
+
33
+ - From the inside
34
+
35
+ ``` ruby
36
+ form = MyForm.new
37
+ ```
38
+
39
+ ``` ruby
40
+ form.delay # => nil
41
+ form[:delay] # => nil
42
+ ```
43
+
44
+ - From the inside
45
+
46
+ ``` ruby
47
+ form = MyForm.raw(:delay => 5, :duration => 2.14)
48
+ ``` ruby
49
+
50
+ ``` ruby
51
+ form.delay # => 5
52
+ form[:delay] # => '5'
53
+ ```
54
+
55
+ ``` ruby
56
+ form.data # => { :delay => 5, :duration => 2.14 }
57
+ form[] # => { :delay => '5', :duration => '2.14' }
58
+ ```
59
+
60
+
61
+ ## License
62
+
63
+ This software is licensed under the [LGPL][lgpl] license.
64
+
65
+ [lgpl]: https://www.gnu.org/licenses/lgpl.html
@@ -0,0 +1,23 @@
1
+ class Foraneus
2
+ module Converters
3
+
4
+ class Boolean
5
+ def parse(s)
6
+ if s == 'true'
7
+ true
8
+ else
9
+ false
10
+ end
11
+ end
12
+
13
+ def raw(v)
14
+ if v
15
+ 'true'
16
+ else
17
+ 'false'
18
+ end
19
+ end
20
+ end
21
+
22
+ end
23
+ end
@@ -0,0 +1,24 @@
1
+ require 'date'
2
+
3
+ class Foraneus
4
+ module Converters
5
+
6
+ class Date
7
+
8
+ DEFAULT_FORMAT = '%Y-%m-%d'
9
+
10
+ def initialize(opts = {})
11
+ @format = opts[:format] || DEFAULT_FORMAT
12
+ end
13
+
14
+ def parse(s)
15
+ ::Date.strptime(s, @format)
16
+ end
17
+
18
+ def raw(v)
19
+ v.strftime(@format)
20
+ end
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,49 @@
1
+ require 'bigdecimal'
2
+
3
+ class Foraneus
4
+ module Converters
5
+
6
+ class Decimal
7
+ DEFAULT_DELIMITER = ','
8
+ DEFAULT_SEPARATOR = '.'
9
+
10
+ DELIMITED_REGEX = /(\d)(?=(\d\d\d)+(?!\d))/
11
+
12
+ def initialize(opts = {})
13
+ @delimiter = opts[:delimiter] || DEFAULT_DELIMITER
14
+ @separator = opts[:separator] || DEFAULT_SEPARATOR
15
+ @precision = opts[:precision]
16
+ end
17
+
18
+ def parse(s)
19
+ parts = s.split(@separator)
20
+
21
+ integer_part = (parts[0] || '0').gsub(@delimiter, '')
22
+ fractional_part = parts[1] || '0'
23
+
24
+ BigDecimal.new("#{integer_part}.#{fractional_part}")
25
+ end
26
+
27
+ def raw(v)
28
+ left, right = v.to_s('F').split('.')
29
+
30
+ if @precision && right.length < @precision
31
+ right = add_trailing_zeros(right, @precision - right.length)
32
+ end
33
+
34
+ left.gsub!(DELIMITED_REGEX) { "#{$1}#{@delimiter}" }
35
+
36
+ "#{left}#{@separator}#{right}"
37
+ end
38
+
39
+ private
40
+ def add_trailing_zeros(s, n)
41
+ zeros = '0' * n
42
+
43
+ "#{s}#{zeros}"
44
+ end
45
+
46
+ end
47
+
48
+ end
49
+ end
@@ -0,0 +1,15 @@
1
+ class Foraneus
2
+ module Converters
3
+
4
+ class Float
5
+ def parse(s)
6
+ Kernel.Float(s)
7
+ end
8
+
9
+ def raw(v)
10
+ v.to_s
11
+ end
12
+ end
13
+
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ class Foraneus
2
+ module Converters
3
+
4
+ class Integer
5
+ def parse(s)
6
+ Kernel.Integer(s)
7
+ end
8
+
9
+ def raw(v)
10
+ v.to_s
11
+ end
12
+ end
13
+
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ class Foraneus
2
+ module Converters
3
+
4
+ class String
5
+ def parse(s)
6
+ s
7
+ end
8
+
9
+ def raw(v)
10
+ v
11
+ end
12
+ end
13
+
14
+ end
15
+ end
@@ -1,59 +1,8 @@
1
- module Foraneus
1
+ class Foraneus
2
2
 
3
- # An error during the parsing of a value.
4
- class ValueError
5
-
6
- # @!attribute name
7
- # @return [String] Name of the field value
8
- attr_accessor :name
9
-
10
- # @!attribute value
11
- # @return [String] Value attempted to be parsed
12
- attr_accessor :value
13
-
14
- # @!attribute expected_type
15
- # @return [String] The expected type to be parsed
16
- attr_accessor :expected_type
17
-
18
- # @param [String] name The name of the field in the value_set
19
- # @param [String] value The value attempted to be parsed
20
- # @param [Symbol] expected_type The expected type to be parsed
21
- def initialize(name, value, expected_type)
22
- @name = name
23
- @value = value
24
- @expected_type = expected_type
25
- end
26
- end
27
-
28
- # Raised on an attempt to create a value_set from invalid value
29
- class ValueSetError < StandardError
30
-
31
- # @!attribute value_set
32
- # @return [ValueSet] ValueSet with errors
33
- attr_accessor :value_set
34
-
35
- # @param [Foraneus::ValueSet] value_set ValueSet with errors
36
- def initialize(value_set)
37
- @value_set = value_set
38
- end
39
- end
40
-
41
- # Raised on an attempt to parse an invalid value
42
- class ConverterError < StandardError
43
-
44
- # @!attribute value
45
- # @return [String] Value attempted to be parsed
46
- attr_accessor :value
47
-
48
- # @!attribute value
49
- # @return [String] Name of the converter that raised the error
50
- attr_accessor :converter_name
51
-
52
- # @param [String] value The value attempted to be parsed
53
- # @param [Symbol] converter_name Name of the converter
54
- def initialize(value, converter_name)
55
- @value = value
56
- @converter_name = converter_name
3
+ Error = Struct.new(:key, :message) do
4
+ def to_s
5
+ "#{key} - #{message}"
57
6
  end
58
7
  end
59
8
 
data/lib/foraneus.rb CHANGED
@@ -1,57 +1,132 @@
1
+ require_relative 'foraneus/converters/boolean'
2
+ require_relative 'foraneus/converters/date'
3
+ require_relative 'foraneus/converters/decimal'
4
+ require_relative 'foraneus/converters/float'
5
+ require_relative 'foraneus/converters/integer'
6
+ require_relative 'foraneus/converters/string'
7
+ require_relative 'foraneus/errors'
8
+
1
9
  # Foraneus is library for parsing external data.
2
10
  #
3
- # It allows to define value_sets that specify how the external data is structured and how should be parsed.
4
- module Foraneus
11
+ # It allows to define value_sets that specify how the external data is structured
12
+ # and how it should be parsed.
13
+ class Foraneus
14
+
15
+ attr_accessor :data
16
+
17
+ def initialize
18
+ @data = {}
19
+ @raw_data = {}
20
+
21
+ @errors = {}
22
+ end
23
+
24
+ def self.boolean(name)
25
+ converter = Foraneus::Converters::Boolean.new
26
+ field(name, converter)
27
+ end
28
+
29
+ def self.date(name, *args)
30
+ converter = Foraneus::Converters::Date.new(*args)
31
+ field(name, converter)
32
+ end
33
+
34
+ def self.decimal(name, *args)
35
+ converter = Foraneus::Converters::Decimal.new(*args)
36
+ field(name, converter)
37
+ end
38
+
39
+ def self.float(name)
40
+ converter = Foraneus::Converters::Float.new
41
+ field(name, converter)
42
+ end
43
+
44
+ def self.integer(name)
45
+ converter = Foraneus::Converters::Integer.new
46
+ field(name, converter)
47
+ end
48
+
49
+ def self.string(name)
50
+ converter = Foraneus::Converters::String.new
51
+ field(name, converter)
52
+ end
5
53
 
6
- @registry = {}
54
+ def self.field(name, converter)
55
+ fields[name.to_s] = converter
56
+ self.send(:attr_accessor, name)
57
+ end
7
58
 
8
- # Returns the converters registry
9
- # @return [Hash<Symbol, ConverterDecorator>] A hash of registered converters
10
- def self.registry
11
- @registry
59
+ def self.fields
60
+ @fields ||= {}
12
61
  end
13
62
 
14
- # Registers a converter.
15
- #
16
- # @param [Class<? extends Converters::AbstractConverter>] converter_class The converter
17
- def self.register(converter_class)
63
+ def self.parse(raw_data)
64
+ instance = self.new
65
+
66
+ parsed_data = {}
67
+
68
+ raw_data.each do |k, v|
69
+ field = k.to_s
70
+ converter = fields[field]
71
+ next unless converter
18
72
 
19
- decorated = Converters::ConverterDecorator.new(converter_class.new)
20
- @registry[decorated.name] = decorated
73
+ instance[k] = v
74
+ begin
75
+ v = if v.nil?
76
+ nil
77
+ else
78
+ converter.parse(v)
79
+ end
21
80
 
22
- self.define_type_method(decorated.name)
81
+ instance.send("#{field}=", v)
82
+ instance.data[k] = v
83
+ rescue
84
+ error = Foraneus::Error.new($!.class.name, $!.message)
85
+ instance.instance_variable_get(:@errors)[k] = error
86
+ end
87
+ end
88
+
89
+ instance
23
90
  end
24
91
 
25
- # Defines a class method in {ValueSet} that corresponds to a converter.
26
- #
27
- # The defined method will be invoked when defining a value_set
28
- #
29
- # @api private
30
- #
31
- # @param [String] name The name of the converter
32
- def self.define_type_method(name)
33
- Foraneus::ValueSet.singleton_class.send :define_method, name do |field|
34
- self.send :attr_reader, field
92
+ def self.raw(data)
93
+ instance = self.new
94
+
95
+ data.each do |k, v|
96
+ next unless fields.has_key?(k.to_s)
97
+ instance.send("#{k}=", v)
98
+ converter = fields[k.to_s]
99
+
100
+ s = if v.nil?
101
+ nil
102
+ else
103
+ converter.raw(v)
104
+ end
35
105
 
36
- @meta ||= {}
37
- @meta[field] = name
106
+ instance[k] = s
107
+ instance.data[k] = v
38
108
  end
109
+
110
+ instance
39
111
  end
40
112
 
41
- end
113
+ def [](m = nil)
114
+ if m == :errors
115
+ @errors
116
+ elsif m.nil?
117
+ @raw_data
118
+ else
119
+ @raw_data.fetch(m) do
120
+ @raw_data[m.to_s]
121
+ end
122
+ end
123
+ end
42
124
 
43
- [
44
- :converters,
45
- :errors,
46
- :hashlike,
47
- :markers,
48
- :simple_converters,
49
- :raw_value_set_builder,
50
- :value_set,
51
- :value_set_builder,
52
- ].each { |f| require_relative "foraneus/#{f}" }
53
-
54
- Foraneus.register(Foraneus::Converters::Boolean)
55
- Foraneus.register(Foraneus::Converters::Float)
56
- Foraneus.register(Foraneus::Converters::Integer)
57
- Foraneus.register(Foraneus::Converters::String)
125
+ def []=(k, v)
126
+ @raw_data[k] = v
127
+ end
128
+
129
+ def valid?
130
+ @errors.empty?
131
+ end
132
+ end
@@ -0,0 +1,36 @@
1
+ require 'spec_helper'
2
+
3
+ describe Foraneus::Converters::Boolean do
4
+
5
+ describe '#parse' do
6
+ it 'returns true with true' do
7
+ parsed = subject.parse('true')
8
+
9
+ parsed.should be_true
10
+ end
11
+
12
+ it 'returns false with sth else' do
13
+ parsed = subject.parse('false')
14
+
15
+ parsed.should be_false
16
+ end
17
+ end
18
+
19
+ describe '#raw' do
20
+ it 'returns "true" with true' do
21
+ subject.raw(true).should eq('true')
22
+ end
23
+
24
+ it 'returns "false" with false' do
25
+ subject.raw(false).should eq('false')
26
+ end
27
+
28
+ it 'returns "false" with nil' do
29
+ subject.raw(nil).should eq('false')
30
+ end
31
+
32
+ it 'returns "true" with everything else' do
33
+ subject.raw(:default).should eq('true')
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,59 @@
1
+ require 'spec_helper'
2
+
3
+ describe Foraneus::Converters::Date do
4
+
5
+ subject(:converter) { Foraneus::Converters::Date.new }
6
+
7
+ describe '#parse' do
8
+
9
+ it 'parses a date representation' do
10
+ s = '2012-04-13'
11
+
12
+ result = converter.parse(s)
13
+
14
+ result.year.should eq(2012)
15
+ result.month.should eq(04)
16
+ result.day.should eq(13)
17
+ end
18
+
19
+ context 'when format is given' do
20
+ subject(:converter) {
21
+ Foraneus::Converters::Date.new(:format => '%d/%m/%Y')
22
+ }
23
+
24
+ it 'parses a date representation' do
25
+ s = '13/04/2012'
26
+
27
+ result = converter.parse(s)
28
+
29
+ result.year.should eq(2012)
30
+ result.month.should eq(04)
31
+ result.day.should eq(13)
32
+ end
33
+ end
34
+ end
35
+
36
+ describe '#raw' do
37
+ let(:d) { Date.today }
38
+
39
+ it 'returns a date representation' do
40
+ s = d.strftime('%Y-%m-%d')
41
+
42
+ converter.raw(d).should eq(s)
43
+ end
44
+
45
+ context 'when format is given' do
46
+ let(:format) { '%m/%d/%Y' }
47
+ subject(:converter) {
48
+ Foraneus::Converters::Date.new(:format => format)
49
+ }
50
+
51
+ it 'returns a date representation' do
52
+ s = d.strftime(format)
53
+
54
+ converter.raw(d).should eq(s)
55
+ end
56
+ end
57
+ end
58
+
59
+ end
@@ -0,0 +1,81 @@
1
+ require 'spec_helper'
2
+
3
+ describe Foraneus::Converters::Decimal do
4
+
5
+ subject(:converter) { Foraneus::Converters::Decimal.new }
6
+
7
+ describe '#parse' do
8
+ it 'parses a decimal representation' do
9
+ s = '1,234.56'
10
+ n = BigDecimal.new('1234.56')
11
+
12
+ converter.parse(s).should eq(n)
13
+ end
14
+
15
+ it 'parses a decimal representation when no integer part' do
16
+ s = '.56'
17
+ n = BigDecimal.new('0.56')
18
+
19
+ converter.parse(s).should eq(n)
20
+ end
21
+
22
+ context 'when separator and delimiter are given' do
23
+ subject(:converter) {
24
+ Foraneus::Converters::Decimal.new(:delimiter => '.', :separator => ',')
25
+ }
26
+
27
+ it 'parses a decimal representation' do
28
+ s = '1.234.567,89'
29
+ n = BigDecimal.new('1234567.89')
30
+
31
+ converter.parse(s).should eq(n)
32
+ end
33
+
34
+ it 'parses a decimal representation when no integer part' do
35
+ s = ',56'
36
+ n = BigDecimal.new('0.56')
37
+
38
+ converter.parse(s).should eq(n)
39
+ end
40
+ end
41
+ end
42
+
43
+ describe '#raw' do
44
+ let(:n) { BigDecimal.new('1234567.89') }
45
+
46
+ it 'returns a decimal representation' do
47
+ s = '1,234,567.89'
48
+
49
+ converter.raw(n).should eq(s)
50
+ end
51
+
52
+ context 'when separator and delimiter are given' do
53
+ subject(:converter) {
54
+ Foraneus::Converters::Decimal.new(:delimiter => '.', :separator => ',')
55
+ }
56
+
57
+ it 'returns a decimal representation' do
58
+ s = '1.234.567,89'
59
+
60
+ converter.raw(n).should eq(s)
61
+ end
62
+ end
63
+
64
+ context 'when precision is given' do
65
+ subject(:converter) {
66
+ Foraneus::Converters::Decimal.new(:precision => 2)
67
+ }
68
+
69
+ it 'x' do
70
+ n = BigDecimal.new('3.1')
71
+ converter.raw(n).should eq('3.10')
72
+ end
73
+
74
+ it 'y' do
75
+ n = BigDecimal.new('3.145')
76
+ converter.raw(n).should eq('3.145')
77
+ end
78
+ end
79
+ end
80
+
81
+ end