frodo 0.10.0

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.
Files changed (153) hide show
  1. checksums.yaml +7 -0
  2. data/.autotest +2 -0
  3. data/.circleci/config.yml +54 -0
  4. data/.gitignore +24 -0
  5. data/.gitlab-ci.yml +9 -0
  6. data/.rspec +2 -0
  7. data/.ruby-gemset +1 -0
  8. data/.ruby-version +1 -0
  9. data/.travis.yml +75 -0
  10. data/CHANGELOG.md +163 -0
  11. data/Gemfile +4 -0
  12. data/LICENSE.txt +23 -0
  13. data/README.md +479 -0
  14. data/Rakefile +7 -0
  15. data/TODO.md +55 -0
  16. data/frodo.gemspec +39 -0
  17. data/images/frodo.jpg +0 -0
  18. data/lib/frodo/abstract_client.rb +11 -0
  19. data/lib/frodo/client.rb +6 -0
  20. data/lib/frodo/concerns/api.rb +292 -0
  21. data/lib/frodo/concerns/authentication.rb +32 -0
  22. data/lib/frodo/concerns/base.rb +84 -0
  23. data/lib/frodo/concerns/caching.rb +26 -0
  24. data/lib/frodo/concerns/connection.rb +79 -0
  25. data/lib/frodo/concerns/verbs.rb +68 -0
  26. data/lib/frodo/config.rb +143 -0
  27. data/lib/frodo/entity.rb +335 -0
  28. data/lib/frodo/entity_container.rb +75 -0
  29. data/lib/frodo/entity_set.rb +131 -0
  30. data/lib/frodo/errors.rb +70 -0
  31. data/lib/frodo/middleware/authentication/token.rb +13 -0
  32. data/lib/frodo/middleware/authentication.rb +87 -0
  33. data/lib/frodo/middleware/authorization.rb +18 -0
  34. data/lib/frodo/middleware/caching.rb +30 -0
  35. data/lib/frodo/middleware/custom_headers.rb +14 -0
  36. data/lib/frodo/middleware/gzip.rb +33 -0
  37. data/lib/frodo/middleware/instance_url.rb +20 -0
  38. data/lib/frodo/middleware/logger.rb +42 -0
  39. data/lib/frodo/middleware/multipart.rb +64 -0
  40. data/lib/frodo/middleware/odata_headers.rb +13 -0
  41. data/lib/frodo/middleware/raise_error.rb +47 -0
  42. data/lib/frodo/middleware.rb +33 -0
  43. data/lib/frodo/navigation_property/proxy.rb +80 -0
  44. data/lib/frodo/navigation_property.rb +29 -0
  45. data/lib/frodo/properties/binary.rb +50 -0
  46. data/lib/frodo/properties/boolean.rb +37 -0
  47. data/lib/frodo/properties/collection.rb +50 -0
  48. data/lib/frodo/properties/complex.rb +114 -0
  49. data/lib/frodo/properties/date.rb +27 -0
  50. data/lib/frodo/properties/date_time.rb +83 -0
  51. data/lib/frodo/properties/date_time_offset.rb +17 -0
  52. data/lib/frodo/properties/decimal.rb +54 -0
  53. data/lib/frodo/properties/enum.rb +62 -0
  54. data/lib/frodo/properties/float.rb +67 -0
  55. data/lib/frodo/properties/geography/base.rb +162 -0
  56. data/lib/frodo/properties/geography/line_string.rb +33 -0
  57. data/lib/frodo/properties/geography/point.rb +31 -0
  58. data/lib/frodo/properties/geography/polygon.rb +38 -0
  59. data/lib/frodo/properties/geography.rb +13 -0
  60. data/lib/frodo/properties/guid.rb +17 -0
  61. data/lib/frodo/properties/integer.rb +107 -0
  62. data/lib/frodo/properties/number.rb +14 -0
  63. data/lib/frodo/properties/string.rb +72 -0
  64. data/lib/frodo/properties/time.rb +40 -0
  65. data/lib/frodo/properties/time_of_day.rb +27 -0
  66. data/lib/frodo/properties.rb +32 -0
  67. data/lib/frodo/property.rb +139 -0
  68. data/lib/frodo/property_registry.rb +41 -0
  69. data/lib/frodo/query/criteria/comparison_operators.rb +49 -0
  70. data/lib/frodo/query/criteria/date_functions.rb +61 -0
  71. data/lib/frodo/query/criteria/geography_functions.rb +21 -0
  72. data/lib/frodo/query/criteria/lambda_operators.rb +27 -0
  73. data/lib/frodo/query/criteria/string_functions.rb +40 -0
  74. data/lib/frodo/query/criteria.rb +92 -0
  75. data/lib/frodo/query/in_batches.rb +58 -0
  76. data/lib/frodo/query.rb +221 -0
  77. data/lib/frodo/railtie.rb +19 -0
  78. data/lib/frodo/schema/complex_type.rb +79 -0
  79. data/lib/frodo/schema/enum_type.rb +95 -0
  80. data/lib/frodo/schema.rb +164 -0
  81. data/lib/frodo/service.rb +199 -0
  82. data/lib/frodo/service_registry.rb +52 -0
  83. data/lib/frodo/version.rb +3 -0
  84. data/lib/frodo.rb +67 -0
  85. data/spec/fixtures/auth_success_response.json +11 -0
  86. data/spec/fixtures/error.json +11 -0
  87. data/spec/fixtures/files/entity_to_xml.xml +18 -0
  88. data/spec/fixtures/files/error.xml +5 -0
  89. data/spec/fixtures/files/metadata.xml +150 -0
  90. data/spec/fixtures/files/metadata_with_error.xml +157 -0
  91. data/spec/fixtures/files/product_0.json +10 -0
  92. data/spec/fixtures/files/product_0.xml +28 -0
  93. data/spec/fixtures/files/products.json +106 -0
  94. data/spec/fixtures/files/products.xml +308 -0
  95. data/spec/fixtures/files/supplier_0.json +26 -0
  96. data/spec/fixtures/files/supplier_0.xml +32 -0
  97. data/spec/fixtures/leads.json +923 -0
  98. data/spec/fixtures/refresh_error_response.json +8 -0
  99. data/spec/frodo/abstract_client_spec.rb +13 -0
  100. data/spec/frodo/client_spec.rb +57 -0
  101. data/spec/frodo/concerns/authentication_spec.rb +79 -0
  102. data/spec/frodo/concerns/base_spec.rb +68 -0
  103. data/spec/frodo/concerns/caching_spec.rb +40 -0
  104. data/spec/frodo/concerns/connection_spec.rb +65 -0
  105. data/spec/frodo/config_spec.rb +127 -0
  106. data/spec/frodo/entity/shared_examples.rb +83 -0
  107. data/spec/frodo/entity_container_spec.rb +38 -0
  108. data/spec/frodo/entity_set_spec.rb +169 -0
  109. data/spec/frodo/entity_spec.rb +153 -0
  110. data/spec/frodo/errors_spec.rb +48 -0
  111. data/spec/frodo/middleware/authentication/token_spec.rb +87 -0
  112. data/spec/frodo/middleware/authentication_spec.rb +83 -0
  113. data/spec/frodo/middleware/authorization_spec.rb +17 -0
  114. data/spec/frodo/middleware/custom_headers_spec.rb +21 -0
  115. data/spec/frodo/middleware/gzip_spec.rb +68 -0
  116. data/spec/frodo/middleware/instance_url_spec.rb +27 -0
  117. data/spec/frodo/middleware/logger_spec.rb +21 -0
  118. data/spec/frodo/middleware/odata_headers_spec.rb +15 -0
  119. data/spec/frodo/middleware/raise_error_spec.rb +66 -0
  120. data/spec/frodo/navigation_property/proxy_spec.rb +46 -0
  121. data/spec/frodo/navigation_property_spec.rb +55 -0
  122. data/spec/frodo/properties/binary_spec.rb +50 -0
  123. data/spec/frodo/properties/boolean_spec.rb +72 -0
  124. data/spec/frodo/properties/collection_spec.rb +44 -0
  125. data/spec/frodo/properties/date_spec.rb +23 -0
  126. data/spec/frodo/properties/date_time_offset_spec.rb +30 -0
  127. data/spec/frodo/properties/date_time_spec.rb +23 -0
  128. data/spec/frodo/properties/decimal_spec.rb +50 -0
  129. data/spec/frodo/properties/float_spec.rb +45 -0
  130. data/spec/frodo/properties/geography/line_string_spec.rb +33 -0
  131. data/spec/frodo/properties/geography/point_spec.rb +29 -0
  132. data/spec/frodo/properties/geography/polygon_spec.rb +55 -0
  133. data/spec/frodo/properties/geography/shared_examples.rb +72 -0
  134. data/spec/frodo/properties/guid_spec.rb +17 -0
  135. data/spec/frodo/properties/integer_spec.rb +58 -0
  136. data/spec/frodo/properties/string_spec.rb +46 -0
  137. data/spec/frodo/properties/time_of_day_spec.rb +23 -0
  138. data/spec/frodo/properties/time_spec.rb +15 -0
  139. data/spec/frodo/property_registry_spec.rb +16 -0
  140. data/spec/frodo/property_spec.rb +71 -0
  141. data/spec/frodo/query/criteria_spec.rb +229 -0
  142. data/spec/frodo/query_spec.rb +156 -0
  143. data/spec/frodo/schema/complex_type_spec.rb +97 -0
  144. data/spec/frodo/schema/enum_type_spec.rb +112 -0
  145. data/spec/frodo/schema_spec.rb +113 -0
  146. data/spec/frodo/service_registry_spec.rb +19 -0
  147. data/spec/frodo/service_spec.rb +153 -0
  148. data/spec/frodo/usage_example_spec.rb +161 -0
  149. data/spec/spec_helper.rb +35 -0
  150. data/spec/support/coverage.rb +2 -0
  151. data/spec/support/fixture_helpers.rb +14 -0
  152. data/spec/support/middleware.rb +19 -0
  153. metadata +479 -0
@@ -0,0 +1,40 @@
1
+ module Frodo
2
+ class Query
3
+ class Criteria
4
+ module StringFunctions
5
+ # Sets up a `contains` function criterium.
6
+ # @param str [to_s]
7
+ # @return [self]
8
+ def contains(str)
9
+ set_function_and_argument(:contains, str)
10
+ end
11
+
12
+ # Sets up a `startswith` function criterium.
13
+ # @param str [to_s]
14
+ # @return [self]
15
+ def startswith(str)
16
+ set_function_and_argument(:startswith, str)
17
+ end
18
+
19
+ # Sets up a `endswith` function criterium.
20
+ # @param str [to_s]
21
+ # @return [self]
22
+ def endswith(str)
23
+ set_function_and_argument(:endswith, str)
24
+ end
25
+
26
+ # Applies the `tolower` function to the property.
27
+ # @return [self]
28
+ def tolower
29
+ set_function_and_argument(:tolower, nil)
30
+ end
31
+
32
+ # Applies the `toupper` function to the property.
33
+ # @return [self]
34
+ def toupper
35
+ set_function_and_argument(:toupper, nil)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,92 @@
1
+ require 'frodo/query/criteria/comparison_operators'
2
+ require 'frodo/query/criteria/string_functions'
3
+ require 'frodo/query/criteria/date_functions'
4
+ require 'frodo/query/criteria/geography_functions'
5
+ require 'frodo/query/criteria/lambda_operators'
6
+
7
+ module Frodo
8
+ class Query
9
+ # Represents a discreet criteria within an Frodo::Query. Should not,
10
+ # normally, be instantiated directly.
11
+ class Criteria
12
+ # The property name that is the target of the criteria.
13
+ attr_reader :property
14
+ # The operator of the criteria.
15
+ attr_reader :operator
16
+ # The value of the criteria.
17
+ attr_reader :value
18
+ # A function to apply to the property.
19
+ attr_reader :function
20
+ # An optional argument to the function.
21
+ attr_reader :argument
22
+
23
+ # Initializes a new criteria with provided options.
24
+ # @param options [Hash]
25
+ def initialize(options = {})
26
+ @property = options[:property]
27
+ @operator = options[:operator]
28
+ @function = options[:function]
29
+ @argument = options[:argument]
30
+ @value = options[:value]
31
+ end
32
+
33
+ include ComparisonOperators
34
+ include StringFunctions
35
+ include DateFunctions
36
+ include GeographyFunctions
37
+ include LambdaOperators
38
+
39
+ # Returns criteria as query-ready string.
40
+ def to_s
41
+ query = function ? function_expression : property_name
42
+
43
+ if operator && !lambda_operator?
44
+ "#{query} #{operator} #{url_value(value)}"
45
+ else
46
+ query
47
+ end
48
+ end
49
+
50
+ private
51
+
52
+ def property_name
53
+ property.name
54
+ rescue NoMethodError
55
+ property.to_s
56
+ end
57
+
58
+ def function_expression
59
+ return lambda_expression if lambda_operator?
60
+
61
+ if argument
62
+ "#{function}(#{property_name},#{url_value(argument)})"
63
+ else
64
+ "#{function}(#{property_name})"
65
+ end
66
+ end
67
+
68
+ def lambda_expression
69
+ "#{property_name}/#{function}(d:d/#{argument} #{operator} #{url_value(value)})"
70
+ end
71
+
72
+ def url_value(value)
73
+ property.value = value
74
+ property.url_value
75
+ rescue
76
+ value
77
+ end
78
+
79
+ def set_operator_and_value(operator, value)
80
+ @operator = operator
81
+ @value = value
82
+ self
83
+ end
84
+
85
+ def set_function_and_argument(function, argument)
86
+ @function = function
87
+ @argument = argument
88
+ self
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,58 @@
1
+ module Frodo
2
+ class Query
3
+ module InBatches
4
+ DEFAULT_BATCH_SIZE = 10
5
+
6
+ # Process results in batches.
7
+ #
8
+ # When a block is given, yields `Frodo::Query::Result`
9
+ # objects of specified batch size to the block.
10
+ #
11
+ # service['Products'].query.in_batches(of: 10) do |batch|
12
+ # batch.count # batch size (10 except for last batch)
13
+ # batch.is_a? Frodo::Query::Result # true
14
+ # end
15
+ #
16
+ # Returns an Enumerator to process results individually.
17
+ #
18
+ # service['Products'].query.in_batches.each do |entity|
19
+ # entity.is_a? Frodo::Entity # true
20
+ # end
21
+ #
22
+ # @param of: [int] batch size
23
+ # @return [Enumerator]
24
+ def in_batches(of: DEFAULT_BATCH_SIZE, &block)
25
+ per_page = of
26
+
27
+ if block_given?
28
+ each_batch(of, &block)
29
+ else
30
+ Enumerator.new do |result|
31
+ each_batch(of) do |batch|
32
+ batch.each { |entity| result << entity }
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ private
39
+
40
+ def each_batch(per_page, &block)
41
+ page = 0
42
+
43
+ loop do
44
+ batch = get_paginated_entities(per_page, page)
45
+ break if batch.empty?
46
+
47
+ yield batch
48
+
49
+ page += 1
50
+ end
51
+ end
52
+
53
+ def get_paginated_entities(per_page, page)
54
+ skip(per_page * page).limit(per_page).execute
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,221 @@
1
+ require 'frodo/query/criteria'
2
+ require 'frodo/query/in_batches'
3
+
4
+ module Frodo
5
+ # Frodo::Query provides the query interface for requesting Entities matching
6
+ # specific criteria from an Frodo::EntitySet. This class should not be
7
+ # instantiated directly, but can be. Normally you will access a Query by
8
+ # first asking for one from the Frodo::EntitySet you want to query.
9
+ class Query
10
+ attr_reader :options
11
+
12
+ # Create a new Query for the provided EntitySet
13
+ # @param entity_set [Frodo::EntitySet]
14
+ # @param options [Hash] Query options
15
+ def initialize(entity_set, options = {})
16
+ @entity_set = entity_set
17
+ @options = options
18
+ setup_empty_criteria_set
19
+ end
20
+
21
+ # Instantiates an Frodo::Query::Criteria for the named property.
22
+ # @param property [to_s]
23
+ def [](property)
24
+ property_instance = @entity_set.new_entity.get_property(property)
25
+ property_instance = property if property_instance.nil?
26
+ Frodo::Query::Criteria.new(property: property_instance)
27
+ end
28
+
29
+ # Build a query to find an entity with the supplied key value.
30
+ # @param key [to_s] primary key to lookup
31
+ # @return the path and querystring [String]
32
+ def find(key)
33
+ entity = @entity_set.new_entity
34
+ key_property = entity.get_property(entity.primary_key)
35
+ key_property.value = key
36
+
37
+ pathname = "#{entity_set.name}(#{key_property.url_value})"
38
+
39
+ select_criteria = if list_criteria(:select)
40
+ list_criteria(:select).map { |k, v| "#{k}=#{v}" }.join('&')
41
+ end
42
+ [pathname, select_criteria].compact.join('?')
43
+ end
44
+
45
+ # Adds a filter criteria to the query.
46
+ # For filter syntax see https://msdn.microsoft.com/en-us/library/gg309461.aspx
47
+ # Syntax:
48
+ # Property Operator Value
49
+ #
50
+ # For example:
51
+ # Name eq 'Customer Service'
52
+ #
53
+ # Operators:
54
+ # eq, ne, gt, ge, lt, le, and, or, not
55
+ #
56
+ # Value
57
+ # can be 'null', can use single quotes
58
+ # @param criteria
59
+ def where(criteria)
60
+ criteria_set[:filter] << criteria
61
+ self
62
+ end
63
+
64
+ # Adds a fulltext search term to the query
65
+ # NOTE: May not be implemented by the service
66
+ # @param term [String]
67
+ def search(term)
68
+ criteria_set[:search] << term
69
+ self
70
+ end
71
+
72
+ # Adds a filter criteria to the query with 'and' logical operator.
73
+ # @param criteria
74
+ #def and(criteria)
75
+ #
76
+ #end
77
+
78
+ # Adds a filter criteria to the query with 'or' logical operator.
79
+ # @param criteria
80
+ #def or(criteria)
81
+ #
82
+ #end
83
+
84
+ # Specify properties to order the result by.
85
+ # Can use 'desc' like 'Name desc'
86
+ # @param properties [Array<Symbol>]
87
+ # @return [self]
88
+ def order_by(*properties)
89
+ criteria_set[:orderby] += properties
90
+ self
91
+ end
92
+
93
+ # Specify associations to expand in the result.
94
+ # @param associations [Array<Symbol>]
95
+ # @return [self]
96
+ def expand(*associations)
97
+ criteria_set[:expand] += associations
98
+ self
99
+ end
100
+
101
+ # Specify properties to select within the result.
102
+ # @param properties [Array<Symbol>]
103
+ # @return [self]
104
+ def select(*properties)
105
+ criteria_set[:select] += properties
106
+ self
107
+ end
108
+
109
+ # Add skip criteria to query.
110
+ # @param value [to_i]
111
+ # @return [self]
112
+ def skip(value)
113
+ criteria_set[:skip] = value.to_i
114
+ self
115
+ end
116
+
117
+ # Add limit criteria to query.
118
+ # @param value [to_i]
119
+ # @return [self]
120
+ def limit(value)
121
+ criteria_set[:top] = value.to_i
122
+ self
123
+ end
124
+
125
+ # Add inline count criteria to query.
126
+ # Not Supported in CRM2011
127
+ # @return [self]
128
+ def include_count
129
+ criteria_set[:inline_count] = true
130
+ self
131
+ end
132
+
133
+ # Convert Query to string.
134
+ # @return [String]
135
+ def to_s
136
+ criteria = params.map { |k, v| "#{k}=#{v}" }.join('&')
137
+ [entity_set.name, params.any? ? criteria : nil].compact.join('?')
138
+ end
139
+
140
+ # Build a query to count of an entity set
141
+ # @return [Integer]
142
+ def count
143
+ "#{entity_set.name}/$count"
144
+ end
145
+
146
+ # The EntitySet for this query.
147
+ # @return [Frodo::EntitySet]
148
+ # @api private
149
+ def entity_set
150
+ @entity_set
151
+ end
152
+
153
+ # The parameter hash for this query.
154
+ # @return [Hash] Params hash
155
+ def params
156
+ assemble_criteria || {}
157
+ end
158
+
159
+ # The service for this query
160
+ # @return [Frodo::Service]
161
+ # @api private
162
+ def service
163
+ @service ||= entity_set.service
164
+ end
165
+
166
+ private
167
+
168
+ attr_reader :criteria_set
169
+
170
+ def setup_empty_criteria_set
171
+ @criteria_set = {
172
+ filter: [],
173
+ search: [],
174
+ select: [],
175
+ expand: [],
176
+ orderby: [],
177
+ skip: 0,
178
+ top: 0,
179
+ inline_count: false
180
+ }
181
+ end
182
+
183
+ def assemble_criteria
184
+ [
185
+ filter_criteria,
186
+ search_criteria,
187
+ list_criteria(:orderby),
188
+ list_criteria(:expand),
189
+ list_criteria(:select),
190
+ inline_count_criteria,
191
+ paging_criteria(:skip),
192
+ paging_criteria(:top)
193
+ ].compact.reduce(&:merge)
194
+ end
195
+
196
+ def filter_criteria
197
+ return nil if criteria_set[:filter].empty?
198
+ filters = criteria_set[:filter].collect(&:to_s)
199
+ { '$filter' => filters.join(' and ') }
200
+ end
201
+
202
+ def search_criteria
203
+ return nil if criteria_set[:search].empty?
204
+ filters = criteria_set[:search].collect(&:to_s)
205
+ { '$search' => filters.join(' AND ') }
206
+ end
207
+
208
+ def list_criteria(name)
209
+ return nil if criteria_set[name].empty?
210
+ { "$#{name}" => criteria_set[name].join(',') }
211
+ end
212
+
213
+ def inline_count_criteria
214
+ criteria_set[:inline_count] ? { '$count' => 'true' } : nil
215
+ end
216
+
217
+ def paging_criteria(name)
218
+ criteria_set[name] == 0 ? nil : { "$#{name}" => criteria_set[name] }
219
+ end
220
+ end
221
+ end
@@ -0,0 +1,19 @@
1
+ module Frodo
2
+ class Railtie < Rails::Railtie
3
+ config.before_initialize do
4
+ ::Frodo::Railtie.load_configuration!
5
+ ::Frodo::Railtie.setup_service_registry!
6
+ end
7
+
8
+ # Looks for config/odata.yml and loads the configuration.
9
+ def self.load_configuration!
10
+ # TODO Implement Rails configuration loading
11
+ end
12
+
13
+ # Examines the loaded configuration and populates the
14
+ # Frodo::ServiceRegistry accordingly.
15
+ def self.setup_service_registry!
16
+ # TODO Populate Frodo::ServiceRegistry based on configuration
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,79 @@
1
+ module Frodo
2
+ class Schema
3
+ # ComplexTypes are used in Frodo to either encapsulate richer data types for
4
+ # use as Entity properties. ComplexTypes are composed of properties the same
5
+ # way that Entities are and, so, the interface for working with the various
6
+ # properties of a ComplexType mimics that of Entities.
7
+ class ComplexType
8
+ # Creates a new ComplexType based on the supplied options.
9
+ # @param type_xml [Nokogiri::XML::Element]
10
+ # @param service [Frodo::Service]
11
+ def initialize(type_definition, schema)
12
+ @type_definition = type_definition
13
+ @schema = schema
14
+ end
15
+
16
+ # The name of the ComplexType
17
+ # @return [String]
18
+ def name
19
+ @name ||= type_definition.attributes['Name'].value
20
+ end
21
+
22
+ # Returns the namespaced type for the ComplexType.
23
+ # @return [String]
24
+ def type
25
+ "#{namespace}.#{name}"
26
+ end
27
+
28
+ # Returns the namespace this ComplexType belongs to.
29
+ # @return [String]
30
+ def namespace
31
+ @namespace ||= service.namespace
32
+ end
33
+
34
+ # Returns this ComplexType's properties.
35
+ # @return [Hash<String, Frodo::Property>]
36
+ def properties
37
+ @properties ||= collect_properties
38
+ end
39
+
40
+ # Returns a list of this ComplexType's property names.
41
+ # @return [Array<String>]
42
+ def property_names
43
+ @property_names ||= properties.keys
44
+ end
45
+
46
+ # Returns the property class that implements this `ComplexType`.
47
+ # @return [Class < Frodo::Properties::Complex]
48
+ def property_class
49
+ @property_class ||= lambda { |type, complex_type|
50
+ klass = Class.new ::Frodo::Properties::Complex
51
+ klass.send(:define_method, :type) { type }
52
+ klass.send(:define_method, :complex_type) { complex_type }
53
+ klass
54
+ }.call(type, self)
55
+ end
56
+
57
+ private
58
+
59
+ def schema
60
+ @schema
61
+ end
62
+
63
+ def service
64
+ @schema.service
65
+ end
66
+
67
+ def type_definition
68
+ @type_definition
69
+ end
70
+
71
+ def collect_properties
72
+ Hash[type_definition.xpath('./Property').map do |property_xml|
73
+ property_name, property = schema.send(:process_property_from_xml, property_xml)
74
+ [property_name, property]
75
+ end]
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,95 @@
1
+ module Frodo
2
+ class Schema
3
+ # Enumeration types are nominal types that represent a series of related values.
4
+ # Enumeration types expose these related values as members of the enumeration.
5
+ class EnumType
6
+ # Creates a new EnumType based on the supplied options.
7
+ # @param type_xml [Nokogiri::XML::Element]
8
+ # @param service [Frodo::Service]
9
+ # @return [self]
10
+ def initialize(type_definition, schema)
11
+ @type_definition = type_definition
12
+ @schema = schema
13
+ end
14
+
15
+ # The name of the EnumType
16
+ # @return [String]
17
+ def name
18
+ options['Name']
19
+ end
20
+
21
+ # Returns the namespaced type for the EnumType.
22
+ # @return [String]
23
+ def type
24
+ "#{namespace}.#{name}"
25
+ end
26
+
27
+ # Whether this EnumType supports setting multiple values.
28
+ # @return [Boolean]
29
+ def is_flags?
30
+ options['IsFlags'] == 'true'
31
+ end
32
+
33
+ # The underlying type of this EnumType.
34
+ # @return [String]
35
+ def underlying_type
36
+ options['UnderlyingType'] || 'Edm.Int32'
37
+ end
38
+
39
+ # Returns the namespace this EnumType belongs to.
40
+ # @return [String]
41
+ def namespace
42
+ @namespace ||= service.namespace
43
+ end
44
+
45
+ # Returns the members of this EnumType and their values.
46
+ # @return [Hash]
47
+ def members
48
+ @members ||= collect_members
49
+ end
50
+
51
+ # Returns the property class that implements this `EnumType`.
52
+ # @return [Class < Frodo::Properties::Enum]
53
+ def property_class
54
+ @property_class ||= lambda { |type, members, is_flags|
55
+ klass = Class.new ::Frodo::Properties::Enum
56
+ klass.send(:define_method, :type) { type }
57
+ klass.send(:define_method, :members) { members }
58
+ klass.send(:define_method, :is_flags?) { is_flags }
59
+ klass
60
+ }.call(type, members, is_flags?)
61
+ end
62
+
63
+ # Returns the value of the requested member.
64
+ # @param member_name [to_s]
65
+ # @return [*]
66
+ def [](member_name)
67
+ members.invert[member_name.to_s]
68
+ end
69
+
70
+ private
71
+
72
+ def service
73
+ @schema.service
74
+ end
75
+
76
+ def type_definition
77
+ @type_definition
78
+ end
79
+
80
+ def options
81
+ @options = type_definition.attributes.map do |name, attr|
82
+ [name, attr.value]
83
+ end.to_h
84
+ end
85
+
86
+ def collect_members
87
+ Hash[type_definition.xpath('./Member').map.with_index do |member_xml, index|
88
+ member_name = member_xml.attributes['Name'].value
89
+ member_value = member_xml.attributes['Value'].andand.value.andand.to_i
90
+ [member_value || index, member_name]
91
+ end]
92
+ end
93
+ end
94
+ end
95
+ end