bodhi-slam 0.3.4 → 0.4.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2ea629d70a0b01fca9df643eb9477d625e3c5d47
4
- data.tar.gz: f4057f1569b8d14baa709c439976af61a5b9b6fa
3
+ metadata.gz: 6df30b96a6aec463e6caa1ccbd279e47d27a57a2
4
+ data.tar.gz: 6852921c1be4570e84dab5869713af2f2f95bd81
5
5
  SHA512:
6
- metadata.gz: 1d89be865cf3730e3567da45b32b0ee2611524d66d08866ab1a885b48db02d1bb7405d0544e7511e9771a6662099be3d025023c340b6cf5212f337267460030c
7
- data.tar.gz: 12b9b414770e1ba9677b0bb58436afd6958e2eec9fcf02a87e8398b6dd91a009867e8019a12e327d58ea48be71bd2bee5ceccf9b5727776530a664751b740b96
6
+ metadata.gz: 7a04d8bf2df4d929e2d533283610f9d93eef65479383250d93c91e4cd22a27ae2b1ed14fdbe3c476b1576561acd85b84bd19e0d9479c18c7a88a2e66ff892d6c
7
+ data.tar.gz: 1df3159b31651ba636903099ac5f7ac5c1dd85bca80866c7de94be3ba1be5ac52c7e34e387ecd1a1cd591a770f44f8b714d7356e6eeee081edc30395720e79eb
@@ -4,6 +4,7 @@ require "json"
4
4
  require "time"
5
5
  require "SecureRandom"
6
6
  require 'regexp-examples'
7
+ require 'active_model'
7
8
 
8
9
  require 'bodhi-slam/validations'
9
10
  require 'bodhi-slam/errors'
@@ -16,6 +17,7 @@ require 'bodhi-slam/resource'
16
17
  require 'bodhi-slam/types'
17
18
  require 'bodhi-slam/users'
18
19
  require 'bodhi-slam/profiles'
20
+ require 'bodhi-slam/queries'
19
21
 
20
22
  class BodhiSlam
21
23
  # Defines a context to interact with the Bodhi API
@@ -0,0 +1,209 @@
1
+ module Bodhi
2
+ class Query
3
+ attr_reader :klass, :url, :context, :criteria, :fields, :paging, :sorting
4
+
5
+ def initialize(klass)
6
+ @klass = Object.const_get(klass.to_s)
7
+ @criteria = []
8
+ @fields = []
9
+ @paging = {}
10
+ @sorting = {}
11
+ end
12
+
13
+ def clear!
14
+ @context = nil
15
+ @criteria.clear
16
+ @fields.clear
17
+ @paging.clear
18
+ @sorting.clear
19
+ end
20
+
21
+ def url
22
+ unless context.nil?
23
+ query = "/#{context.namespace}/resources/#{klass}?"
24
+ else
25
+ query = "/resources/#{klass}?"
26
+ end
27
+ params = []
28
+
29
+ unless criteria.empty?
30
+ if criteria.size > 1
31
+ where_string = "{$and:[#{criteria.join(',')}]}"
32
+ else
33
+ where_string = criteria.join
34
+ end
35
+
36
+ params << "where=#{where_string}"
37
+ end
38
+
39
+ unless fields.empty?
40
+ params << "fields=#{fields.join(',')}"
41
+ end
42
+
43
+ unless paging.empty?
44
+ paging_params = []
45
+
46
+ if paging[:page]
47
+ paging_params << "page:#{paging[:page]}"
48
+ end
49
+
50
+ if paging[:limit]
51
+ paging_params << "limit:#{paging[:limit]}"
52
+ end
53
+
54
+ params << "paging=#{paging_params.join(',')}"
55
+ end
56
+
57
+ unless sorting.empty?
58
+ sort_params = []
59
+
60
+ if sorting[:field]
61
+ sort_params << sorting[:field]
62
+ end
63
+
64
+ if sorting[:order]
65
+ sort_params << sorting[:order]
66
+ end
67
+
68
+ params << "sort=#{sort_params.join(':')}"
69
+ end
70
+
71
+ query << params.join('&')
72
+ query.gsub(/\s+/, "")
73
+ end
74
+
75
+ def all
76
+ if context.nil?
77
+ raise ArgumentError.new("a Bodhi::Context is required to query the API")
78
+ end
79
+
80
+ if context.invalid?
81
+ raise Bodhi::ContextErrors.new(context.errors.messages), context.errors.to_a.to_s
82
+ end
83
+
84
+ result = context.connection.get do |request|
85
+ request.url self.url
86
+ request.headers[context.credentials_header] = context.credentials
87
+ end
88
+
89
+ if result.status != 200
90
+ raise Bodhi::ApiErrors.new(body: result.body, status: result.status), "status: #{result.status}, body: #{result.body}"
91
+ end
92
+
93
+ resources = JSON.parse(result.body)
94
+ resources.map{ |attributes| klass.factory.build(context, attributes) }
95
+ end
96
+
97
+ def first
98
+ if context.nil?
99
+ raise ArgumentError.new("a Bodhi::Context is required to query the API")
100
+ end
101
+
102
+ if context.invalid?
103
+ raise Bodhi::ContextErrors.new(context.errors.messages), context.errors.to_a.to_s
104
+ end
105
+
106
+ result = context.connection.get do |request|
107
+ request.url self.url
108
+ request.headers[context.credentials_header] = context.credentials
109
+ end
110
+
111
+ if result.status != 200
112
+ raise Bodhi::ApiErrors.new(body: result.body, status: result.status), "status: #{result.status}, body: #{result.body}"
113
+ end
114
+
115
+ resources = JSON.parse(result.body)
116
+ resources.map{ |attributes| klass.factory.build(context, attributes) }.first
117
+ end
118
+
119
+ def last
120
+ if context.nil?
121
+ raise ArgumentError.new("a Bodhi::Context is required to query the API")
122
+ end
123
+
124
+ if context.invalid?
125
+ raise Bodhi::ContextErrors.new(context.errors.messages), context.errors.to_a.to_s
126
+ end
127
+
128
+ result = context.connection.get do |request|
129
+ request.url self.url
130
+ request.headers[context.credentials_header] = context.credentials
131
+ end
132
+
133
+ if result.status != 200
134
+ raise Bodhi::ApiErrors.new(body: result.body, status: result.status), "status: #{result.status}, body: #{result.body}"
135
+ end
136
+
137
+ resources = JSON.parse(result.body)
138
+ resources.map{ |attributes| klass.factory.build(context, attributes) }.last
139
+ end
140
+
141
+ def from(context)
142
+ unless context.is_a? Bodhi::Context
143
+ raise ArgumentError.new("Expected Bodhi::Context but received #{context.class}")
144
+ end
145
+
146
+ @context = context
147
+ self
148
+ end
149
+
150
+ def where(query)
151
+ unless query.is_a? String
152
+ raise ArgumentError.new("Expected String but received #{query.class}")
153
+ end
154
+
155
+ @criteria << query
156
+ @criteria.uniq!
157
+ self
158
+ end
159
+ alias :and :where
160
+
161
+ def select(field_names)
162
+ unless field_names.is_a? String
163
+ raise ArgumentError.new("Expected String but received #{field_names.class}")
164
+ end
165
+
166
+ fields_array = field_names.split(',')
167
+ @fields << fields_array
168
+ @fields.flatten!
169
+ @fields.uniq!
170
+ self
171
+ end
172
+
173
+ def limit(number)
174
+ unless number.is_a? Integer
175
+ raise ArgumentError.new("Expected Integer but received #{number.class}")
176
+ end
177
+
178
+ unless number <= 100
179
+ raise ArgumentError.new("Expected limit to be less than or equal to 100 but received #{number}")
180
+ end
181
+
182
+ @paging[:limit] = number
183
+ self
184
+ end
185
+
186
+ def page(number)
187
+ unless number.is_a? Integer
188
+ raise ArgumentError.new("Expected Integer but received #{number.class}")
189
+ end
190
+
191
+ @paging[:page] = number
192
+ self
193
+ end
194
+
195
+ def sort(field, order=nil)
196
+ unless field.is_a? String
197
+ raise ArgumentError.new("Expected String but received #{field.class}")
198
+ end
199
+
200
+ unless order.nil?
201
+ @sorting[:order] = order
202
+ end
203
+
204
+ @sorting[:field] = field
205
+ self
206
+ end
207
+
208
+ end
209
+ end
@@ -1,5 +1,6 @@
1
1
  module Bodhi
2
2
  module Resource
3
+
3
4
  SYSTEM_ATTRIBUTES = [:sys_created_at, :sys_version, :sys_modified_at, :sys_modified_by,
4
5
  :sys_namespace, :sys_created_by, :sys_type_version, :sys_id, :sys_embeddedType]
5
6
  SUPPORT_ATTRIBUTES = [:bodhi_context, :errors]
@@ -62,8 +63,9 @@ module Bodhi
62
63
  raise Bodhi::ApiErrors.new(body: result.body, status: result.status), "status: #{result.status}, body: #{result.body}"
63
64
  end
64
65
 
65
- resource_attributes = JSON.parse(result.body)
66
- factory.build(context, resource_attributes)
66
+ record = Object.const_get(name).new(JSON.parse(result.body))
67
+ record.bodhi_context = context
68
+ record
67
69
  end
68
70
 
69
71
  # Returns all records of the given resource from the Bodhi Cloud.
@@ -92,7 +94,7 @@ module Bodhi
92
94
  records << JSON.parse(result.body)
93
95
  end while records.size == 100
94
96
 
95
- records.flatten.collect{ |record| factory.build(record) }
97
+ records.flatten.collect{ |record| Object.const_get(name).new(record) }
96
98
  end
97
99
 
98
100
  # Aggregates the given resource based on the supplied +pipeline+
@@ -120,30 +122,15 @@ module Bodhi
120
122
  JSON.parse(result.body)
121
123
  end
122
124
 
123
- # Returns all records for a resource which match the given +query+
125
+ # Returns a Bodhi::Query object for quering the given Resource
124
126
  #
125
127
  # context = Bodhi::Context.new
126
- # Resource.where(context, "{property: 'value'}")
127
- def where(context, query)
128
- if context.invalid?
129
- raise Bodhi::ContextErrors.new(context.errors.messages), context.errors.to_a.to_s
130
- end
131
-
132
- unless query.is_a? String
133
- raise ArgumentError.new("Expected 'query' to be a String. 'query' #=> #{query.class}")
134
- end
135
-
136
- result = context.connection.get do |request|
137
- request.url "/#{context.namespace}/resources/#{name}?where=#{query}"
138
- request.headers[context.credentials_header] = context.credentials
139
- end
140
-
141
- if result.status != 200
142
- raise Bodhi::ApiErrors.new(body: result.body, status: result.status), "status: #{result.status}, body: #{result.body}"
143
- end
144
-
145
- resources = JSON.parse(result.body)
146
- resources.map{ |attributes| factory.build(context, attributes) }
128
+ # Resource.where("{property: 'value'}").from(context).all
129
+ # Resource.where("{conditions}").and("{more conditions}").limit(10).from(context).all
130
+ def where(query)
131
+ query_obj = Bodhi::Query.new(name)
132
+ query_obj.where(query)
133
+ query_obj
147
134
  end
148
135
 
149
136
  # Deletes all records from a resource in the given +context+
@@ -167,6 +154,9 @@ module Bodhi
167
154
  end
168
155
 
169
156
  module InstanceMethods
157
+ def id; @sys_id; end
158
+ def persisted?; !@sys_id.nil?; end
159
+
170
160
  # Returns a Hash of the Objects form attributes
171
161
  #
172
162
  # s = SomeResource.build({foo:"test", bar:12345})
@@ -181,7 +171,22 @@ module Bodhi
181
171
  end
182
172
  attributes
183
173
  end
184
-
174
+
175
+ # Updates the resource with the given attributes Hash
176
+ #
177
+ # s = SomeResource.factory.build(foo:"test", bar:12345)
178
+ # s.attributes # => { foo: "test", bar: 12345 }
179
+ # s.update_attributes(foo:"12345", bar:10)
180
+ # s.attributes # => { foo: "12345", bar: 10 }
181
+ def update_attributes(params)
182
+ self.instance_variables.each do |variable|
183
+ attribute_name = variable.to_s.delete('@').to_sym
184
+ unless SYSTEM_ATTRIBUTES.include?(attribute_name) || SUPPORT_ATTRIBUTES.include?(attribute_name)
185
+ send("#{attribute_name}=", params[attribute_name])
186
+ end
187
+ end
188
+ end
189
+
185
190
  # Returns all the Objects attributes as JSON.
186
191
  # It converts any nested Objects to JSON if they respond to +to_json+
187
192
  #
@@ -192,6 +197,34 @@ module Bodhi
192
197
  attributes.to_json
193
198
  end
194
199
 
200
+ # Saves the resource to the Bodhi Cloud. Returns true if record was saved
201
+ #
202
+ # obj = Resource.new
203
+ # obj.save # => true
204
+ # obj.persisted? # => true
205
+ def save
206
+ if self.invalid?
207
+ return false
208
+ end
209
+
210
+ result = bodhi_context.connection.post do |request|
211
+ request.url "/#{bodhi_context.namespace}/resources/#{self.class}"
212
+ request.headers['Content-Type'] = 'application/json'
213
+ request.headers[bodhi_context.credentials_header] = bodhi_context.credentials
214
+ request.body = attributes.to_json
215
+ end
216
+
217
+ if result.status != 201
218
+ raise Bodhi::ApiErrors.new(body: result.body, status: result.status), "status: #{result.status}, body: #{result.body}"
219
+ end
220
+
221
+ if result.headers['location']
222
+ @sys_id = result.headers['location'].match(/(?<id>[a-zA-Z0-9]{24})/)[:id]
223
+ end
224
+
225
+ true
226
+ end
227
+
195
228
  # Saves the resource to the Bodhi Cloud. Raises ArgumentError if record could not be saved.
196
229
  #
197
230
  # obj = Resouce.new
@@ -224,6 +257,29 @@ module Bodhi
224
257
  raise Bodhi::ApiErrors.new(body: result.body, status: result.status), "status: #{result.status}, body: #{result.body}"
225
258
  end
226
259
  end
260
+ alias :destroy :delete!
261
+
262
+ def update!(params)
263
+ update_attributes(params)
264
+
265
+ if invalid?
266
+ return false
267
+ end
268
+
269
+ result = bodhi_context.connection.put do |request|
270
+ request.url "/#{bodhi_context.namespace}/resources/#{self.class}/#{sys_id}"
271
+ request.headers['Content-Type'] = 'application/json'
272
+ request.headers[bodhi_context.credentials_header] = bodhi_context.credentials
273
+ request.body = params.to_json
274
+ end
275
+
276
+ if result.status != 204
277
+ raise Bodhi::ApiErrors.new(body: result.body, status: result.status), "status: #{result.status}, body: #{result.body}"
278
+ end
279
+
280
+ true
281
+ end
282
+ alias :update :update!
227
283
 
228
284
  def patch!(params)
229
285
  result = bodhi_context.connection.patch do |request|
@@ -241,7 +297,7 @@ module Bodhi
241
297
 
242
298
  def self.included(base)
243
299
  base.extend(ClassMethods)
244
- base.include(InstanceMethods, Bodhi::Validations)
300
+ base.include(InstanceMethods, Bodhi::Validations, ActiveModel::Model)
245
301
  base.instance_variable_set(:@factory, Bodhi::Factory.new(base))
246
302
  end
247
303
  end
@@ -176,7 +176,7 @@ module Bodhi
176
176
  })
177
177
 
178
178
  type.properties.each_pair do |attr_name, attr_properties|
179
- attr_properties.delete_if{ |key, value| ["system", "trim", "unique", "default", "isCurrentUser"].include?(key) }
179
+ attr_properties.delete_if{ |key, value| ["system", "trim", "unique", "default", "isCurrentUser", "toLower"].include?(key) }
180
180
  klass.validates(attr_name.to_sym, attr_properties)
181
181
  klass.factory.add_generator(attr_name.to_sym, attr_properties)
182
182
  end
@@ -4,7 +4,11 @@ module Bodhi
4
4
  def initialize(value); end
5
5
 
6
6
  def validate(record, attribute, value)
7
- record.errors.add(attribute, "must be an array") unless value.is_a? Array
7
+ if value.nil?
8
+ #do nothing
9
+ else
10
+ record.errors.add(attribute, "must be an array") unless value.is_a? Array
11
+ end
8
12
  end
9
13
 
10
14
  def to_options
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bodhi-slam
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.4
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - willdavis
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-09-23 00:00:00.000000000 Z
11
+ date: 2015-10-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: net-http-persistent
@@ -66,6 +66,20 @@ dependencies:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: '1.1'
69
+ - !ruby/object:Gem::Dependency
70
+ name: activemodel
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '4.2'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '4.2'
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: rspec
71
85
  requirement: !ruby/object:Gem::Requirement
@@ -138,6 +152,7 @@ files:
138
152
  - lib/bodhi-slam/errors/context.rb
139
153
  - lib/bodhi-slam/factory.rb
140
154
  - lib/bodhi-slam/profiles.rb
155
+ - lib/bodhi-slam/queries.rb
141
156
  - lib/bodhi-slam/resource.rb
142
157
  - lib/bodhi-slam/types.rb
143
158
  - lib/bodhi-slam/users.rb