foraneus 0.0.1 → 0.0.2

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