StarRezApi 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 [name of plugin creator]
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,23 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+
5
+ desc 'Default: run unit tests.'
6
+ task :default => :test
7
+
8
+ desc 'Test the starrez_api plugin.'
9
+ Rake::TestTask.new(:test) do |t|
10
+ t.libs << 'lib'
11
+ t.libs << 'test'
12
+ t.pattern = 'test/**/*_test.rb'
13
+ t.verbose = true
14
+ end
15
+
16
+ desc 'Generate documentation for the starrez_api plugin.'
17
+ Rake::RDocTask.new(:rdoc) do |rdoc|
18
+ rdoc.rdoc_dir = 'rdoc'
19
+ rdoc.title = 'StarrezApi'
20
+ rdoc.options << '--line-numbers' << '--inline-source'
21
+ rdoc.rdoc_files.include('README')
22
+ rdoc.rdoc_files.include('lib/**/*.rb')
23
+ end
@@ -0,0 +1,8 @@
1
+ development:
2
+ base_uri: https://localhost/StarRezPortalREST/services/
3
+
4
+ production:
5
+ base_uri: https://localhost/StarRezPortalREST/services/
6
+
7
+ test:
8
+ base_uri: https://localhost/StarRezPortalREST/services/
@@ -0,0 +1,22 @@
1
+ # StarRez REST Web Services and Report API
2
+
3
+ # Load our configuration
4
+ if File.exists?(File.join(Rails.root,"config","starrez.yml"))
5
+ config_path = File.join(Rails.root,"config","starrez.yml")
6
+ else
7
+ config_path = File.dirname(__FILE__) + "/starrez.yml"
8
+ end
9
+ STARREZ_CONFIG = YAML.load_file(config_path)[Rails.env]
10
+
11
+ # Check for required gems
12
+ begin
13
+ require 'httparty'
14
+ HTTParty
15
+ rescue Exception => e
16
+ puts "HTTParty is a required GEM for this plugin. Solve by typing: gem install httparty"
17
+ end
18
+
19
+ # Load our Plugin files
20
+ require 'starrez_api/object'
21
+ require 'starrez_api/star_rez_api'
22
+ require 'starrez_api/star_rez_report'
@@ -0,0 +1,19 @@
1
+ # Much props to _why and his (poignant) guide
2
+ # This code can originally be found at
3
+ # http://viewsourcecode.org/why/hacking/seeingMetaclassesClearly.html
4
+
5
+ class Object
6
+ # The hidden singleton lurks behind everyone
7
+ def metaclass; class << self; self; end; end
8
+ def meta_eval &blk; metaclass.instance_eval &blk; end
9
+
10
+ # Adds methods to a metaclass
11
+ def meta_def name, &blk
12
+ meta_eval { define_method name, &blk }
13
+ end
14
+
15
+ # Defines an instance method within a class
16
+ def class_def name, &blk
17
+ class_eval { define_method name, &blk }
18
+ end
19
+ end
@@ -0,0 +1,312 @@
1
+ require 'httparty'
2
+ require 'xmlsimple'
3
+
4
+ module StarRezApi
5
+ include HTTParty
6
+ base_uri STARREZ_CONFIG['base_uri']
7
+
8
+ def self.included receiver
9
+ receiver.extend ClassMethods
10
+ end
11
+
12
+ module ClassMethods
13
+ def first
14
+ results = find(:first)
15
+ end
16
+
17
+ def url
18
+ "#{StarRezApi::base_uri}/select/#{self.name.gsub(/.*\:\:/,'').downcase}.xml/?_top=1"
19
+ end
20
+
21
+ def what_am_i?
22
+ "#{self.class.name}"
23
+ end
24
+
25
+ def class_name
26
+ self.name.gsub(/.*\:\:/,'')
27
+ end
28
+
29
+ def populate_variables(hash, related_tables=[])
30
+ self.instance_variable_set("@original_hash",hash)
31
+ hash.each do |k,v|
32
+ if related_tables.include? k.to_s
33
+ children = Array.new
34
+ v = [v] unless v.is_a? Array # This handles the instance of a single-child
35
+ v.each do |child|
36
+ new_child = self.clone
37
+ new_child.populate_variables(child)
38
+ children << new_child
39
+ end
40
+ new_k = k.to_s.underscore.pluralize
41
+ self.instance_variable_set("@#{new_k}", children)
42
+ meta_def new_k do
43
+ self.instance_variable_get("@#{new_k}")
44
+ end
45
+ meta_def "#{new_k}=" do |v|
46
+ self.instance_variable_set("@#{new_k}",v)
47
+ end
48
+ elsif k.is_a?(Hash)
49
+ # Ignore sub-objects
50
+ else
51
+ unless k.blank?
52
+ k = k.to_s.underscore
53
+ self.instance_variable_set("@#{k}",v)
54
+ meta_def k do
55
+ self.instance_variable_get("@#{k}")
56
+ end
57
+ meta_def "#{k}=" do |v|
58
+ self.instance_variable_set("@#{k}",v)
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+
65
+ def id
66
+ self.send "#{self.class_name.underscore}_id"
67
+ end
68
+
69
+ def changed
70
+ changed_attributes = Hash.new
71
+ self.instance_variable_get("@original_hash").each do |k,v|
72
+ k = k.to_s.underscore
73
+ current_value = self.send(k.to_sym)
74
+ unless current_value.eql? v
75
+ changed_attributes[k.to_sym] = [v, current_value]
76
+ end
77
+ end
78
+ return changed_attributes
79
+ end
80
+
81
+ def changed?
82
+ self.changed.size > 0
83
+ end
84
+
85
+ def save
86
+ if self.changed?
87
+ response = StarRezApi::post("#{StarRezApi::base_uri}/update/#{self.class_name}/#{self.id}", :body => self.build_query(self.changed))
88
+ if response.code.eql? 200
89
+ original_hash = self.instance_variable_get("@original_hash")
90
+ self.build_query(self.changed).keys.each do |attribute|
91
+ original_hash[attribute.to_s] = self.send(attribute.to_s.underscore.to_sym)
92
+ end
93
+ self.instance_variable_set("@original_hash",original_hash)
94
+ return true
95
+ else
96
+ return response
97
+ end
98
+ else
99
+ warn "[WARN] Nothing to save"
100
+ return false
101
+ end
102
+ end
103
+
104
+ def create(attribute_hash = {}, options ={})
105
+ formatted_hash = Hash.new
106
+ attribute_hash.each_pair { |column, value| formatted_hash[column.to_s.camelize.to_sym] = value }
107
+ results = StarRezApi::post("#{StarRezApi::base_uri}/create/#{self.class_name}", :body => formatted_hash)
108
+ if results.code.eql? 200
109
+ if options[:return].eql? :boolean
110
+ return true
111
+ else
112
+ response = XmlSimple.xml_in(results.body)
113
+ new_id = response["entry"][0]["content"]
114
+ return self.find new_id
115
+ end
116
+ elsif results.code.eql? 400
117
+ return false
118
+ else
119
+ return false
120
+ end
121
+ end
122
+
123
+
124
+ def all(page_index = 1, page_size = 50)
125
+ return find(:all, {:size => page_size, :page => page_index})
126
+ end
127
+
128
+
129
+
130
+ # Find objects via the API interface
131
+ # Returns an object defined on demand by the API result
132
+ #
133
+ # An Object ID or one of the following symbols (:all, :first) must be the first argument
134
+ # +id+:: The ID of the Object your are searching
135
+ # +:all+:: Returns an array of objects
136
+ # +:first+:: Returns the first matched object
137
+ #
138
+ #
139
+ # The following options are not required but can be used to refine the search
140
+ # +:conditions+:: Field-Value Pairs
141
+ # +:page+:: The index page for pagination
142
+ # +:size+:: The number of results returned per page
143
+ # +:fields+:: An array of field names to return
144
+ # +:include+:: Include related tables (this only works if you go from parent->child)
145
+ # +:order+:: An array of field names to order the response by.
146
+ # +:limit+:: The number of results to return
147
+ #
148
+ # The +conditions+ option should be a hash with key-value pairs related to the search
149
+ # requirements. Due to the nature of the StarRez API, there is an additional complexity
150
+ # of seach operands. For this reason the conditions can be in either of the following
151
+ # two formats:
152
+ #
153
+ # { :column_name => value }
154
+ #
155
+ # { :column_name => { :operand => value } }
156
+ #
157
+ # Available Operands are:
158
+ #
159
+ # +ne+:: Not Equals
160
+ # +gt+:: Greater Than
161
+ # +lt+:: Less Than
162
+ # +gte+:: Greater Than or Equal To
163
+ # +lte+:: Less Than or Equal To
164
+ # +c+:: Contains
165
+ # +nc+:: Not Contains
166
+ # +sw+:: Starts With
167
+ # +nsw+:: Not Starts With
168
+ # +ew+:: Ends With
169
+ # +new+:: Not Ends With
170
+ # +in+:: Value is in (comma separated integer list)
171
+ # +notin+:: Value is not in (comma separated integer list)
172
+ #
173
+ # The +fields+ option should be an Array of either symbols or strings which refer to a
174
+ # column in the field. There is no error checking prior to the submission, so an invalid
175
+ # field name will result in an error.
176
+ #
177
+ # The +include+ option should be an Array of either symbols or strings which refer to a
178
+ # related tables in the database. There is no error checking and an invalid relationship
179
+ # will result in a failure. This will create an instance variable of an Array of the
180
+ # returned objects which will be accessible by a method with the table name pluralized.
181
+ #
182
+ # The +order+ option should be an array of fields which the response is ordered by. If
183
+ # You wish to search by descending order, use a key value of :desc. For example:
184
+ # :order => [:name_last, :name_first => :desc]
185
+ #
186
+ #
187
+ # Usage:
188
+ #
189
+ # # Search for a single Entry with the ID of 1234
190
+ # Entry.find(1234)
191
+ #
192
+ # # Search for all entries with the last name 'Smith'
193
+ # Entry.find(:all, :conditions => { :name_last => "Smith"})
194
+ #
195
+ # # Search for all entries with the last name that starts with 'Sm' and only return first and last names
196
+ # Entry.find(:all, :conditions => { :name_last => { :sw => "Sm" } }, :fields => [:name_last, :name_first])
197
+ #
198
+ # # Search for all rooms on a specific floor ('654')
199
+ # RoomLocationFloorSuite(654, :include => [:room])
200
+
201
+
202
+ def find(entry, options = {})
203
+ options[:size] ||= 50
204
+ options[:page] = options[:page].blank? || options[:page] == 1 ? 0 : options[:page] * options[:size]
205
+ query_array = Array.new
206
+ unless options[:conditions].blank?
207
+ query_array << get_condition_string(options[:conditions])
208
+ end
209
+ if entry.is_a?(Symbol)
210
+ get_url = "#{StarRezApi::base_uri}/select/#{self.class_name}.xml/"
211
+ else
212
+ get_url = "#{StarRezApi::base_uri}/select/#{self.class_name}.xml/#{entry}"
213
+ end
214
+ if entry.eql? :first
215
+ query_array << "_top=1"
216
+ elsif entry.eql? :all
217
+ query_array << "_pageIndex=#{options[:page]}"
218
+ query_array << "_pageSize=#{options[:size]}"
219
+ end
220
+ unless options[:fields].blank?
221
+ fields = Array.new
222
+ options[:fields].each { |f| fields << f.to_s.camelize }
223
+ query_array << "_fields=#{fields.join(',')}"
224
+ end
225
+ tables = Array.new
226
+ unless options[:include].blank?
227
+ options[:include].each { |t| tables << t.to_s.camelize }
228
+ query_array << "_relatedtables=#{tables.join(',')}"
229
+ end
230
+ unless options[:order].blank?
231
+ order = Array.new
232
+ options[:order].each { |o| order << (options[:order][o].eql? :desc) ? "#{o.to_s.camelize}.desc" : "#{o.to_s.camelize}"}
233
+ query_array << "_orderby=#{order.join(',')}"
234
+ end
235
+ unless options[:limit].blank?
236
+ query_array << "_top=#{options[:limit]}"
237
+ end
238
+ get_url += "?#{query_array.join('&')}"
239
+ results = StarRezApi::get(get_url)
240
+ if results.response.is_a?(Net::HTTPNotFound)
241
+ return nil
242
+ elsif results.code.eql? 403
243
+ raise SecurityError, "Access Denied to API"
244
+ elsif options[:return].eql? :response
245
+ return results
246
+ elsif results.code.eql? 200
247
+ ret = results["Results"][self.class_name]
248
+ else
249
+ return results
250
+ end
251
+ if ret.is_a?(Hash)
252
+ self.populate_variables(ret,tables)
253
+ if entry.eql? :all
254
+ return [self]
255
+ else
256
+ return self
257
+ end
258
+ else
259
+ results = Array.new
260
+ ret.each do |entry_hash|
261
+ new_entry = self.clone
262
+ new_entry.populate_variables(entry_hash,tables)
263
+ results << new_entry
264
+ end
265
+ return results
266
+ end
267
+ end
268
+
269
+
270
+ def build_query(hash)
271
+ query = Hash.new
272
+ hash.keys.each do |attribute|
273
+ query[attribute.to_s.camelize.to_sym] = self.send(attribute)
274
+ end
275
+ return query
276
+ end
277
+
278
+ private
279
+
280
+ #Just a quick method used in get_condition_string that would have been repeated
281
+ #Just takes the array and converts it into a formatted string for StarRezAPI
282
+ def parse_value(values)
283
+ if values.is_a?(Array)
284
+ return URI::encode(values.join(','))
285
+ else
286
+ return URI::encode(values.to_s)
287
+ end
288
+ end
289
+
290
+ # Coditions Clean-up by Dan
291
+ # Example:
292
+ # find(:all, :conditions => { :column_name => value, :column_name => { :operator => value } })
293
+
294
+ def get_condition_string(conditions)
295
+ queries = Array.new
296
+ if conditions.is_a?(Hash)
297
+ conditions.each_pair do |column, value|
298
+ query = column.to_s.camelize
299
+ if value.is_a?(Hash)
300
+ query += "[_operator%3D#{value.keys.first.to_s}]=#{parse_value(value[value.keys.first])}"
301
+ else
302
+ query += "=#{parse_value(value)}"
303
+ end
304
+ queries << query
305
+ end
306
+ return queries.join('&')
307
+ else
308
+ raise ArgumentError, "Condition needs to be a hash of values, Please review the source code"
309
+ end
310
+ end
311
+ end
312
+ end
@@ -0,0 +1,88 @@
1
+ require 'httparty'
2
+ class StarRezReport
3
+ include HTTParty
4
+ base_uri STARREZ_CONFIG['base_uri']
5
+ attr_accessor :name, :results
6
+
7
+ def self.find_by_id(entry, options = {})
8
+ if entry.blank?
9
+ raise IOError, "Must include a report ID to search"
10
+ end
11
+ conditions = options[:conditions].blank? ? '' : "?#{self.get_condition_string(options[:conditions])}"
12
+ url = "#{base_uri}/getreportbyid/#{entry}.xml/#{conditions}"
13
+ response = get(url)
14
+ if options[:return].eql? :response
15
+ return response
16
+ else
17
+ return self.parse_response(response)
18
+ end
19
+ end
20
+
21
+ def self.find_by_name(name, options = {})
22
+ if name.blank?
23
+ raise IOError, "Must include a report name"
24
+ end
25
+ conditions = options[:conditions].blank? ? '' : "?#{self.get_condition_string(options[:conditions])}"
26
+ url = "#{base_uri}/getreportbyname/#{URI::escape(name.to_s)}.xml/#{conditions}"
27
+ response = get(url)
28
+ if options[:return].eql? :response
29
+ return response
30
+ else
31
+ return self.parse_response(response)
32
+ end
33
+ end
34
+
35
+ private
36
+ # Parse the response from the API
37
+ def self.parse_response(response)
38
+ if response.code.eql? 200
39
+ report = StarRezReport.new
40
+ report.name = response.keys.first
41
+ if response[report.name].blank?
42
+ report.results = []
43
+ else
44
+ if response[report.name]["Record"].is_a? Hash
45
+ report.results = [response[report.name]["Record"]]
46
+ else
47
+ report.results = response[report.name]["Record"]
48
+ end
49
+ end
50
+ return report
51
+ elsif response.code.eql? 403
52
+ raise SecurityError, "Access Denied to API"
53
+ else
54
+ return false
55
+ end
56
+ end
57
+
58
+ # Coditions Clean-up by Dan
59
+ # Example:
60
+ # find(:all, :conditions => { :column_name => value, :column_name => { :operator => value } })
61
+ def self.get_condition_string(conditions)
62
+ queries = Array.new
63
+ if conditions.is_a?(Hash)
64
+ conditions.each_pair do |column, value|
65
+ query = column.to_s.camelize
66
+ if value.is_a?(Hash)
67
+ query += "[_operator%3D#{value.keys.first.to_s}]=#{self.parse_value(value[value.keys.first])}"
68
+ else
69
+ query += "=#{self.parse_value(value)}"
70
+ end
71
+ queries << query
72
+ end
73
+ return queries.join('&')
74
+ else
75
+ raise ArgumentError, "Condition needs to be a hash of values, Please review the source code"
76
+ end
77
+ end
78
+
79
+ #Just a quick method used in get_condition_string that would have been repeated
80
+ #Just takes the array and converts it into a formatted string for StarRezAPI
81
+ def self.parse_value(values)
82
+ if values.is_a?(Array)
83
+ return URI::encode(values.join(','))
84
+ else
85
+ return URI::encode(values.to_s)
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :starrez_api do
3
+ # # Task goes here
4
+ # end
@@ -0,0 +1,2 @@
1
+ # Include hook code here
2
+ require 'starrez_api'
metadata ADDED
@@ -0,0 +1,84 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: StarRezApi
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.2.3
6
+ platform: ruby
7
+ authors:
8
+ - Daniel Reedy
9
+ - Steven Stephen
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+
14
+ date: 2011-06-15 00:00:00 Z
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: httparty
18
+ prerelease: false
19
+ requirement: &id001 !ruby/object:Gem::Requirement
20
+ none: false
21
+ requirements:
22
+ - - ">="
23
+ - !ruby/object:Gem::Version
24
+ version: 0.7.4
25
+ type: :runtime
26
+ version_requirements: *id001
27
+ - !ruby/object:Gem::Dependency
28
+ name: xml-simple
29
+ prerelease: false
30
+ requirement: &id002 !ruby/object:Gem::Requirement
31
+ none: false
32
+ requirements:
33
+ - - ">="
34
+ - !ruby/object:Gem::Version
35
+ version: 1.0.12
36
+ type: :runtime
37
+ version_requirements: *id002
38
+ description: This gem that allows the user access to the StarRez REST Web Services and Reporting API
39
+ email: dreedy@housing.siu.edu
40
+ executables: []
41
+
42
+ extensions: []
43
+
44
+ extra_rdoc_files: []
45
+
46
+ files:
47
+ - lib/starrez.yml
48
+ - lib/starrez_api/object.rb
49
+ - lib/starrez_api/star_rez_api.rb
50
+ - lib/starrez_api/star_rez_report.rb
51
+ - lib/starrez_api.rb
52
+ - lib/tasks/starrez_api.rake
53
+ - rails/init.rb
54
+ - MIT-LICENSE
55
+ - Rakefile
56
+ homepage: http://www.housing.siu.edu
57
+ licenses: []
58
+
59
+ post_install_message:
60
+ rdoc_options: []
61
+
62
+ require_paths:
63
+ - lib
64
+ required_ruby_version: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: "0"
70
+ required_rubygems_version: !ruby/object:Gem::Requirement
71
+ none: false
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: "0"
76
+ requirements: []
77
+
78
+ rubyforge_project:
79
+ rubygems_version: 1.8.5
80
+ signing_key:
81
+ specification_version: 3
82
+ summary: A module mixin that allows a class to access StarRez
83
+ test_files: []
84
+