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,14 @@
1
+ module FrOData
2
+ module Properties
3
+ # Defines common behavior for FrOData numeric types.
4
+ module Number
5
+ private
6
+
7
+ def validate(value)
8
+ if value > max_value || value < min_value
9
+ validation_error "Value is outside accepted range: #{min_value} to #{max_value}"
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,72 @@
1
+ module FrOData
2
+ module Properties
3
+ # Defines the String FrOData type.
4
+ class String < FrOData::Property
5
+ # Returns the property value, properly typecast
6
+ # @return [String,nil]
7
+ def value
8
+ if (@value.nil? || @value.empty?) && allows_nil?
9
+ nil
10
+ else
11
+ encode_value(@value)
12
+ end
13
+ end
14
+
15
+ # Sets the property value
16
+ # @params new_value [to_s,nil]
17
+ def value=(new_value)
18
+ validate(new_value)
19
+ @value = new_value.nil? ? nil : encode_value(new_value.to_s)
20
+ end
21
+
22
+ # Value to be used in URLs.
23
+ # @return [String]
24
+ def url_value
25
+ "'#{value}'"
26
+ end
27
+
28
+ # The FrOData type name
29
+ def type
30
+ 'Edm.String'
31
+ end
32
+
33
+ # Is the property value Unicode encoded
34
+ def is_unicode?
35
+ options[:unicode]
36
+ end
37
+
38
+ # Does the property have a default value
39
+ def has_default_value?
40
+ not(options[:default_value].nil?)
41
+ end
42
+
43
+ # The default value for the property
44
+ def default_value
45
+ options[:default_value]
46
+ end
47
+
48
+ private
49
+
50
+ def default_options
51
+ super.merge({
52
+ unicode: true,
53
+ default_value: nil
54
+ })
55
+ end
56
+
57
+ def validate(new_value)
58
+ if new_value.nil? && !allows_nil?
59
+ validation_error 'This property does not allow for nil values to be set'
60
+ end
61
+ end
62
+
63
+ def encode_value(new_value)
64
+ if options[:unicode]
65
+ new_value.encode(Encoding::UTF_8)
66
+ else
67
+ new_value.encode(Encoding::ASCII)
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,40 @@
1
+ module FrOData
2
+ module Properties
3
+ # Defines the Time FrOData type.
4
+ class Time < FrOData::Property
5
+ # Returns the property value, properly typecast
6
+ # @return [Time,nil]
7
+ def value
8
+ if (@value.nil? || @value.empty?) && allows_nil?
9
+ nil
10
+ else
11
+ ::Time.strptime(@value, '%H:%M:%S%:z')
12
+ end
13
+ end
14
+
15
+ # Sets the property value
16
+ # @params new_value [Time]
17
+ def value=(new_value)
18
+ validate(new_value)
19
+ @value = parse_value(new_value)
20
+ end
21
+
22
+ # The FrOData type name
23
+ def type
24
+ 'Edm.Time'
25
+ end
26
+
27
+ private
28
+
29
+ def validate(value)
30
+ unless value.is_a?(::Time)
31
+ validation_error 'Value is not a time object'
32
+ end
33
+ end
34
+
35
+ def parse_value(value)
36
+ value.strftime('%H:%M:%S%:z')
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,27 @@
1
+ require 'frodata/properties/date_time'
2
+
3
+ module FrOData
4
+ module Properties
5
+ # Defines the Date FrOData type.
6
+ class TimeOfDay < FrOData::Properties::DateTime
7
+ # The FrOData type name
8
+ def type
9
+ 'Edm.TimeOfDay'
10
+ end
11
+
12
+ def url_value
13
+ @value
14
+ end
15
+
16
+ protected
17
+
18
+ def date_class
19
+ ::Time
20
+ end
21
+
22
+ def strptime_format
23
+ '%H:%M:%S.%L'
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,139 @@
1
+ module FrOData
2
+ # FrOData::Property represents an abstract property, defining the basic
3
+ # interface and required methods, with some default implementations. All
4
+ # supported property types should be implemented under the FrOData::Properties
5
+ # namespace.
6
+ class Property
7
+ # The property's name
8
+ attr_reader :name
9
+ # The property's value
10
+ attr_accessor :value
11
+ # The property's options
12
+ attr_reader :options
13
+
14
+ # Default intialization for a property with a name, value and options.
15
+ # @param name [to_s]
16
+ # @param value [to_s,nil]
17
+ # @param options [Hash]
18
+ def initialize(name, value, options = {})
19
+ @name = name.to_s
20
+ @value = value.nil? ? nil : value.to_s
21
+ @options = default_options.merge(options)
22
+ end
23
+
24
+ # Abstract implementation, should return property type, based on
25
+ # FrOData::Service metadata in proper implementation.
26
+ # @raise NotImplementedError
27
+ def type
28
+ raise NotImplementedError
29
+ end
30
+
31
+ # Provides for value-based equality checking.
32
+ # @param other [value] object for comparison
33
+ # @return [Boolean]
34
+ def ==(other)
35
+ self.value == other.value
36
+ end
37
+
38
+ # Whether the property permits a nil value.
39
+ # (Default=true)
40
+ # @return [Boolean]
41
+ def allows_nil?
42
+ options[:allows_nil]
43
+ end
44
+
45
+ # Whether the property uses strict validation.
46
+ # (Default=false)
47
+ # @return [Boolean]
48
+ def strict?
49
+ if options.key? :strict
50
+ options[:strict]
51
+ elsif service
52
+ service.options[:strict]
53
+ else
54
+ true
55
+ end
56
+ end
57
+
58
+ # The configured concurrency mode for the property.
59
+ # @return [String]
60
+ def concurrency_mode
61
+ @concurrency_mode ||= options[:concurrency_mode]
62
+ end
63
+
64
+ # Value to be used in XML.
65
+ # @return [String]
66
+ def xml_value
67
+ @value
68
+ end
69
+
70
+ # Value to be used in JSON.
71
+ # @return [*]
72
+ def json_value
73
+ value
74
+ end
75
+
76
+ # Value to be used in URLs.
77
+ # @return [String]
78
+ def url_value
79
+ @value
80
+ end
81
+
82
+ # Returns the XML representation of the property to the supplied XML
83
+ # builder.
84
+ # @param xml_builder [Nokogiri::XML::Builder]
85
+ def to_xml(xml_builder)
86
+ attributes = {
87
+ 'metadata:type' => type,
88
+ }
89
+
90
+ if value.nil?
91
+ attributes['metadata:null'] = 'true'
92
+ xml_builder['data'].send(name.to_sym, attributes)
93
+ else
94
+ xml_builder['data'].send(name.to_sym, attributes, xml_value)
95
+ end
96
+ end
97
+
98
+ # Creates a new property instance from an XML element
99
+ # @param property_xml [Nokogiri::XML::Element]
100
+ # @param options [Hash]
101
+ # @return [FrOData::Property]
102
+ def self.from_xml(property_xml, options = {})
103
+ if property_xml.attributes['null'].andand.value == 'true'
104
+ content = nil
105
+ else
106
+ content = property_xml.content
107
+ end
108
+
109
+ new(property_xml.name, content, options)
110
+ end
111
+
112
+ protected
113
+
114
+ def default_options
115
+ {
116
+ allows_nil: true,
117
+ concurrency_mode: :none
118
+ }
119
+ end
120
+
121
+ def service
122
+ options[:service]
123
+ end
124
+
125
+ def logger
126
+ # Use a dummy logger if service is not available (-> unit tests)
127
+ @logger ||= service.andand.logger || Logger.new('/dev/null')
128
+ end
129
+
130
+ def validation_error(message)
131
+ if strict?
132
+ raise ArgumentError, "#{name}: #{message}"
133
+ else
134
+ logger.warn "#{name}: #{message}"
135
+ nil
136
+ end
137
+ end
138
+ end
139
+ end
@@ -0,0 +1,41 @@
1
+ require 'singleton'
2
+
3
+ module FrOData
4
+ # Provides a registry for keeping track of various property types used by
5
+ # FrOData.
6
+ class PropertyRegistry
7
+ include Singleton
8
+
9
+ # Add a property type to the registry
10
+ #
11
+ # @param type_name [String] property type name to register
12
+ # @param klass [Class] Ruby class to use for the specified type
13
+ def add(type_name, klass)
14
+ properties[type_name] = klass
15
+ end
16
+
17
+ # Lookup a property by name and get the Ruby class to use for its instances
18
+ #
19
+ # @param type_name [String] the type name to lookup
20
+ # @return [Class, nil] the proper class or nil
21
+ def [](type_name)
22
+ properties[type_name]
23
+ end
24
+
25
+ # (see #add)
26
+ def self.add(type_name, klass)
27
+ FrOData::PropertyRegistry.instance.add(type_name, klass)
28
+ end
29
+
30
+ # (see #[])
31
+ def self.[](type_name)
32
+ FrOData::PropertyRegistry.instance[type_name]
33
+ end
34
+
35
+ private
36
+
37
+ def properties
38
+ @properties ||= {}
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,233 @@
1
+ require 'frodata/query/criteria'
2
+ require 'frodata/query/in_batches'
3
+
4
+ module FrOData
5
+ # FrOData::Query provides the query interface for requesting Entities matching
6
+ # specific criteria from an FrOData::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 FrOData::EntitySet you want to query.
9
+ class Query
10
+ attr_reader :options
11
+
12
+ include InBatches
13
+
14
+ # Create a new Query for the provided EntitySet
15
+ # @param entity_set [FrOData::EntitySet]
16
+ # @param options [Hash] Query options
17
+ def initialize(entity_set, options = {})
18
+ @entity_set = entity_set
19
+ @options = options
20
+ setup_empty_criteria_set
21
+ end
22
+
23
+ # Instantiates an FrOData::Query::Criteria for the named property.
24
+ # @param property [to_s]
25
+ def [](property)
26
+ property_instance = @entity_set.new_entity.get_property(property)
27
+ property_instance = property if property_instance.nil?
28
+ FrOData::Query::Criteria.new(property: property_instance)
29
+ end
30
+
31
+ # Find the Entity with the supplied key value.
32
+ # @param key [to_s] primary key to lookup
33
+ # @return [FrOData::Entity,nil]
34
+ def find(key)
35
+ entity = @entity_set.new_entity
36
+ key_property = entity.get_property(entity.primary_key)
37
+ key_property.value = key
38
+
39
+ pathname = "#{entity_set.name}(#{key_property.url_value})"
40
+ query = [pathname, assemble_criteria].compact.join('?')
41
+ execute(query).first
42
+ end
43
+
44
+ # Adds a filter criteria to the query.
45
+ # For filter syntax see https://msdn.microsoft.com/en-us/library/gg309461.aspx
46
+ # Syntax:
47
+ # Property Operator Value
48
+ #
49
+ # For example:
50
+ # Name eq 'Customer Service'
51
+ #
52
+ # Operators:
53
+ # eq, ne, gt, ge, lt, le, and, or, not
54
+ #
55
+ # Value
56
+ # can be 'null', can use single quotes
57
+ # @param criteria
58
+ def where(criteria)
59
+ criteria_set[:filter] << criteria
60
+ self
61
+ end
62
+
63
+ # Adds a fulltext search term to the query
64
+ # NOTE: May not be implemented by the service
65
+ # @param term [String]
66
+ def search(term)
67
+ criteria_set[:search] << term
68
+ self
69
+ end
70
+
71
+ # Adds a filter criteria to the query with 'and' logical operator.
72
+ # @param criteria
73
+ #def and(criteria)
74
+ #
75
+ #end
76
+
77
+ # Adds a filter criteria to the query with 'or' logical operator.
78
+ # @param criteria
79
+ #def or(criteria)
80
+ #
81
+ #end
82
+
83
+ # Specify properties to order the result by.
84
+ # Can use 'desc' like 'Name desc'
85
+ # @param properties [Array<Symbol>]
86
+ # @return [self]
87
+ def order_by(*properties)
88
+ criteria_set[:orderby] += properties
89
+ self
90
+ end
91
+
92
+ # Specify associations to expand in the result.
93
+ # @param associations [Array<Symbol>]
94
+ # @return [self]
95
+ def expand(*associations)
96
+ criteria_set[:expand] += associations
97
+ self
98
+ end
99
+
100
+ # Specify properties to select within the result.
101
+ # @param properties [Array<Symbol>]
102
+ # @return [self]
103
+ def select(*properties)
104
+ criteria_set[:select] += properties
105
+ self
106
+ end
107
+
108
+ # Add skip criteria to query.
109
+ # @param value [to_i]
110
+ # @return [self]
111
+ def skip(value)
112
+ criteria_set[:skip] = value.to_i
113
+ self
114
+ end
115
+
116
+ # Add limit criteria to query.
117
+ # @param value [to_i]
118
+ # @return [self]
119
+ def limit(value)
120
+ criteria_set[:top] = value.to_i
121
+ self
122
+ end
123
+
124
+ # Add inline count criteria to query.
125
+ # Not Supported in CRM2011
126
+ # @return [self]
127
+ def include_count
128
+ criteria_set[:inline_count] = true
129
+ self
130
+ end
131
+
132
+ # Convert Query to string.
133
+ # @return [String]
134
+ def to_s
135
+ [entity_set.name, assemble_criteria].compact.join('?')
136
+ end
137
+
138
+ # Execute the query.
139
+ # @return [FrOData::Service::Response]
140
+ def execute(url_chunk = self.to_s)
141
+ service.execute(url_chunk, options.merge(query: self))
142
+ end
143
+
144
+ # Executes the query to get a count of entities.
145
+ # @return [Integer]
146
+ def count
147
+ url_chunk = ["#{entity_set.name}/$count", assemble_criteria].compact.join('?')
148
+ response = self.execute(url_chunk)
149
+ # Some servers (*cough* Microsoft *cough*) seem to
150
+ # return extraneous characters in the response.
151
+ response.body.scan(/\d+/).first.to_i
152
+ end
153
+
154
+ # Checks whether a query will return any results by calling #count
155
+ # @return [Boolean]
156
+ def empty?
157
+ self.count == 0
158
+ end
159
+
160
+ # The EntitySet for this query.
161
+ # @return [FrOData::EntitySet]
162
+ # @api private
163
+ def entity_set
164
+ @entity_set
165
+ end
166
+
167
+ # The service for this query
168
+ # @return [FrOData::Service]
169
+ # @api private
170
+ def service
171
+ @service ||= entity_set.service
172
+ end
173
+
174
+ private
175
+
176
+ def criteria_set
177
+ @criteria_set
178
+ end
179
+
180
+ def setup_empty_criteria_set
181
+ @criteria_set = {
182
+ filter: [],
183
+ search: [],
184
+ select: [],
185
+ expand: [],
186
+ orderby: [],
187
+ skip: 0,
188
+ top: 0,
189
+ inline_count: false
190
+ }
191
+ end
192
+
193
+ def assemble_criteria
194
+ criteria = [
195
+ filter_criteria,
196
+ search_criteria,
197
+ list_criteria(:orderby),
198
+ list_criteria(:expand),
199
+ list_criteria(:select),
200
+ inline_count_criteria,
201
+ paging_criteria(:skip),
202
+ paging_criteria(:top)
203
+ ].compact!
204
+
205
+ criteria.empty? ? nil : criteria.join('&')
206
+ end
207
+
208
+ def filter_criteria
209
+ return nil if criteria_set[:filter].empty?
210
+ filters = criteria_set[:filter].collect(&:to_s)
211
+ "$filter=#{filters.join(' and ')}"
212
+ end
213
+
214
+ def search_criteria
215
+ return nil if criteria_set[:search].empty?
216
+ filters = criteria_set[:search].collect(&:to_s)
217
+ "$search=#{filters.join(' AND ')}"
218
+ end
219
+
220
+ def list_criteria(name)
221
+ criteria_set[name].empty? ? nil : "$#{name}=#{criteria_set[name].join(',')}"
222
+ end
223
+
224
+ # inlinecount not supported by Microsoft CRM 2011
225
+ def inline_count_criteria
226
+ criteria_set[:inline_count] ? '$count=true' : nil
227
+ end
228
+
229
+ def paging_criteria(name)
230
+ criteria_set[name] == 0 ? nil : "$#{name}=#{criteria_set[name]}"
231
+ end
232
+ end
233
+ end