xmlable 0.0.0.alpha1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +21 -0
  3. data/README.md +245 -0
  4. data/lib/xmlable/attribute.rb +16 -0
  5. data/lib/xmlable/builder.rb +189 -0
  6. data/lib/xmlable/document.rb +16 -0
  7. data/lib/xmlable/element.rb +19 -0
  8. data/lib/xmlable/exports/base.rb +78 -0
  9. data/lib/xmlable/exports/json_exporter.rb +208 -0
  10. data/lib/xmlable/exports/xml_exporter.rb +179 -0
  11. data/lib/xmlable/exports.rb +11 -0
  12. data/lib/xmlable/handlers/attribute.rb +41 -0
  13. data/lib/xmlable/handlers/attribute_none.rb +10 -0
  14. data/lib/xmlable/handlers/base.rb +103 -0
  15. data/lib/xmlable/handlers/document.rb +33 -0
  16. data/lib/xmlable/handlers/element.rb +89 -0
  17. data/lib/xmlable/handlers/element_none.rb +10 -0
  18. data/lib/xmlable/handlers/elements.rb +15 -0
  19. data/lib/xmlable/handlers/mixins/described.rb +19 -0
  20. data/lib/xmlable/handlers/mixins/namespace.rb +40 -0
  21. data/lib/xmlable/handlers/mixins/tag.rb +24 -0
  22. data/lib/xmlable/handlers/namespace.rb +26 -0
  23. data/lib/xmlable/handlers/root.rb +9 -0
  24. data/lib/xmlable/handlers/root_none.rb +10 -0
  25. data/lib/xmlable/handlers/storage.rb +104 -0
  26. data/lib/xmlable/handlers.rb +23 -0
  27. data/lib/xmlable/mixins/attributes_storage.rb +195 -0
  28. data/lib/xmlable/mixins/bare_value.rb +41 -0
  29. data/lib/xmlable/mixins/castable.rb +93 -0
  30. data/lib/xmlable/mixins/container.rb +71 -0
  31. data/lib/xmlable/mixins/content_storage.rb +138 -0
  32. data/lib/xmlable/mixins/document_storage.rb +136 -0
  33. data/lib/xmlable/mixins/elements_storage.rb +219 -0
  34. data/lib/xmlable/mixins/export.rb +45 -0
  35. data/lib/xmlable/mixins/instantiable.rb +47 -0
  36. data/lib/xmlable/mixins/namespace_definitions_storage.rb +105 -0
  37. data/lib/xmlable/mixins/object.rb +95 -0
  38. data/lib/xmlable/mixins/options_storage.rb +39 -0
  39. data/lib/xmlable/mixins/root_storage.rb +162 -0
  40. data/lib/xmlable/mixins/standalone_attribute.rb +34 -0
  41. data/lib/xmlable/mixins/standalone_element.rb +47 -0
  42. data/lib/xmlable/mixins/value_storage.rb +84 -0
  43. data/lib/xmlable/mixins/wrapper.rb +37 -0
  44. data/lib/xmlable/mixins.rb +25 -0
  45. data/lib/xmlable/options/nokogiri_export.rb +19 -0
  46. data/lib/xmlable/options/storage.rb +97 -0
  47. data/lib/xmlable/options.rb +9 -0
  48. data/lib/xmlable/types.rb +31 -0
  49. data/lib/xmlable/version.rb +4 -0
  50. data/lib/xmlable.rb +49 -0
  51. metadata +149 -0
@@ -0,0 +1,95 @@
1
+ module XMLable
2
+ module Mixins
3
+ #
4
+ # Base class contains base item's methods
5
+ #
6
+ module Object
7
+ # @return [Nokogiri::XML::Node]
8
+ attr_reader :__node
9
+ # @return [XMLable::Handlers::Base]
10
+ attr_reader :__handler
11
+
12
+ def self.included(base)
13
+ base.extend(ClassMethods)
14
+ end
15
+
16
+ #
17
+ # Initialize
18
+ #
19
+ # @param [Nokogiri::XML::Node] node item's XML node
20
+ # @param [XMLable::Handlers::Base] handler item's handler
21
+ #
22
+ def initialize(node, handler)
23
+ @__node = node
24
+ @__handler = handler
25
+ __inject_node(@__node, @__handler)
26
+ end
27
+
28
+ #
29
+ # Is this object empty?
30
+ #
31
+ # @api private
32
+ #
33
+ # @return [Boolean]
34
+ #
35
+ def __empty?
36
+ true
37
+ end
38
+
39
+ #
40
+ # Does this object have a nested object with given key
41
+ #
42
+ # @return [Object, false]
43
+ #
44
+ def key?(*)
45
+ false
46
+ end
47
+
48
+ #
49
+ # Get nested object
50
+ #
51
+ def [](*)
52
+ end
53
+
54
+ #
55
+ # get nested object value
56
+ #
57
+ def []=(*)
58
+ end
59
+
60
+ private
61
+
62
+ #
63
+ # Inject XML node and handler with the curren object
64
+ #
65
+ # @param [Nokogiri::XML::Node] node item's XML node
66
+ # @param [XMLable::Handlers::Base] handler item's handler
67
+ #
68
+ # @api private
69
+ #
70
+ def __inject_node(node, handler)
71
+ node.instance_variable_set(:@__handler, handler)
72
+ node.instance_variable_set(:@__element, self)
73
+ end
74
+
75
+ module ClassMethods
76
+ #
77
+ # Get inherited object value if it's set
78
+ #
79
+ # @param [String, Symbol] var varibale name
80
+ #
81
+ # @return [Object, nil]
82
+ #
83
+ def __nested(var)
84
+ klass = superclass
85
+ obj = nil
86
+ loop do
87
+ obj = klass.instance_variable_get(var)
88
+ break if obj || !(klass = klass.superclass)
89
+ end
90
+ obj ? obj.clone : nil
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,39 @@
1
+ module XMLable
2
+ module Mixins
3
+ #
4
+ # OptionsStorage modules contains method to manage with addtional options
5
+ #
6
+ module OptionsStorage
7
+ def self.included(base)
8
+ base.extend(ClassMethods)
9
+ end
10
+
11
+ module ClassMethods
12
+ #
13
+ # Set the export options
14
+ #
15
+ def on_export(*args)
16
+ args.each { |opt| __options.opt_set(opt, true) }
17
+ end
18
+
19
+ #
20
+ # Set the before export options
21
+ #
22
+ def before_export(&block)
23
+ __options.opt_set(:before_export, block) if block_given?
24
+ end
25
+
26
+ #
27
+ # Get the options storage
28
+ #
29
+ # @api private
30
+ #
31
+ # @return [XMLable::Options::Strage]
32
+ #
33
+ def __options
34
+ @__options ||= __nested(:@__options) || Options::Storage.new
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,162 @@
1
+ module XMLable
2
+ module Mixins
3
+ #
4
+ # RootStorage module contains the logic to manage with XML root element
5
+ #
6
+ module RootStorage
7
+ def self.included(base)
8
+ base.extend(ClassMethods)
9
+ end
10
+
11
+ #
12
+ # Set XML root element
13
+ #
14
+ # @param [Nokogiri::XML::Element, nil] node
15
+ #
16
+ def __set_root(node)
17
+ unless node.is_a?(Nokogiri::XML::Element)
18
+ node = Nokogiri::XML::Element.new(__root_handler.tag.to_s, @__node)
19
+ @__node.root = node
20
+ end
21
+ node.instance_variable_set(:@__handler, __root_handler)
22
+ @root = __root_handler.from_xml_element(node)
23
+ if __root_handler.namespace_prefix
24
+ node.namespace = node.namespace_scopes.find do |n|
25
+ n.prefix == __root_handler.namespace_prefix
26
+ end
27
+ end
28
+ end
29
+
30
+ def method_missing(name, *args, &block)
31
+ return super unless key?(name)
32
+ return root unless name.to_s.end_with?('=')
33
+ self.root = args.first
34
+ end
35
+
36
+ #
37
+ # Is the current object empty?
38
+ #
39
+ # @return [Boolean]
40
+ #
41
+ def __empty?
42
+ return false unless super
43
+ __empty(root)
44
+ end
45
+
46
+ #
47
+ # Get root object
48
+ #
49
+ # @return [XMLable::Mixins::Object]
50
+ #
51
+ def root
52
+ __set_root(nil) unless instance_variable_defined?(:@root)
53
+ @root
54
+ end
55
+
56
+ #
57
+ # Get unwraped root object's value
58
+ #
59
+ # @return [Object]
60
+ #
61
+ def root!
62
+ root.__object
63
+ end
64
+
65
+ #
66
+ # Initialize root object with params
67
+ #
68
+ # @param [XMLable::Handlers::Root, XMLable::Handlers::RootNone] h root's handler
69
+ # @param [Object] value
70
+ #
71
+ def __root_object_initialize(h, value)
72
+ return self.root = value unless value.is_a?(Hash)
73
+ value.each do |key, val|
74
+ root[key].__initialize_with(val)
75
+ end
76
+ end
77
+
78
+ #
79
+ # Set root object content
80
+ #
81
+ # @param [Object] val
82
+ #
83
+ def root=(val)
84
+ @root.__overwrite_content(val)
85
+ end
86
+
87
+ #
88
+ # Does this object has root object with given key?
89
+ #
90
+ # @return [Boolean]
91
+ #
92
+ def key?(key)
93
+ names = ['root', __root_handler.method_name]
94
+ return __root_handler if names.include?(key.to_s)
95
+ super
96
+ end
97
+
98
+ #
99
+ # Get root object by key
100
+ #
101
+ # @param [String] key
102
+ #
103
+ def [](key)
104
+ root if key?(key)
105
+ end
106
+
107
+ #
108
+ # Set root object value
109
+ #
110
+ # @param [String] key
111
+ # @param [Object] val
112
+ #
113
+ def []=(key, val)
114
+ self.root = val if key?(key)
115
+ end
116
+
117
+ #
118
+ # Cureent root object's handler
119
+ #
120
+ # @return [XMLable::Handlers::Root, XMLable::Handlers::RootNone]
121
+ #
122
+ def __root_handler
123
+ self.class.__root_handler
124
+ end
125
+
126
+ module ClassMethods
127
+ #
128
+ # Define root handler
129
+ #
130
+ # @see XMLable::Handler::Base#build
131
+ #
132
+ def root(*args, &block)
133
+ @__root_handler = Handlers::Root.build(*args, &block)
134
+ __define_root(@__root_handler)
135
+ end
136
+
137
+ #
138
+ # Define root object methods
139
+ #
140
+ # @param [XMLable::Handlers::Root] h root's handler
141
+ #
142
+ def __define_root(h)
143
+ return if h.method_name == 'root'
144
+ define_method(h.method_name) { root }
145
+ define_method("#{h.method_name}=") { |val| self.root = val }
146
+ define_method("#{h.method_name}!") { root! }
147
+ define_method("__initialize_#{h.method_name}") { |val| __initialize_root(val) }
148
+ end
149
+
150
+ #
151
+ # Root object's handler
152
+ #
153
+ # @return [XMLable::Handlers::Root, XMLable::Handlers::RootNone]
154
+ #
155
+ def __root_handler
156
+ @__root_handler ||=
157
+ __nested(:@__root_handler) || Handlers::RootNone.build(:root)
158
+ end
159
+ end
160
+ end
161
+ end
162
+ end
@@ -0,0 +1,34 @@
1
+ module XMLable
2
+ module Mixins
3
+ #
4
+ # StandaloneAttribute module contains standalone attribute's logic
5
+ #
6
+ module StandaloneAttribute
7
+ def self.included(base)
8
+ base.extend(ClassMethods)
9
+ end
10
+
11
+ module ClassMethods
12
+ #
13
+ # Describe attribute name/tag
14
+ #
15
+ # @param [String, Symbol] tag
16
+ #
17
+ def attr_name(tag)
18
+ @__tag = tag.to_s
19
+ end
20
+
21
+ #
22
+ # Get attribute name/tag
23
+ #
24
+ # @api private
25
+ #
26
+ # @return [String]
27
+ #
28
+ def __tag
29
+ @__tag
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,47 @@
1
+ module XMLable
2
+ module Mixins
3
+ #
4
+ # StandaloneElement module contains standalone element's logic
5
+ #
6
+ module StandaloneElement
7
+ def self.included(base)
8
+ base.extend(ClassMethods)
9
+ end
10
+
11
+ #
12
+ # @param [Nokogiri::XML::Element] node XML element
13
+ # @param [XMLable::Handlers::Document] handler element's handler
14
+ #
15
+ def initialize(node = nil, handler = nil)
16
+ unless node
17
+ doc = Nokogiri::XML::Document.new
18
+ node = Nokogiri::XML::Element.new(self.class.__tag, doc)
19
+ end
20
+ handler = self.class.__standalone_element_handler unless handler
21
+ super
22
+ end
23
+
24
+ module ClassMethods
25
+ #
26
+ # Describe element's tag name
27
+ #
28
+ # @param [String, Symbol] name
29
+ #
30
+ def tag(name)
31
+ @__tag = name.to_s
32
+ end
33
+
34
+ #
35
+ # Get tag name
36
+ #
37
+ # @api private
38
+ #
39
+ # @return [String]
40
+ #
41
+ def __tag
42
+ @__tag
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,84 @@
1
+ module XMLable
2
+ module Mixins
3
+ #
4
+ # ValueStorage modules contains the logic to manage XML attributes values
5
+ #
6
+ module ValueStorage
7
+ #
8
+ # Set XML attribute value
9
+ #
10
+ # @param [Nokogiri::XML::Attr] node XML attribute node
11
+ #
12
+ # @api private
13
+ #
14
+ def __set_value(node)
15
+ self.__value = __cast(node.content)
16
+ end
17
+
18
+ #
19
+ # Set value
20
+ #
21
+ # @api private
22
+ #
23
+ # @param [Object] val
24
+ #
25
+ def __value=(val)
26
+ @__value = val
27
+ end
28
+
29
+ #
30
+ # Overwrite XML attribute's value
31
+ #
32
+ # @api private
33
+ #
34
+ # @param [Object] val
35
+ #
36
+ def __overwrite_value(val)
37
+ @__node.content = __export_to_xml(val)
38
+ self.__value = val
39
+ end
40
+
41
+ #
42
+ # Get unwraped attribute object's value
43
+ #
44
+ # @api private
45
+ #
46
+ # @return [Object]
47
+ #
48
+ def __object
49
+ __value
50
+ end
51
+
52
+ #
53
+ #
54
+ # Get attribute value
55
+ #
56
+ # @api private
57
+ #
58
+ # @return [Object]
59
+ #
60
+ def __value
61
+ @__value
62
+ end
63
+
64
+ #
65
+ # Is this element empty?
66
+ #
67
+ # @api private
68
+ #
69
+ # @return [Boolean]
70
+ #
71
+ def __empty?
72
+ return false unless super
73
+ __empty(__value)
74
+ end
75
+
76
+ #
77
+ # @return [String]
78
+ #
79
+ def to_s
80
+ __value.to_s
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,37 @@
1
+ module XMLable
2
+ module Mixins
3
+ #
4
+ # Wrapper module contains methods to wrap the primitive classes
5
+ #
6
+ module Wrapper
7
+ def method_missing(*args, &block)
8
+ __object.send(*args, &block)
9
+ end
10
+
11
+ #
12
+ # @return [String]
13
+ #
14
+ def to_s(*args)
15
+ __object.to_s(*args)
16
+ end
17
+
18
+ #
19
+ # Is wrapped class inherits from given class?
20
+ #
21
+ # @param [Class] klass
22
+ #
23
+ # @param [Boolean]
24
+ #
25
+ def is_a?(klass)
26
+ __object.is_a?(klass)
27
+ end
28
+
29
+ #
30
+ # @return [String]
31
+ #
32
+ def inspect
33
+ "[Wrapper(#{__object.class})] #{__object.inspect}"
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,25 @@
1
+ module XMLable
2
+ #
3
+ # Mixins module contains object mixins modules
4
+ #
5
+ module Mixins
6
+ end
7
+ end
8
+
9
+ require_relative 'mixins/object'
10
+ require_relative 'mixins/standalone_element'
11
+ require_relative 'mixins/standalone_attribute'
12
+ require_relative 'mixins/value_storage'
13
+ require_relative 'mixins/elements_storage'
14
+ require_relative 'mixins/attributes_storage'
15
+ require_relative 'mixins/container'
16
+ require_relative 'mixins/content_storage'
17
+ require_relative 'mixins/document_storage'
18
+ require_relative 'mixins/root_storage'
19
+ require_relative 'mixins/options_storage'
20
+ require_relative 'mixins/namespace_definitions_storage'
21
+ require_relative 'mixins/export'
22
+ require_relative 'mixins/bare_value'
23
+ require_relative 'mixins/instantiable'
24
+ require_relative 'mixins/castable'
25
+ require_relative 'mixins/wrapper'
@@ -0,0 +1,19 @@
1
+ module XMLable
2
+ module Options
3
+ #
4
+ # NokogiriExport class contains Nokogiri's export settings
5
+ #
6
+ # @see http://www.rubydoc.info/github/sparklemotion/nokogiri/Nokogiri/XML/Node/SaveOptions
7
+ #
8
+ class NokogiriExport < Hash
9
+ #
10
+ # Nokogiri's save_with options
11
+ #
12
+ # @return [Nokogiri::XML::Node::SaveOptions.new]
13
+ #
14
+ def save_with
15
+ self[:save_with] ||= Nokogiri::XML::Node::SaveOptions.new
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,97 @@
1
+ module XMLable
2
+ module Options
3
+ #
4
+ # Storage class stores handlers options
5
+ #
6
+ class Storage < Hash
7
+ #
8
+ # Initialize
9
+ #
10
+ # @param [Hash] hash initial params
11
+ #
12
+ def new(hash = {})
13
+ opts_set(hash)
14
+ end
15
+
16
+ #
17
+ # Set multiple options
18
+ #
19
+ # @param [Hash] hash
20
+ #
21
+ def opts_set(hash)
22
+ hash.each { |key, value| opt_set(key, value) }
23
+ end
24
+
25
+ #
26
+ # Set option
27
+ #
28
+ # @param [String, Symbol] key
29
+ # @param [Object] value
30
+ #
31
+ # @return [XMLable::Options::Storage]
32
+ #
33
+ def opt_set(key, value)
34
+ key = key.to_s
35
+ value = false if key =~ /^no_/
36
+ key = key.sub(/^no_/, '').to_sym
37
+ method = "opt_set_#{key}"
38
+ respond_to?(method) ? send(method, value) : opt_default_set(key, value)
39
+ self
40
+ end
41
+
42
+ #
43
+ # Set the drop empty objects
44
+ #
45
+ # @param [Boolean]
46
+ #
47
+ def opt_set_drop_empty(value)
48
+ self[:drop_empty_attributes] = self[:drop_empty_elements] = value
49
+ end
50
+
51
+ #
52
+ # Set the drop undescribed objects
53
+ #
54
+ # @param [Boolean]
55
+ #
56
+ def opt_set_drop_undescribed(value)
57
+ self[:drop_undescribed_attributes] = self[:drop_undescribed_elements] = value
58
+ end
59
+
60
+ #
61
+ # Default option setter
62
+ #
63
+ # @param [String, Symbol] key
64
+ # @param [Object] value
65
+ #
66
+ def opt_default_set(key, value)
67
+ self[key.to_sym] = value
68
+ end
69
+
70
+ %i[undescribed empty].each do |m|
71
+ %i[attributes elements].each do |e|
72
+ define_method("drop_#{m}_#{e}?") { fetch(:"drop_#{m}_#{e}", false) }
73
+ end
74
+ end
75
+
76
+ #
77
+ # Merge options
78
+ #
79
+ # @param [Hash, XMLable::Options::Storage] opts
80
+ #
81
+ # @return [XMLable::Options::Storage]
82
+ #
83
+ def merge_opts(other)
84
+ merger = proc do |_, v1, v2|
85
+ if [v1, v2].all? { |h| h.is_a?(Hash) }
86
+ v1.merge(v2, &merger)
87
+ elsif [v1, v2].all? { |h| h.is_a?(Array) }
88
+ v1 + v2
89
+ else
90
+ v2
91
+ end
92
+ end
93
+ merge(other, &merger)
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,9 @@
1
+ module XMLable
2
+ #
3
+ # Options module contains the options handlers
4
+ #
5
+ module Options
6
+ end
7
+ end
8
+ require_relative 'options/storage'
9
+ require_relative 'options/nokogiri_export'
@@ -0,0 +1,31 @@
1
+ XMLable::Builder.tap do |b|
2
+ b.define_type :string, :str, String do
3
+ cast { |val| val.to_s }
4
+ end
5
+
6
+ b.define_type :integer, :int, Integer do
7
+ cast { |val| val.to_i }
8
+ export_to_json { |val| val }
9
+ end
10
+
11
+ b.define_type :float, Float do
12
+ cast { |val| val.to_f }
13
+ export_to_json { |val| val }
14
+ end
15
+
16
+
17
+ b.define_type :date, Date do
18
+ cast do |val|
19
+ Date.parse(val) if val.is_a?(String) && !val.empty?
20
+ end
21
+ export { |val| val.is_a?(Date) ? val.to_s : val }
22
+ end
23
+
24
+ b.define_type :bool, :boolean do
25
+ cast do |val|
26
+ !['false', '0', ''].include?(val.to_s.downcase)
27
+ end
28
+
29
+ export_to_json { |val| val }
30
+ end
31
+ end
@@ -0,0 +1,4 @@
1
+ module XMLable
2
+ # @return [String] returns gem version
3
+ VERSION = '0.0.0.alpha1'
4
+ end