Converter 1.0.2.0 → 1.0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/Converter.rb +61 -78
- metadata +1 -1
data/lib/Converter.rb
CHANGED
@@ -4,125 +4,108 @@ module Converter
|
|
4
4
|
def self.included(base)
|
5
5
|
base.extend(ClassConverter)
|
6
6
|
end
|
7
|
+
|
8
|
+
def convert_to target_class
|
9
|
+
Converter.convert self, target_class
|
10
|
+
end
|
7
11
|
|
8
|
-
# Create new Target_Type instance according to Conversion definition
|
12
|
+
# Create new Target_Type instance according to the Conversion definition
|
13
|
+
# one of the classes (source.class or target_type) should include Converter module
|
9
14
|
# @param [Any Class] source instance to copy from
|
10
15
|
# @param [class] target_type the result type
|
11
|
-
# @return instance of target_type with data from source
|
16
|
+
# @return instance of target_type with data converted from source
|
12
17
|
def self.convert(source, target_type, hash = {})
|
13
|
-
#
|
14
|
-
if(
|
15
|
-
return
|
18
|
+
# Check if the object has already been converted (Circular pointing)
|
19
|
+
if(converted_object = hash[source])
|
20
|
+
return converted_object
|
16
21
|
end
|
17
22
|
|
18
23
|
# Create new target instance
|
19
24
|
hash[source] = target = target_type.new
|
20
|
-
|
25
|
+
|
21
26
|
# Gets source Conversion definition
|
22
|
-
|
27
|
+
# Check which one includes Converter module
|
28
|
+
convertable_object = get_convertable_object source, target
|
29
|
+
conversion_metadatas = convertable_object.class.class_eval { @attribute_converter}
|
23
30
|
|
24
31
|
# update each accessor on the target according to the attr_converters
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
# Get the conversion definition for the current accessor if exists
|
29
|
-
if convert_property = conversion_properties[var_name]
|
30
|
-
target_property_name = convert_property.target_property_name.to_s.concat('=').to_sym
|
31
|
-
|
32
|
-
# Convert from one type to another (by default doesn't do anything)
|
33
|
-
if convert_property.convert_block.parameters.count == 1
|
34
|
-
target_value = convert_property.convert_block.call(source.send(var_name))
|
35
|
-
else
|
36
|
-
target_value = convert_property.convert_block.call(source.send(var_name), hash)
|
37
|
-
end
|
38
|
-
|
39
|
-
target.send(target_property_name, target_value)
|
40
|
-
end
|
32
|
+
conversion_metadatas.values.each do |conversion_metadata|
|
33
|
+
convert_one_attribute source, target, conversion_metadata, source == convertable_object, hash
|
41
34
|
end
|
42
35
|
|
43
36
|
target
|
44
37
|
end
|
45
38
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
39
|
+
private
|
40
|
+
def self.get_convertable_object source, target
|
41
|
+
if source.class.included_modules.include? Converter
|
42
|
+
source
|
43
|
+
elsif target.class.included_modules.include? Converter
|
44
|
+
target
|
45
|
+
else
|
46
|
+
raise ArgumentError.new "One of the given classes should include Converter module"
|
47
|
+
end
|
54
48
|
end
|
55
49
|
|
56
|
-
|
57
|
-
|
50
|
+
def self.convert_one_attribute source, target, conversion_metadata, is_source_convertable, hash
|
51
|
+
source_attribute_name = is_source_convertable ? conversion_metadata.convertable_attribute_name : conversion_metadata.poro_attribute_name
|
52
|
+
convert_block = is_source_convertable ? conversion_metadata.convert_from_convertable_block : conversion_metadata.convert_from_poro_block
|
53
|
+
target_attribute_name = is_source_convertable ? conversion_metadata.poro_attribute_name : conversion_metadata.convertable_attribute_name
|
54
|
+
target_attribute_name = target_attribute_name.to_s.concat('=').to_sym
|
58
55
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
var_name_trimmed = var_name.to_s.delete('=').to_sym
|
65
|
-
|
66
|
-
# Get the conversion definition for the current accessor if exists
|
67
|
-
if convert_property = conversion_properties[var_name_trimmed]
|
68
|
-
target_property_name = convert_property.target_property_name
|
69
|
-
|
70
|
-
# Convert from one type to another (by default doesn't do anything)
|
71
|
-
if convert_property.convert_back_block.parameters.count == 1
|
72
|
-
source_value = convert_property.convert_back_block.call(target.send(target_property_name))
|
73
|
-
else
|
74
|
-
source_value = convert_property.convert_back_block.call(target.send(target_property_name), hash)
|
75
|
-
end
|
76
|
-
|
77
|
-
source.send(var_name, source_value)
|
56
|
+
# Convert from one type to another (by default doesn't do anything)
|
57
|
+
if convert_block.parameters.count == 1
|
58
|
+
target_value = convert_block.call(source.send(source_attribute_name))
|
59
|
+
else
|
60
|
+
target_value = convert_block.call(source.send(source_attribute_name), hash)
|
78
61
|
end
|
62
|
+
|
63
|
+
target.send(target_attribute_name, target_value)
|
79
64
|
end
|
80
65
|
|
81
|
-
|
82
|
-
end
|
66
|
+
public
|
83
67
|
|
84
68
|
# This module add class extension of attr_converter
|
85
69
|
module ClassConverter
|
86
70
|
# Create an attr_accessor and map this attribute as convertable ,
|
87
71
|
# this means that this attribute will be converted when calling to Convert/
|
88
|
-
# @param [symbol]
|
89
|
-
# @param [symbol]
|
90
|
-
# @param [block] convert_block block that convert the
|
91
|
-
# @param [block] convert_back_block block that convert the
|
92
|
-
def attr_converter(
|
72
|
+
# @param [symbol] convertable_attribute_name class attribute name
|
73
|
+
# @param [symbol] poro_attribute_name the attribute name of a poro class (plain old ruby object)
|
74
|
+
# @param [block] convert_block block that convert between the convetable attribute class and the poro attribute class
|
75
|
+
# @param [block] convert_back_block block that convert between the poro attribute class and the convetable attribute class
|
76
|
+
def attr_converter(convertable_attribute_name, poro_attribute_name = nil, convert_block = nil, convert_back_block = nil)
|
93
77
|
# Set default values for nil arguments
|
94
|
-
|
78
|
+
poro_attribute_name ||= convertable_attribute_name
|
95
79
|
@attribute_converter ||= {}
|
96
80
|
|
97
81
|
if convert_block.class == Symbol
|
98
|
-
|
99
|
-
|
100
|
-
convert_block = lambda { |
|
101
|
-
convert_back_block = lambda { |
|
82
|
+
convertable_type = convert_block.to_s
|
83
|
+
poro_type = convert_back_block.to_s
|
84
|
+
convert_block = lambda { |source, hash| Converter.convert(source, eval(poro_type), hash) }
|
85
|
+
convert_back_block = lambda { |source, hash| Converter.convert(source, eval(convertable_type), hash) }
|
102
86
|
else
|
103
|
-
convert_block ||= lambda { |source
|
104
|
-
convert_back_block ||= lambda { |
|
87
|
+
convert_block ||= lambda { |source| source }
|
88
|
+
convert_back_block ||= lambda { |source| source }
|
105
89
|
end
|
106
90
|
|
107
|
-
|
108
91
|
# Create new ConversionMetadata
|
109
|
-
@attribute_converter[
|
110
|
-
attr_accessor
|
92
|
+
@attribute_converter[convertable_attribute_name] =ConversionMetadata.new(convertable_attribute_name, poro_attribute_name, convert_block, convert_back_block)
|
93
|
+
attr_accessor convertable_attribute_name
|
111
94
|
end
|
112
95
|
end
|
113
96
|
|
114
97
|
# Represent conversion data of one property to another
|
115
98
|
class ConversionMetadata
|
116
|
-
def initialize(
|
117
|
-
@
|
118
|
-
@
|
119
|
-
@
|
120
|
-
@
|
99
|
+
def initialize(convertable_attribute_name, poro_attribute_name, convert_from_convertable_block, convert_from_poro_block)
|
100
|
+
@convertable_attribute_name = convertable_attribute_name
|
101
|
+
@poro_attribute_name = poro_attribute_name
|
102
|
+
@convert_from_convertable_block = convert_from_convertable_block
|
103
|
+
@convert_from_poro_block = convert_from_poro_block
|
121
104
|
end
|
122
105
|
|
123
|
-
attr_accessor :
|
124
|
-
attr_accessor :
|
125
|
-
attr_accessor :
|
126
|
-
attr_accessor :
|
106
|
+
attr_accessor :convertable_attribute_name
|
107
|
+
attr_accessor :poro_attribute_name
|
108
|
+
attr_accessor :convert_from_convertable_block
|
109
|
+
attr_accessor :convert_from_poro_block
|
127
110
|
end
|
128
111
|
end
|