frodata 0.9.1

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 (128) hide show
  1. checksums.yaml +7 -0
  2. data/.autotest +2 -0
  3. data/.gitignore +24 -0
  4. data/.rspec +2 -0
  5. data/.ruby-gemset +1 -0
  6. data/.ruby-version +1 -0
  7. data/.travis.yml +75 -0
  8. data/CHANGELOG.md +150 -0
  9. data/Gemfile +4 -0
  10. data/LICENSE.txt +23 -0
  11. data/README.md +427 -0
  12. data/Rakefile +7 -0
  13. data/TODO.md +55 -0
  14. data/frodata.gemspec +34 -0
  15. data/lib/frodata.rb +36 -0
  16. data/lib/frodata/entity.rb +332 -0
  17. data/lib/frodata/entity_container.rb +75 -0
  18. data/lib/frodata/entity_set.rb +161 -0
  19. data/lib/frodata/errors.rb +68 -0
  20. data/lib/frodata/navigation_property.rb +29 -0
  21. data/lib/frodata/navigation_property/proxy.rb +80 -0
  22. data/lib/frodata/properties.rb +32 -0
  23. data/lib/frodata/properties/binary.rb +50 -0
  24. data/lib/frodata/properties/boolean.rb +37 -0
  25. data/lib/frodata/properties/collection.rb +50 -0
  26. data/lib/frodata/properties/complex.rb +114 -0
  27. data/lib/frodata/properties/date.rb +27 -0
  28. data/lib/frodata/properties/date_time.rb +83 -0
  29. data/lib/frodata/properties/date_time_offset.rb +17 -0
  30. data/lib/frodata/properties/decimal.rb +50 -0
  31. data/lib/frodata/properties/enum.rb +62 -0
  32. data/lib/frodata/properties/float.rb +67 -0
  33. data/lib/frodata/properties/geography.rb +13 -0
  34. data/lib/frodata/properties/geography/base.rb +162 -0
  35. data/lib/frodata/properties/geography/line_string.rb +33 -0
  36. data/lib/frodata/properties/geography/point.rb +31 -0
  37. data/lib/frodata/properties/geography/polygon.rb +38 -0
  38. data/lib/frodata/properties/guid.rb +17 -0
  39. data/lib/frodata/properties/integer.rb +107 -0
  40. data/lib/frodata/properties/number.rb +14 -0
  41. data/lib/frodata/properties/string.rb +72 -0
  42. data/lib/frodata/properties/time.rb +40 -0
  43. data/lib/frodata/properties/time_of_day.rb +27 -0
  44. data/lib/frodata/property.rb +139 -0
  45. data/lib/frodata/property_registry.rb +41 -0
  46. data/lib/frodata/query.rb +233 -0
  47. data/lib/frodata/query/criteria.rb +92 -0
  48. data/lib/frodata/query/criteria/comparison_operators.rb +49 -0
  49. data/lib/frodata/query/criteria/date_functions.rb +61 -0
  50. data/lib/frodata/query/criteria/geography_functions.rb +21 -0
  51. data/lib/frodata/query/criteria/lambda_operators.rb +27 -0
  52. data/lib/frodata/query/criteria/string_functions.rb +40 -0
  53. data/lib/frodata/query/in_batches.rb +58 -0
  54. data/lib/frodata/railtie.rb +19 -0
  55. data/lib/frodata/schema.rb +155 -0
  56. data/lib/frodata/schema/complex_type.rb +79 -0
  57. data/lib/frodata/schema/enum_type.rb +95 -0
  58. data/lib/frodata/service.rb +254 -0
  59. data/lib/frodata/service/request.rb +85 -0
  60. data/lib/frodata/service/response.rb +162 -0
  61. data/lib/frodata/service/response/atom.rb +40 -0
  62. data/lib/frodata/service/response/json.rb +41 -0
  63. data/lib/frodata/service/response/plain.rb +36 -0
  64. data/lib/frodata/service/response/xml.rb +40 -0
  65. data/lib/frodata/service_registry.rb +52 -0
  66. data/lib/frodata/version.rb +3 -0
  67. data/spec/fixtures/files/entity_to_xml.xml +17 -0
  68. data/spec/fixtures/files/error.xml +5 -0
  69. data/spec/fixtures/files/metadata.xml +150 -0
  70. data/spec/fixtures/files/product_0.json +10 -0
  71. data/spec/fixtures/files/product_0.xml +28 -0
  72. data/spec/fixtures/files/products.json +106 -0
  73. data/spec/fixtures/files/products.xml +308 -0
  74. data/spec/fixtures/files/supplier_0.json +26 -0
  75. data/spec/fixtures/files/supplier_0.xml +32 -0
  76. data/spec/fixtures/vcr_cassettes/entity_set_specs.yml +1635 -0
  77. data/spec/fixtures/vcr_cassettes/entity_set_specs/bad_entry.yml +183 -0
  78. data/spec/fixtures/vcr_cassettes/entity_set_specs/existing_entry.yml +256 -0
  79. data/spec/fixtures/vcr_cassettes/entity_set_specs/new_entry.yml +185 -0
  80. data/spec/fixtures/vcr_cassettes/entity_specs.yml +285 -0
  81. data/spec/fixtures/vcr_cassettes/navigation_property_proxy_specs.yml +346 -0
  82. data/spec/fixtures/vcr_cassettes/query/result_specs.yml +189 -0
  83. data/spec/fixtures/vcr_cassettes/query_specs.yml +1060 -0
  84. data/spec/fixtures/vcr_cassettes/schema/complex_type_specs.yml +127 -0
  85. data/spec/fixtures/vcr_cassettes/service/request_specs.yml +193 -0
  86. data/spec/fixtures/vcr_cassettes/service_registry_specs.yml +129 -0
  87. data/spec/fixtures/vcr_cassettes/service_specs.yml +127 -0
  88. data/spec/fixtures/vcr_cassettes/usage_example_specs.yml +1330 -0
  89. data/spec/frodata/entity/shared_examples.rb +82 -0
  90. data/spec/frodata/entity_container_spec.rb +38 -0
  91. data/spec/frodata/entity_set_spec.rb +168 -0
  92. data/spec/frodata/entity_spec.rb +151 -0
  93. data/spec/frodata/errors_spec.rb +48 -0
  94. data/spec/frodata/navigation_property/proxy_spec.rb +44 -0
  95. data/spec/frodata/navigation_property_spec.rb +55 -0
  96. data/spec/frodata/properties/binary_spec.rb +50 -0
  97. data/spec/frodata/properties/boolean_spec.rb +72 -0
  98. data/spec/frodata/properties/collection_spec.rb +44 -0
  99. data/spec/frodata/properties/date_spec.rb +23 -0
  100. data/spec/frodata/properties/date_time_offset_spec.rb +30 -0
  101. data/spec/frodata/properties/date_time_spec.rb +23 -0
  102. data/spec/frodata/properties/decimal_spec.rb +51 -0
  103. data/spec/frodata/properties/float_spec.rb +45 -0
  104. data/spec/frodata/properties/geography/line_string_spec.rb +33 -0
  105. data/spec/frodata/properties/geography/point_spec.rb +29 -0
  106. data/spec/frodata/properties/geography/polygon_spec.rb +55 -0
  107. data/spec/frodata/properties/geography/shared_examples.rb +72 -0
  108. data/spec/frodata/properties/guid_spec.rb +17 -0
  109. data/spec/frodata/properties/integer_spec.rb +58 -0
  110. data/spec/frodata/properties/string_spec.rb +46 -0
  111. data/spec/frodata/properties/time_of_day_spec.rb +23 -0
  112. data/spec/frodata/properties/time_spec.rb +15 -0
  113. data/spec/frodata/property_registry_spec.rb +16 -0
  114. data/spec/frodata/property_spec.rb +71 -0
  115. data/spec/frodata/query/criteria_spec.rb +229 -0
  116. data/spec/frodata/query_spec.rb +199 -0
  117. data/spec/frodata/schema/complex_type_spec.rb +96 -0
  118. data/spec/frodata/schema/enum_type_spec.rb +112 -0
  119. data/spec/frodata/schema_spec.rb +97 -0
  120. data/spec/frodata/service/request_spec.rb +49 -0
  121. data/spec/frodata/service/response_spec.rb +85 -0
  122. data/spec/frodata/service_registry_spec.rb +18 -0
  123. data/spec/frodata/service_spec.rb +191 -0
  124. data/spec/frodata/usage_example_spec.rb +188 -0
  125. data/spec/spec_helper.rb +32 -0
  126. data/spec/support/coverage.rb +2 -0
  127. data/spec/support/vcr.rb +9 -0
  128. metadata +401 -0
@@ -0,0 +1,92 @@
1
+ require 'frodata/query/criteria/comparison_operators'
2
+ require 'frodata/query/criteria/string_functions'
3
+ require 'frodata/query/criteria/date_functions'
4
+ require 'frodata/query/criteria/geography_functions'
5
+ require 'frodata/query/criteria/lambda_operators'
6
+
7
+ module FrOData
8
+ class Query
9
+ # Represents a discreet criteria within an FrOData::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,49 @@
1
+ module FrOData
2
+ class Query
3
+ class Criteria
4
+ module ComparisonOperators
5
+ # Sets up equality operator.
6
+ # @param value [to_s]
7
+ # @return [self]
8
+ def eq(value)
9
+ set_operator_and_value(:eq, value)
10
+ end
11
+
12
+ # Sets up non-equality operator.
13
+ # @param value [to_s]
14
+ # @return [self]
15
+ def ne(value)
16
+ set_operator_and_value(:ne, value)
17
+ end
18
+
19
+ # Sets up greater-than operator.
20
+ # @param value [to_s]
21
+ # @return [self]
22
+ def gt(value)
23
+ set_operator_and_value(:gt, value)
24
+ end
25
+
26
+ # Sets up greater-than-or-equal operator.
27
+ # @param value [to_s]
28
+ # @return [self]
29
+ def ge(value)
30
+ set_operator_and_value(:ge, value)
31
+ end
32
+
33
+ # Sets up less-than operator.
34
+ # @param value [to_s]
35
+ # @return [self]
36
+ def lt(value)
37
+ set_operator_and_value(:lt, value)
38
+ end
39
+
40
+ # Sets up less-than-or-equal operator.
41
+ # @param value [to_s]
42
+ # @return [self]
43
+ def le(value)
44
+ set_operator_and_value(:le, value)
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,61 @@
1
+ module FrOData
2
+ class Query
3
+ class Criteria
4
+ module DateFunctions
5
+ # Applies the `year` function.
6
+ # @return [self]
7
+ def year
8
+ set_function_and_argument(:year, nil)
9
+ end
10
+
11
+ # Applies the `month` function.
12
+ # @return [self]
13
+ def month
14
+ set_function_and_argument(:month, nil)
15
+ end
16
+
17
+ # Applies the `day` function.
18
+ # @return [self]
19
+ def day
20
+ set_function_and_argument(:day, nil)
21
+ end
22
+
23
+ # Applies the `hour` function.
24
+ # @return [self]
25
+ def hour
26
+ set_function_and_argument(:hour, nil)
27
+ end
28
+
29
+ # Applies the `minute` function.
30
+ # @return [self]
31
+ def minute
32
+ set_function_and_argument(:minute, nil)
33
+ end
34
+
35
+ # Applies the `second` function.
36
+ # @return [self]
37
+ def second
38
+ set_function_and_argument(:second, nil)
39
+ end
40
+
41
+ # Applies the `fractionalseconds` function.
42
+ # @return [self]
43
+ def fractionalseconds
44
+ set_function_and_argument(:fractionalseconds, nil)
45
+ end
46
+
47
+ # Applies the `date` function.
48
+ # @return [self]
49
+ def date
50
+ set_function_and_argument(:date, nil)
51
+ end
52
+
53
+ # Applies the `time` function.
54
+ # @return [self]
55
+ def time
56
+ set_function_and_argument(:time, nil)
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,21 @@
1
+ module FrOData
2
+ class Query
3
+ class Criteria
4
+ module GeographyFunctions
5
+ # Applies the `geo.distance` function.
6
+ # @param to [to_s]
7
+ # @return [self]
8
+ def distance(to)
9
+ set_function_and_argument(:'geo.distance', to)
10
+ end
11
+
12
+ # Applies the `geo.intersects` function.
13
+ # @param what [to_s]
14
+ # @return [self]
15
+ def intersects(what)
16
+ set_function_and_argument(:'geo.intersects', what)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,27 @@
1
+ module FrOData
2
+ class Query
3
+ class Criteria
4
+ module LambdaOperators
5
+ # Applies the `any` lambda operator to the given property
6
+ # @param property [to_s]
7
+ # @return [self]
8
+ def any(property)
9
+ set_function_and_argument(:any, property)
10
+ end
11
+
12
+ # Applies the `any` lambda operator to the given property
13
+ # @param property [to_s]
14
+ # @return [self]
15
+ def all(property)
16
+ set_function_and_argument(:all, property)
17
+ end
18
+
19
+ private
20
+
21
+ def lambda_operator?
22
+ [:any, :all].include?(function)
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,40 @@
1
+ module FrOData
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,58 @@
1
+ module FrOData
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 `FrOData::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? FrOData::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? FrOData::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,19 @@
1
+ module FrOData
2
+ class Railtie < Rails::Railtie
3
+ config.before_initialize do
4
+ ::FrOData::Railtie.load_configuration!
5
+ ::FrOData::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
+ # FrOData::ServiceRegistry accordingly.
15
+ def self.setup_service_registry!
16
+ # TODO Populate FrOData::ServiceRegistry based on configuration
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,155 @@
1
+ require 'frodata/schema/complex_type'
2
+ require 'frodata/schema/enum_type'
3
+
4
+ module FrOData
5
+ class Schema
6
+ # The schema's parent service
7
+ attr_reader :service
8
+ # The schema's metadata (i.e its XML definition)
9
+ attr_reader :metadata
10
+
11
+ # Creates a new schema.
12
+ #
13
+ # @param schema_definition [Nokogiri::XML] The schema's XML definition
14
+ # @param service [FrOData::Service] The schema's parent service
15
+ def initialize(schema_definition, service)
16
+ @metadata = schema_definition
17
+ @service = service
18
+ end
19
+
20
+ # Returns the schema's `Namespace` attribute (mandatory).
21
+ # @return [String]
22
+ def namespace
23
+ @namespace ||= metadata.attributes['Namespace'].value
24
+ end
25
+
26
+ # Returns a list of actions defined by the schema.
27
+ # @return [Array<String>]
28
+ def actions
29
+ @actions ||= metadata.xpath('//Action').map do |action|
30
+ action.attributes['Name'].value
31
+ end
32
+ end
33
+
34
+ # Returns a list of entities defined by the schema.
35
+ # @return [Array<String>]
36
+ def entity_types
37
+ @entity_types ||= metadata.xpath('//EntityType').map do |entity|
38
+ entity.attributes['Name'].value
39
+ end
40
+ end
41
+
42
+ # Returns a list of `ComplexType`s defined by the schema.
43
+ # @return [Hash<String, FrOData::Schema::ComplexType>]
44
+ def complex_types
45
+ @complex_types ||= metadata.xpath('//ComplexType').map do |entity|
46
+ [
47
+ entity.attributes['Name'].value,
48
+ ComplexType.new(entity, self)
49
+ ]
50
+ end.to_h
51
+ end
52
+
53
+ # Returns a list of EnumTypes defined by the schema.
54
+ # @return [Hash<String, FrOData::Schema::EnumType>]
55
+ def enum_types
56
+ @enum_types ||= metadata.xpath('//EnumType').map do |entity|
57
+ [
58
+ entity.attributes['Name'].value,
59
+ EnumType.new(entity, self)
60
+ ]
61
+ end.to_h
62
+ end
63
+
64
+ # Returns a list of functions defined by the schema.
65
+ # @return [Array<String>]
66
+ def functions
67
+ @functions ||= metadata.xpath('//Function').map do |function|
68
+ function.attributes['Name'].value
69
+ end
70
+ end
71
+
72
+ # Returns a list of type definitions defined by the schema.
73
+ # @return [Array<String>]
74
+ def type_definitions
75
+ @typedefs ||= metadata.xpath('//TypeDefinition').map do |typedef|
76
+ typedef.attributes['Name'].value
77
+ end
78
+ end
79
+
80
+ # Returns a hash for finding an association through an entity type's defined
81
+ # NavigationProperty elements.
82
+ # @return [Hash<Hash<FrOData::NavigationProperty>>]
83
+ def navigation_properties
84
+ @navigation_properties ||= metadata.xpath('//EntityType').map do |entity_type_def|
85
+ [
86
+ entity_type_def.attributes['Name'].value,
87
+ entity_type_def.xpath('./NavigationProperty').map do |nav_property_def|
88
+ [
89
+ nav_property_def.attributes['Name'].value,
90
+ ::FrOData::NavigationProperty.build(nav_property_def)
91
+ ]
92
+ end.to_h
93
+ ]
94
+ end.to_h
95
+ end
96
+
97
+ # Get the property type for an entity from metadata.
98
+ #
99
+ # @param entity_name [to_s] the name of the relevant entity
100
+ # @param property_name [to_s] the property name needed
101
+ # @return [String] the name of the property's type
102
+ def get_property_type(entity_name, property_name)
103
+ metadata.xpath("//EntityType[@Name='#{entity_name}']/Property[@Name='#{property_name}']").first.attributes['Type'].value
104
+ end
105
+
106
+ # Get the primary key for the supplied Entity.
107
+ #
108
+ # @param entity_name [to_s]
109
+ # @return [String]
110
+ def primary_key_for(entity_name)
111
+ metadata.xpath("//EntityType[@Name='#{entity_name}']/Key/PropertyRef").first.attributes['Name'].value
112
+ end
113
+
114
+ # Get the list of properties and their various options for the supplied
115
+ # Entity name.
116
+ # @param entity_name [to_s]
117
+ # @return [Hash]
118
+ # @api private
119
+ def properties_for_entity(entity_name)
120
+ type_definition = metadata.xpath("//EntityType[@Name='#{entity_name}']").first
121
+ raise ArgumentError, "Unknown EntityType: #{entity_name}" if type_definition.nil?
122
+ properties_to_return = {}
123
+ type_definition.xpath('./Property').each do |property_xml|
124
+ property_name, property = process_property_from_xml(property_xml)
125
+ properties_to_return[property_name] = property
126
+ end
127
+ properties_to_return
128
+ end
129
+
130
+ private
131
+
132
+ def process_property_from_xml(property_xml)
133
+ property_name = property_xml.attributes['Name'].value
134
+ property_type = property_xml.attributes['Type'].value
135
+ property_options = { service: service }
136
+
137
+ property_type, value_type = property_type.split(/\(|\)/)
138
+ if property_type == 'Collection'
139
+ klass = ::FrOData::Properties::Collection
140
+ property_options.merge(value_type: value_type)
141
+ else
142
+ klass = ::FrOData::PropertyRegistry[property_type]
143
+ end
144
+
145
+ if klass.nil?
146
+ raise RuntimeError, "Unknown property type: #{value_type}"
147
+ else
148
+ property_options[:allows_nil] = false if property_xml.attributes['Nullable'] == 'false'
149
+ property = klass.new(property_name, nil, property_options)
150
+ end
151
+
152
+ return [property_name, property]
153
+ end
154
+ end
155
+ end