foraneus 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/COPYING.LESSER ADDED
@@ -0,0 +1,165 @@
1
+ GNU LESSER GENERAL PUBLIC LICENSE
2
+ Version 3, 29 June 2007
3
+
4
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
5
+ Everyone is permitted to copy and distribute verbatim copies
6
+ of this license document, but changing it is not allowed.
7
+
8
+
9
+ This version of the GNU Lesser General Public License incorporates
10
+ the terms and conditions of version 3 of the GNU General Public
11
+ License, supplemented by the additional permissions listed below.
12
+
13
+ 0. Additional Definitions.
14
+
15
+ As used herein, "this License" refers to version 3 of the GNU Lesser
16
+ General Public License, and the "GNU GPL" refers to version 3 of the GNU
17
+ General Public License.
18
+
19
+ "The Library" refers to a covered work governed by this License,
20
+ other than an Application or a Combined Work as defined below.
21
+
22
+ An "Application" is any work that makes use of an interface provided
23
+ by the Library, but which is not otherwise based on the Library.
24
+ Defining a subclass of a class defined by the Library is deemed a mode
25
+ of using an interface provided by the Library.
26
+
27
+ A "Combined Work" is a work produced by combining or linking an
28
+ Application with the Library. The particular version of the Library
29
+ with which the Combined Work was made is also called the "Linked
30
+ Version".
31
+
32
+ The "Minimal Corresponding Source" for a Combined Work means the
33
+ Corresponding Source for the Combined Work, excluding any source code
34
+ for portions of the Combined Work that, considered in isolation, are
35
+ based on the Application, and not on the Linked Version.
36
+
37
+ The "Corresponding Application Code" for a Combined Work means the
38
+ object code and/or source code for the Application, including any data
39
+ and utility programs needed for reproducing the Combined Work from the
40
+ Application, but excluding the System Libraries of the Combined Work.
41
+
42
+ 1. Exception to Section 3 of the GNU GPL.
43
+
44
+ You may convey a covered work under sections 3 and 4 of this License
45
+ without being bound by section 3 of the GNU GPL.
46
+
47
+ 2. Conveying Modified Versions.
48
+
49
+ If you modify a copy of the Library, and, in your modifications, a
50
+ facility refers to a function or data to be supplied by an Application
51
+ that uses the facility (other than as an argument passed when the
52
+ facility is invoked), then you may convey a copy of the modified
53
+ version:
54
+
55
+ a) under this License, provided that you make a good faith effort to
56
+ ensure that, in the event an Application does not supply the
57
+ function or data, the facility still operates, and performs
58
+ whatever part of its purpose remains meaningful, or
59
+
60
+ b) under the GNU GPL, with none of the additional permissions of
61
+ this License applicable to that copy.
62
+
63
+ 3. Object Code Incorporating Material from Library Header Files.
64
+
65
+ The object code form of an Application may incorporate material from
66
+ a header file that is part of the Library. You may convey such object
67
+ code under terms of your choice, provided that, if the incorporated
68
+ material is not limited to numerical parameters, data structure
69
+ layouts and accessors, or small macros, inline functions and templates
70
+ (ten or fewer lines in length), you do both of the following:
71
+
72
+ a) Give prominent notice with each copy of the object code that the
73
+ Library is used in it and that the Library and its use are
74
+ covered by this License.
75
+
76
+ b) Accompany the object code with a copy of the GNU GPL and this license
77
+ document.
78
+
79
+ 4. Combined Works.
80
+
81
+ You may convey a Combined Work under terms of your choice that,
82
+ taken together, effectively do not restrict modification of the
83
+ portions of the Library contained in the Combined Work and reverse
84
+ engineering for debugging such modifications, if you also do each of
85
+ the following:
86
+
87
+ a) Give prominent notice with each copy of the Combined Work that
88
+ the Library is used in it and that the Library and its use are
89
+ covered by this License.
90
+
91
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
92
+ document.
93
+
94
+ c) For a Combined Work that displays copyright notices during
95
+ execution, include the copyright notice for the Library among
96
+ these notices, as well as a reference directing the user to the
97
+ copies of the GNU GPL and this license document.
98
+
99
+ d) Do one of the following:
100
+
101
+ 0) Convey the Minimal Corresponding Source under the terms of this
102
+ License, and the Corresponding Application Code in a form
103
+ suitable for, and under terms that permit, the user to
104
+ recombine or relink the Application with a modified version of
105
+ the Linked Version to produce a modified Combined Work, in the
106
+ manner specified by section 6 of the GNU GPL for conveying
107
+ Corresponding Source.
108
+
109
+ 1) Use a suitable shared library mechanism for linking with the
110
+ Library. A suitable mechanism is one that (a) uses at run time
111
+ a copy of the Library already present on the user's computer
112
+ system, and (b) will operate properly with a modified version
113
+ of the Library that is interface-compatible with the Linked
114
+ Version.
115
+
116
+ e) Provide Installation Information, but only if you would otherwise
117
+ be required to provide such information under section 6 of the
118
+ GNU GPL, and only to the extent that such information is
119
+ necessary to install and execute a modified version of the
120
+ Combined Work produced by recombining or relinking the
121
+ Application with a modified version of the Linked Version. (If
122
+ you use option 4d0, the Installation Information must accompany
123
+ the Minimal Corresponding Source and Corresponding Application
124
+ Code. If you use option 4d1, you must provide the Installation
125
+ Information in the manner specified by section 6 of the GNU GPL
126
+ for conveying Corresponding Source.)
127
+
128
+ 5. Combined Libraries.
129
+
130
+ You may place library facilities that are a work based on the
131
+ Library side by side in a single library together with other library
132
+ facilities that are not Applications and are not covered by this
133
+ License, and convey such a combined library under terms of your
134
+ choice, if you do both of the following:
135
+
136
+ a) Accompany the combined library with a copy of the same work based
137
+ on the Library, uncombined with any other library facilities,
138
+ conveyed under the terms of this License.
139
+
140
+ b) Give prominent notice with the combined library that part of it
141
+ is a work based on the Library, and explaining where to find the
142
+ accompanying uncombined form of the same work.
143
+
144
+ 6. Revised Versions of the GNU Lesser General Public License.
145
+
146
+ The Free Software Foundation may publish revised and/or new versions
147
+ of the GNU Lesser General Public License from time to time. Such new
148
+ versions will be similar in spirit to the present version, but may
149
+ differ in detail to address new problems or concerns.
150
+
151
+ Each version is given a distinguishing version number. If the
152
+ Library as you received it specifies that a certain numbered version
153
+ of the GNU Lesser General Public License "or any later version"
154
+ applies to it, you have the option of following the terms and
155
+ conditions either of that published version or of any later version
156
+ published by the Free Software Foundation. If the Library as you
157
+ received it does not specify a version number of the GNU Lesser
158
+ General Public License, you may choose any version of the GNU Lesser
159
+ General Public License ever published by the Free Software Foundation.
160
+
161
+ If the Library as you received it specifies that a proxy can decide
162
+ whether future versions of the GNU Lesser General Public License shall
163
+ apply, that proxy's public statement of acceptance of any version is
164
+ permanent authorization for you to choose that version for the
165
+ Library.
data/README.md ADDED
@@ -0,0 +1,5 @@
1
+ == .
2
+ Copyright (c) 2012 "snmgian", released under LGPL v3 license.
3
+
4
+ == .
5
+ Contact at: snmgian at gmail dot com
@@ -0,0 +1,50 @@
1
+ require 'delegate'
2
+
3
+ module Foraneus
4
+
5
+ # A converter is intended for parsing a string and returning a value.
6
+ module Converters
7
+
8
+ # A decorator for concrete converters. It prevents nil values and manages errors raised by {AbstractConverter#parse}
9
+ class ConverterDecorator < SimpleDelegator
10
+
11
+ # @param [AbstractConverter] converter The converter to be decorated
12
+ def initialize(converter)
13
+ super(converter)
14
+ @source = converter
15
+ end
16
+
17
+ # Invokes {AbstractConverter#parse}. Manages errors raised by the converter.
18
+ # Also, it returns nil if value.nil?
19
+ # @param [String] value Value to be parsed
20
+ # @return [Object] Parsed value
21
+ # @raise [Foraneus::ConverterError] if the concrete converter raises an error
22
+ def parse(value)
23
+ return nil if value.nil?
24
+
25
+ begin
26
+ @source.parse(value)
27
+ rescue
28
+ raise Foraneus::ConverterError.new(value, @source.name)
29
+ end
30
+ end
31
+ end
32
+
33
+ # @abstract Converters should inherit from this class and override #{code_name} and #{parse}
34
+ class AbstractConverter
35
+
36
+ # Returns the of this converter
37
+ # @return [String]
38
+ def name
39
+ raise NotImplementedError
40
+ end
41
+
42
+ # Parses a value and returns the obtained parsed value
43
+ # @param [String] value Value to be parsed
44
+ # @return [Object]
45
+ def parse(value)
46
+ raise NotImplementedError
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,60 @@
1
+ module Foraneus
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
57
+ end
58
+ end
59
+
60
+ end
@@ -0,0 +1,36 @@
1
+ module Foraneus
2
+
3
+ # Adds hash read-only capabilities.
4
+ module HashlikeValueSet
5
+
6
+ # Returns the value associated with a key
7
+ #
8
+ # Possible keys are:
9
+ # - valid?
10
+ # - errors
11
+ # - raw_values
12
+ # - as_hash
13
+ #
14
+ # @param [Symbol] key The key for the value to retrieve
15
+ # @return [Object, nil] The value associated with key, or nil if the key is unknown
16
+ def [](key)
17
+ ivar = case key
18
+ when :valid?
19
+ :@valid
20
+ when :errors
21
+ :@errors
22
+ when :raw_values
23
+ :@raw_values
24
+ when :as_hash
25
+ :@hash_values
26
+ else
27
+ nil
28
+ end
29
+
30
+ if ivar
31
+ self.instance_variable_get(ivar)
32
+ end
33
+ end
34
+ end
35
+
36
+ end
@@ -0,0 +1,9 @@
1
+ module Foraneus
2
+
3
+ # Marks a value_set as invalid.
4
+ module InvalidValueSet; end
5
+
6
+ # Marks a value_set whose getter methods return raw values.
7
+ module RawValueSet; end
8
+
9
+ end
@@ -0,0 +1,43 @@
1
+ module Foraneus
2
+
3
+ # Module for building raw value_sets
4
+ module RawValueSetBuilder
5
+
6
+ # Builds a RawValueSet.
7
+ # @param [Class] form_class A ValueSet class
8
+ # @param [Hash] meta Meta information about fields and converters
9
+ # @param [ValueSet, Hash] form_or_params ValueSet or Hash that will be used to build a RawValueSet
10
+ # @return [RawValueSet]
11
+ def self.build(form_class, meta, form_or_params)
12
+ if form_or_params.is_a?(Foraneus::ValueSet)
13
+ self.raw_form(form_or_params)
14
+ elsif form_or_params.is_a?(Hash)
15
+ self.raw_params(form_class, meta, form_or_params)
16
+ end
17
+ end
18
+
19
+ # Builds a RawValueSet from a ValueSet object
20
+ # @api private
21
+ # @param [ValueSet] value_set
22
+ # @return [RawValueSet]
23
+ def self.raw_form(value_set)
24
+ raw_vs_class = Class.new(value_set.class) do
25
+ include Foraneus::RawValueSet
26
+ end
27
+
28
+ raw_vs = raw_vs_class.new
29
+ value_set[:raw_values].each do |name, value|
30
+ raw_vs.instance_variable_set("@#{name}", value)
31
+ end
32
+ raw_vs
33
+ end
34
+
35
+ # Builds a value_set and returns a RawValueSet
36
+ # @api private
37
+ def self.raw_params(form_class, meta, params)
38
+ form = Foraneus::ValueSetBuilder.build(form_class, meta, params)
39
+ raw_form(form)
40
+ end
41
+
42
+ end
43
+ end
@@ -0,0 +1,79 @@
1
+ require 'delegate'
2
+
3
+ module Foraneus
4
+ module Converters
5
+
6
+ # Boolean converter.
7
+ class Boolean < AbstractConverter
8
+
9
+ # @see {AbstractConverter#see}
10
+ def name
11
+ :boolean
12
+ end
13
+
14
+ # @see AbstractConverter#parse
15
+ # @param [String] value The value to be parsed
16
+ # @return [Boolean] Returns true only if value == 'true'
17
+ def parse(value)
18
+ if value == true
19
+ true
20
+ elsif value == 'true'
21
+ true
22
+ else
23
+ false
24
+ end
25
+ end
26
+ end
27
+
28
+ # Float converter.
29
+ class Float < AbstractConverter
30
+
31
+ # @see {AbstractConverter#see}
32
+ def name
33
+ :float
34
+ end
35
+
36
+ # @see AbstractConverter#parse
37
+ # @return [Float] Returns a float number
38
+ def parse(value)
39
+ Kernel.Float(value)
40
+ end
41
+ end
42
+
43
+ # Integer converter.
44
+ class Integer < AbstractConverter
45
+
46
+ # @see {AbstractConverter#see}
47
+ def name
48
+ :integer
49
+ end
50
+
51
+ # @see AbstractConverter#parse
52
+ # @return [Integer] Returns an integer number
53
+ def parse(value)
54
+ Kernel.Integer(value)
55
+ end
56
+ end
57
+
58
+ # String converter.
59
+ class String < AbstractConverter
60
+
61
+ # @see {AbstractConverter#see}
62
+ def name
63
+ :string
64
+ end
65
+
66
+ # @see AbstractConverter#parse
67
+ # @return [String] Returns a String reprensentation of the given value.
68
+ def parse(value)
69
+ if value.is_a?(::String)
70
+ value
71
+ else
72
+ value.to_s
73
+ end
74
+ end
75
+ end
76
+
77
+ end
78
+ end
79
+
@@ -0,0 +1,46 @@
1
+ module Foraneus
2
+
3
+ # Base class from which all the value_sets should inherit.
4
+ #
5
+ # Concrete classes model a set of params/values that are parsed from an external source (like HTTP query params)
6
+ class ValueSet
7
+
8
+ # Builds an instance of ValueSet that conforms with its fields specification.
9
+ #
10
+ # If any of the params can be parsed by its corresponding convertor then an
11
+ # {InvalidValueSet} and {RawValueSet} is returned that holds raw_values
12
+ #
13
+ # @param [Hash<Symbol, String>] params Hash that holds values that will be parsed and associated
14
+ # with the created ValueSet instance.
15
+ # @return [ValueSet, InvalidValueSet, RawValueSet] A value_set
16
+ def self.build(params = {})
17
+ Foraneus::ValueSetBuilder.build(self, @meta, params)
18
+ end
19
+
20
+ # Builds an instance of ValueSet and raises an {ValueSetError} if invalid params.
21
+ # @see .build
22
+ # @param (see .build)
23
+ def self.build!(params = {})
24
+ value_set = self.build(params)
25
+ if value_set.kind_of?(Foraneus::InvalidValueSet)
26
+ raise Foraneus::ValueSetError.new(value_set)
27
+ end
28
+
29
+ value_set
30
+ end
31
+
32
+ # Returns a {RawValueSet} that corresponds to the given argument.
33
+ #
34
+ # @overload raw(value_set)
35
+ # @param [ValueSet] value_set ValueSet whose raw value will be used to create a {RawValueSet}
36
+ #
37
+ # @overload raw(params)
38
+ # @param [Hash] params Params for creating a {RawValueSet}
39
+ #
40
+ # @return [RawValueSet]
41
+ def self.raw(vs_or_params)
42
+ Foraneus::RawValueSetBuilder.build(self, @meta, vs_or_params)
43
+ end
44
+
45
+ end
46
+ end
@@ -0,0 +1,132 @@
1
+ module Foraneus
2
+
3
+ # Module for building value_sets
4
+ module ValueSetBuilder
5
+
6
+ # Builds an instance of a value_set
7
+ #
8
+ # @param [Class<? extends ValueSet>] vs_class ValueSet subclass from with the value_set will be instantiated
9
+ #
10
+ # @param [Hash<Symbol, Symbol>] meta Hash with metainformation. Each key is a field name, and each value is the name of the converter to be used
11
+ #
12
+ # @param [Hash<Symbol, Object>] params Parameters that will be parsed and set as attributes for the newly value_set
13
+ #
14
+ # @return [ValueSet] An instance of (or an instance of subclass of) vs_class
15
+ def self.build(vs_class, meta, params = {})
16
+
17
+ parsed_params, raw_params, errors = self.parse_params(meta, params)
18
+
19
+ if errors.empty?
20
+ form = vs_class.new
21
+ set_instance_vars(form, parsed_params)
22
+ form.instance_variable_set(:@hash_values, parsed_params)
23
+ else
24
+ form = create_invalid_value_set(vs_class)
25
+ set_instance_vars(form, raw_params)
26
+ end
27
+
28
+ form.instance_variable_set(:@valid, errors.empty?)
29
+ form.instance_variable_set(:@errors, errors)
30
+ form.instance_variable_set(:@raw_values, raw_params)
31
+ form
32
+ end
33
+
34
+ # Creates a value_set marked as invalid.
35
+ #
36
+ # It creates a value set by creating an instance from a subclass of vs_class.
37
+ # That subclass is mixed in with {InvalidValueSet} and {RawValueSet}
38
+ #
39
+ # @api private
40
+ #
41
+ # @param [Class<? extends ValueSet>] vs_class Subclass of ValueSet from which the invalid value_set
42
+ # will be instantiated
43
+ #
44
+ # @return [ValueSet] A kind of ValueSet, mixed with {InvalidValueSet} and {RawValueSet}
45
+ def self.create_invalid_value_set(vs_class)
46
+ form_class = Class.new(vs_class) do
47
+ include Foraneus::InvalidValueSet
48
+ include Foraneus::RawValueSet
49
+ end
50
+ form = form_class.new
51
+ end
52
+
53
+ # Parses a given value.
54
+ #
55
+ # The converter is selected by querying the given metadata, searching for the given name.
56
+ # @api private
57
+ #
58
+ # @param [Hash<Symbol, Symbol>] meta (see .build)
59
+ # @param [Symbol] name Name of the field to be parsed
60
+ # @param [String] value Value to be parsed
61
+ #
62
+ # @return [Array] An array of two elements. The first of them is the result of the parsing,
63
+ # and the last one is a boolean value that indicates if an error occured during the parsing.
64
+ def self.parse(meta, name, value)
65
+ parsed_value = nil
66
+ error = false
67
+
68
+ parser_code = meta[name]
69
+ parser = Foraneus.registry[parser_code]
70
+
71
+ begin
72
+ parsed_value = parser.parse(value)
73
+ rescue StandardError => e
74
+ error = true
75
+ end
76
+
77
+ [parsed_value, error]
78
+ end
79
+
80
+ # Parses each of the given params.
81
+ #
82
+ # The converter is select by searching for the param name in the metadata.
83
+ # If a param is not expected by the metadata, then it is simply ignored.
84
+ #
85
+ # @api private
86
+ #
87
+ # @param [Hash<Symbol, Symbol>] meta (see .build)
88
+ # @param [Hash<Symbol, Object>] params (see .build)
89
+ #
90
+ # @return [Array] An array consisting of three elements:
91
+ # - Hash { Symbol => Object } Parsed params/values
92
+ # - Hash { Symbol => Object } Given params/values, it only contains params that are present in the metadata.
93
+ # - Hash { Symbol => {ValueError} } Errors ocurred during parsing, each key is the name of a field with an associated error
94
+ def self.parse_params(meta, params)
95
+ parsed_params = {}
96
+ raw_params = {}
97
+ errors = {}
98
+
99
+ params.each do |name, value|
100
+ normalized_name = name.to_sym
101
+ next unless meta.include?(normalized_name)
102
+
103
+ raw_params[name] = value
104
+ parsed_value, error = self.parse(meta, normalized_name, value)
105
+ unless error
106
+ parsed_params[normalized_name] = parsed_value
107
+ else
108
+ errors[normalized_name] = Foraneus::ValueError.new(normalized_name, value, meta[normalized_name])
109
+ end
110
+ end
111
+
112
+ [parsed_params, raw_params, errors]
113
+ end
114
+
115
+ # Sets instance variables to an object.
116
+ #
117
+ # @api private
118
+ #
119
+ # @param [Object] o The object to set the instance variables
120
+ # @param [Hash<Symbol, Object>] params A hash with the names and values of instance variables that will be set.
121
+ #
122
+ # @return [Object] The received object with the instance variables set
123
+ def self.set_instance_vars(o, params)
124
+ params.each do |name, value|
125
+ o.instance_variable_set("@#{name}", value)
126
+ end
127
+
128
+ o
129
+ end
130
+
131
+ end
132
+ end
data/lib/foraneus.rb ADDED
@@ -0,0 +1,57 @@
1
+ # Foraneus is library for parsing external data.
2
+ #
3
+ # It allows to define value_sets that specify how the external data is structured and how should be parsed.
4
+ module Foraneus
5
+
6
+ @registry = {}
7
+
8
+ # Returns the converters registry
9
+ # @return [Hash<Symbol, ConverterDecorator>] A hash of registered converters
10
+ def self.registry
11
+ @registry
12
+ end
13
+
14
+ # Registers a converter.
15
+ #
16
+ # @param [Class<? extends Converters::AbstractConverter>] converter_class The converter
17
+ def self.register(converter_class)
18
+
19
+ decorated = Converters::ConverterDecorator.new(converter_class.new)
20
+ @registry[decorated.name] = decorated
21
+
22
+ self.define_type_method(decorated.name)
23
+ end
24
+
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
35
+
36
+ @meta ||= {}
37
+ @meta[field] = name
38
+ end
39
+ end
40
+
41
+ end
42
+
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)