sds-rest 0.1.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.
data/History.txt ADDED
@@ -0,0 +1,4 @@
1
+ == 0.0.1 2008-10-23
2
+
3
+ * 1 major enhancement:
4
+ * Initial release
data/Manifest.txt ADDED
@@ -0,0 +1,30 @@
1
+ History.txt
2
+ Manifest.txt
3
+ PostInstall.txt
4
+ README.rdoc
5
+ Rakefile
6
+ config/hoe.rb
7
+ config/requirements.rb
8
+ lib/sds-activeresource.rb
9
+ lib/sds-rest.rb
10
+ lib/sds-rest/version.rb
11
+ script/console
12
+ script/destroy
13
+ script/generate
14
+ script/txt2html
15
+ setup.rb
16
+ spec/authority_spec.rb
17
+ spec/blob_spec.rb
18
+ spec/container_spec.rb
19
+ spec/entity_spec.rb
20
+ spec/sdsactiveresource_spec.rb
21
+ spec/sdsconnection_spec.rb
22
+ spec/spec_helper.rb
23
+ tasks/deployment.rake
24
+ tasks/environment.rake
25
+ tasks/website.rake
26
+ website/index.html
27
+ website/index.txt
28
+ website/javascripts/rounded_corners_lite.inc.js
29
+ website/stylesheets/screen.css
30
+ website/template.html.erb
data/PostInstall.txt ADDED
@@ -0,0 +1,7 @@
1
+
2
+ For more information on sds-rest, see http://sdsrest.rubyforge.org
3
+
4
+ NOTE: Change this information in PostInstall.txt
5
+ You can also delete it if you don't want it.
6
+
7
+
data/README.rdoc ADDED
@@ -0,0 +1,62 @@
1
+ = sds-rest
2
+
3
+ * FIX (url)
4
+
5
+ == DESCRIPTION:
6
+
7
+ A ruby library for accessing the SDS REST interface.
8
+
9
+ == FEATURES/PROBLEMS:
10
+
11
+ SDSRest Library
12
+ Active Resource Adapter
13
+
14
+ == SYNOPSIS:
15
+
16
+
17
+
18
+ == REQUIREMENTS:
19
+
20
+ gem install uuidtools
21
+
22
+ == INSTALL:
23
+
24
+ If you are new to ruby follow these instructions:
25
+
26
+ 1) Install Ruby with the One-Click Installer - http://rubyforge.org/frs/download.php/29263/ruby186-26.exe
27
+ 2) Install Ruby Gems - http://rubyforge.org/frs/download.php/38647/rubygems-1.2.0.zip
28
+ 3) Install Rails - cmd line => gem install rails --include-dependencies
29
+ 4) Install UUIDTools - cmd line => gem install uuidtools
30
+
31
+ == LICENSE:
32
+
33
+ Copyright (c) 2008 Microsoft
34
+
35
+ This license governs use of the accompanying software. If you use the software, you accept this license. If you do not accept the license, do not use the software.
36
+ 1. Definitions
37
+
38
+ The terms “reproduce,” “reproduction,” “derivative works,” and “distribution” have the same meaning here as under U.S. copyright law.
39
+
40
+ A “contribution” is the original software, or any additions or changes to the software.
41
+
42
+ A “contributor” is any person that distributes its contribution under this license.
43
+
44
+ “Licensed patents” are a contributor’s patent claims that read directly on its contribution.
45
+ 2. Grant of Rights
46
+
47
+ (A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create.
48
+
49
+ (B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software.
50
+ 3. Conditions and Limitations
51
+
52
+ (A) No Trademark License- This license does not grant you rights to use any contributors’ name, logo, or trademarks.
53
+
54
+ (B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically.
55
+
56
+ (C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software.
57
+
58
+ (D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license.
59
+
60
+ (E) The software is licensed “as-is.” You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement.
61
+
62
+ See FAQ.html for answers to frequently asked questions about this license.
data/Rakefile ADDED
@@ -0,0 +1,4 @@
1
+ require 'config/requirements'
2
+ require 'config/hoe' # setup Hoe + all gem configuration
3
+
4
+ Dir['tasks/**/*.rake'].each { |rake| load rake }
data/config/hoe.rb ADDED
@@ -0,0 +1,77 @@
1
+ require 'sds-rest/version'
2
+
3
+ AUTHOR = 'James Avery' # can also be an array of Authors
4
+ EMAIL = "javery@infozerk.com"
5
+ DESCRIPTION = "description of gem"
6
+ GEM_NAME = 'sds-rest' # what ppl will type to install your gem
7
+ RUBYFORGE_PROJECT = 'sdsrest' # The unix name for your project
8
+ HOMEPATH = "http://#{RUBYFORGE_PROJECT}.rubyforge.org"
9
+ DOWNLOAD_PATH = "http://rubyforge.org/projects/#{RUBYFORGE_PROJECT}"
10
+ EXTRA_DEPENDENCIES = [
11
+ # ['activesupport', '>= 1.3.1']
12
+ ] # An array of rubygem dependencies [name, version]
13
+ EXTRA_DEV_DEPENDENCIES = [
14
+ # ['rspec', '>= 1.1.5']
15
+ ] # An array of rubygem dependencies [name, version]
16
+
17
+ @config_file = "~/.rubyforge/user-config.yml"
18
+ @config = nil
19
+ RUBYFORGE_USERNAME = "unknown"
20
+ def rubyforge_username
21
+ unless @config
22
+ begin
23
+ @config = YAML.load(File.read(File.expand_path(@config_file)))
24
+ rescue
25
+ puts <<-EOS
26
+ ERROR: No rubyforge config file found: #{@config_file}
27
+ Run 'rubyforge setup' to prepare your env for access to Rubyforge
28
+ - See http://newgem.rubyforge.org/rubyforge.html for more details
29
+ EOS
30
+ exit
31
+ end
32
+ end
33
+ RUBYFORGE_USERNAME.replace @config["username"]
34
+ end
35
+
36
+
37
+ REV = nil
38
+ # UNCOMMENT IF REQUIRED:
39
+ # REV = YAML.load(`svn info`)['Revision']
40
+ VERS = SDSRest::VERSION::STRING + (REV ? ".#{REV}" : "")
41
+ RDOC_OPTS = ['--quiet', '--title', 'sds-rest documentation',
42
+ "--opname", "index.html",
43
+ "--line-numbers",
44
+ "--main", "README",
45
+ "--inline-source"]
46
+
47
+ class Hoe
48
+ def extra_deps
49
+ @extra_deps.reject! { |x| Array(x).first == 'hoe' }
50
+ @extra_deps
51
+ end
52
+ end
53
+
54
+ # Generate all the Rake tasks
55
+ # Run 'rake -T' to see list of generated tasks (from gem root directory)
56
+ $hoe = Hoe.new(GEM_NAME, VERS) do |p|
57
+ p.developer(AUTHOR, EMAIL)
58
+ p.description = DESCRIPTION
59
+ p.summary = DESCRIPTION
60
+ p.url = HOMEPATH
61
+ p.rubyforge_name = RUBYFORGE_PROJECT if RUBYFORGE_PROJECT
62
+ p.test_globs = ["spec/**/*_spec.rb"]
63
+ p.clean_globs |= ['**/.*.sw?', '*.gem', '.config', '**/.DS_Store'] #An array of file patterns to delete on clean.
64
+
65
+ # == Optional
66
+ p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
67
+ p.extra_deps = EXTRA_DEPENDENCIES
68
+ p.extra_dev_deps = EXTRA_DEV_DEPENDENCIES
69
+
70
+ p.spec_extras = {} # A hash of extra values to set in the gemspec.
71
+ end
72
+
73
+ CHANGES = $hoe.paragraphs_of('History.txt', 0..1).join("\\n\\n")
74
+ PATH = (RUBYFORGE_PROJECT == GEM_NAME) ? RUBYFORGE_PROJECT : "#{RUBYFORGE_PROJECT}/#{GEM_NAME}"
75
+ $hoe.remote_rdoc_dir = File.join(PATH.gsub(/^#{RUBYFORGE_PROJECT}\/?/,''), 'rdoc')
76
+ $hoe.rsync_args = '-av --delete --ignore-errors'
77
+ $hoe.spec.post_install_message = File.open(File.dirname(__FILE__) + "/../PostInstall.txt").read rescue ""
@@ -0,0 +1,15 @@
1
+ require 'fileutils'
2
+ include FileUtils
3
+
4
+ require 'rubygems'
5
+ %w[rake hoe newgem rubigen].each do |req_gem|
6
+ begin
7
+ require req_gem
8
+ rescue LoadError
9
+ puts "This Rakefile requires the '#{req_gem}' RubyGem."
10
+ puts "Installation: gem install #{req_gem} -y"
11
+ exit
12
+ end
13
+ end
14
+
15
+ $:.unshift(File.join(File.dirname(__FILE__), %w[.. lib]))
@@ -0,0 +1,183 @@
1
+ require 'rubygems'
2
+ require 'activesupport'
3
+ require 'activeresource'
4
+ require 'rexml/document'
5
+ require 'uri'
6
+ require 'sds-rest'
7
+
8
+ module SDSActiveResource
9
+
10
+ class Base < ActiveResource::Base
11
+
12
+ def self.connection(refresh = false)
13
+ @connection = SDSConnection.new(super.site, super.format) if refresh || @connection.nil?
14
+ @connection.user = user
15
+ @connection.password = password
16
+ @connection
17
+ end
18
+
19
+ #we don't want to create XML, we want to pass the attributes directly to the SDS service where the XML can be created
20
+ def to_xml(options={})
21
+ newattributes = {}
22
+ newattributes.merge! attributes
23
+ newattributes["entityname"] = self.class.element_name
24
+ newattributes
25
+ end
26
+
27
+ def self.query(query)
28
+ instantiate_collection(@connection.query(collection_path(), query))
29
+ end
30
+ end
31
+
32
+ class SDSConnection < ActiveResource::Connection
33
+
34
+ def service
35
+ @service = SDSRest::Service.new :username => user, :password => password, :authority => get_authority(site), :url => get_url(site) if @service.nil?
36
+ @service
37
+ end
38
+
39
+ def query(path, query)
40
+ container = get_container(path)
41
+ query.gsub!(/AND/i, "%26%26 ")
42
+ response = service.query(container, query)
43
+ entity = REXML::Document.new(response.body)
44
+ entities = []
45
+
46
+ entity.root().elements.each { |element|
47
+ options = {}
48
+ element.elements.each { |element2|
49
+ options[element2.name] = parse_value(element.attributes["xsi:type"], element2.text)
50
+ }
51
+ entities.push(options)
52
+ }
53
+
54
+ entities
55
+ end
56
+
57
+ def post(path, body = '', headers = {})
58
+ container = get_container(path)
59
+ response = service.create_entity(container, body["entityname"], body["Id"], body)
60
+ response['location'] = "test/" + body["Id"].to_s
61
+ response
62
+ end
63
+
64
+ def delete(path, body = '', headers = {})
65
+ container = get_container(path)
66
+ id = get_id(path)
67
+ service.delete_entity(container, id)
68
+ end
69
+
70
+ def put(path, body = '', headers = {})
71
+ container = get_container(path)
72
+ response = service.update_entity(container, body["name"], body["Id"], nil, body)
73
+ response['location'] = "test/" + body["Id"].to_s
74
+ response
75
+ end
76
+
77
+ def get(path, headers = {})
78
+ container = get_container(path)
79
+ entityname = get_entity(path)
80
+ id = get_id(path)
81
+ if(id.nil?)
82
+ query = 'from e in entities where e.Kind == "' + entityname.singularize + '" select e'
83
+ response = service.query(container, query)
84
+ entity = REXML::Document.new(response.body)
85
+ entities = []
86
+
87
+ entity.root().elements.each { |element|
88
+ options = {}
89
+ element.elements.each { |element2|
90
+ options[element2.name] = parse_value(element2.attributes["xsi:type"], element2.text)
91
+ }
92
+ entities.push(options)
93
+ }
94
+
95
+ entities
96
+ else
97
+
98
+ response = service.get_entity(container, id)
99
+
100
+ entity = REXML::Document.new(response.body)
101
+
102
+ options = {}
103
+ entity.root().elements.each { |element|
104
+ options[element.name] = parse_value(element.attributes["xsi:type"], element.text)
105
+ }
106
+ options['id'] = id
107
+ options
108
+
109
+ end
110
+ end
111
+
112
+ def get_authority(path)
113
+ splitpath = URI::split(path.to_s)
114
+ splitpath[2].split('.')[0]
115
+ end
116
+
117
+ def get_url(path)
118
+ splitpath = URI::split(path.to_s)
119
+ host = splitpath[2]
120
+ host[get_authority(path) + "."] = ""
121
+ host
122
+ end
123
+
124
+ def get_params(path)
125
+ puts path
126
+ if(path.include? "?")
127
+ params = path.split('?')[1]
128
+ params || ""
129
+ end
130
+ end
131
+
132
+ def get_container(path)
133
+ container = path.to_s.split('/')[1]
134
+
135
+ if(container.nil?)
136
+ raise "no container found"
137
+ end
138
+ container
139
+ end
140
+
141
+ def get_entity(path)
142
+ path.delete! '.xml'
143
+ entity = path.split('/')[2]
144
+
145
+ if(entity.include? "?")
146
+ entity = entity.split('?')[0]
147
+ end
148
+
149
+ if(entity.nil?)
150
+ raise "no entity found"
151
+ end
152
+ entity
153
+ end
154
+
155
+ def parse_value(type, value)
156
+ if(type == "x:decimal")
157
+ if(value.include?("."))
158
+ Float(value)
159
+ else
160
+ Integer(value)
161
+ end
162
+ elsif(type == "x:boolean")
163
+ Boolean(value)
164
+ elsif(type == "x:dateTime")
165
+ DateTime.parse(value)
166
+ else
167
+ value
168
+ end
169
+ end
170
+
171
+ def get_id(path)
172
+ path.delete! '.xml'
173
+ path.to_s.split('/')[3]
174
+ end
175
+
176
+ def Boolean(string)
177
+ return true if string == true || string =~ /^true$/i
178
+ return false if string == false || string.nil? || string =~ /^false$/i
179
+ raise ArgumentError.new("invalid value for Boolean: \"#{string}\"")
180
+ end
181
+
182
+ end
183
+ end
data/lib/sds-rest.rb ADDED
@@ -0,0 +1,255 @@
1
+ require 'net/http'
2
+ require 'rexml/document'
3
+ require 'yaml'
4
+
5
+ module SDSRest
6
+
7
+ ssds_config = "#{RAILS_ROOT}/config/ssds.yml"
8
+
9
+ if File.exist?(ssds_config)
10
+ SSDSCONFIG = YAML.load_file(ssds_config)[RAILS_ENV]
11
+ ENV['username'] = SSDSCONFIG['username']
12
+ ENV['password'] = SSDSCONFIG['password']
13
+ ENV['url'] = SSDSCONFIG['url']
14
+ ENV['authority'] = SSDSCONFIG['authority']
15
+ end
16
+
17
+
18
+ class Service
19
+
20
+ def initialize(options={})
21
+ @username = options[:username] || ENV['username']
22
+ @password = options[:password] || ENV['password']
23
+ @url = options[:url] || ENV['url']
24
+ @authority = options[:authority] || ENV['authority']
25
+ end
26
+
27
+ def authority=(value)
28
+ @authority = value
29
+ end
30
+
31
+ def create_authority(authority)
32
+ request_xml = REXML::Document.new()
33
+ request_xml.add_element('s:Authority')
34
+ request_xml.root().add_attribute('xmlns:s', 'http://schemas.microsoft.com/sitka/2008/03/')
35
+ request_xml.root().add_element("s:Id")
36
+ request_xml.root().elements["s:Id"].text = authority
37
+ req = create_post(request_xml)
38
+ execute_request req
39
+ end
40
+
41
+ def get_authority(authority)
42
+ @authority = authority
43
+ get(get_request_url)
44
+ end
45
+
46
+ def create_container(container)
47
+ request_xml = REXML::Document.new()
48
+ request_xml.add_element('s:Container')
49
+ request_xml.root().add_attribute('xmlns:s', 'http://schemas.microsoft.com/sitka/2008/03/')
50
+ request_xml.root().add_element("s:Id")
51
+ request_xml.root().elements["s:Id"].text = container
52
+ req = create_post(request_xml)
53
+ execute_request req
54
+ end
55
+
56
+ def delete_container(container)
57
+ delete(get_request_url(container))
58
+ end
59
+
60
+ def get_container(container)
61
+ get(get_request_url(container))
62
+ end
63
+
64
+ def create_entity(container, entity, id, options={})
65
+ request_xml = REXML::Document.new()
66
+ request_xml.add_element(entity)
67
+ request_xml.root().add_attribute('xmlns:s', 'http://schemas.microsoft.com/sitka/2008/03/')
68
+ request_xml.root().add_attribute('xmlns:x', 'http://www.w3.org/2001/XMLSchema')
69
+ request_xml.root().add_attribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance' )
70
+ request_xml.root().add_element('s:Id')
71
+ request_xml.root().elements['s:Id'].text = id
72
+
73
+ options.each { |key, value|
74
+ request_xml.root().add_element(key.to_s)
75
+ request_xml.root().elements[key.to_s].add_attribute('xsi:type', infer_type(value))
76
+ request_xml.root().elements[key.to_s].text = value
77
+ }
78
+ req = create_post(request_xml, container)
79
+ execute_request req
80
+ end
81
+
82
+ # passing in a version number means that SSDS will only delete the entity if the version
83
+ # number equals the current version number.
84
+ def delete_entity(container, id, version = nil)
85
+ url = get_request_url(container, id)
86
+ delete(url, version)
87
+ end
88
+
89
+ # passing in a version number means that SSDS will only delete the entity if the version
90
+ # number equals the current version number.
91
+ def get_entity(container, id, version = nil)
92
+ url = get_request_url(container, id)
93
+ get(url, version)
94
+ end
95
+
96
+ # passing in a version number means that SSDS will only delete the entity if the version
97
+ # number equals the current version number.
98
+ def update_entity(container, entity, id, version = nil, options={})
99
+ results = get_entity container, id
100
+ entity = REXML::Document.new(results.body)
101
+
102
+ options.each { |key, value|
103
+ if(entity.root().elements[key.to_s].nil?)
104
+ entity.root().add_element(key.to_s)
105
+ entity.root().elements[key.to_s].add_attribute('xsi:type', infer_type(value))
106
+ end
107
+
108
+ entity.root().elements[key.to_s].text = value
109
+ }
110
+ url = get_request_url(container, id)
111
+ put(url, entity, version)
112
+ end
113
+
114
+ #sends a query using the SSDS query syntax
115
+ #
116
+ # examples:
117
+ # from c in entities select c - selects all entities
118
+ # from c in entities where c.Kind = 'Car' select c - select all entities of type 'Car'
119
+ # from c in entities where c["Make"] = 'Toyota' - select all entities with property Make that equals Toyota
120
+ def query(container, query)
121
+ url = get_request_url(container) + "?q=" + URI.escape(query)
122
+ get(url)
123
+ end
124
+
125
+ def create_blob(container, blob, id)
126
+ url = get_request_url(container)
127
+ req = Net::HTTP::Post.new(url)
128
+ req.content_type = 'text'
129
+ req.content_length = blob.to_s.size.to_s
130
+ req['slug'] = id
131
+ req.basic_auth @username, @password
132
+ req.body = blob.to_s
133
+ execute_request(req)
134
+ end
135
+
136
+ def update_blob(container, blob, id)
137
+ url = get_request_url(container, id)
138
+ req = Net::HTTP::Put.new(url)
139
+ req.content_type = 'text'
140
+ req.content_length = blob.to_s.size.to_s
141
+ req['slug'] = id
142
+ req.basic_auth @username, @password
143
+ req.body = blob.to_s
144
+ execute_request(req)
145
+ end
146
+
147
+ def delete_blob(container, id)
148
+ delete(get_request_url(container, id))
149
+ end
150
+
151
+ def get_blob(container, id)
152
+ get(get_request_url(container, id))
153
+ end
154
+
155
+ def infer_type(value)
156
+ if(value.is_a?(Integer) || value.is_a?(Float))
157
+ 'x:decimal'
158
+ elsif(value.is_a?(true.class) || value.is_a?(false.class))
159
+ 'x:boolean'
160
+ elsif(value.is_a?(DateTime))
161
+ 'x:dateTime'
162
+ else
163
+ 'x:string'
164
+ end
165
+ end
166
+
167
+
168
+ private
169
+ #execute a request
170
+ def execute_request(req)
171
+ Net::HTTP.new(get_url).start {|http|
172
+ response = http.request(req)
173
+ }
174
+ end
175
+
176
+ #gets the base url to send the request to
177
+ def get_url
178
+ url = @url
179
+ if(!@authority.nil?)
180
+ url = @authority + "." + @url
181
+ end
182
+
183
+ url
184
+ end
185
+
186
+ #sends an HTTP delete request to SSDS
187
+ def delete(url, version = nil)
188
+ req = Net::HTTP::Delete.new(url)
189
+ req.content_type = 'application/x-ssds+xml'
190
+
191
+ if(!version.nil?)
192
+ req['if-match'] = version;
193
+ end
194
+
195
+ req.basic_auth @username, @password
196
+ execute_request(req)
197
+ end
198
+
199
+ #sends an HTTP get request to SSDS
200
+ def get(url, version = nil)
201
+ req = Net::HTTP::Get.new(url)
202
+ req.content_type = 'application/x-ssds+xml'
203
+
204
+ if(!version.nil?)
205
+ req['if-none-match'] = version;
206
+ end
207
+
208
+ req.basic_auth @username, @password
209
+ execute_request(req)
210
+ end
211
+
212
+ #sends an HTTP put request to SSDS
213
+ def put(url, xml, version = nil)
214
+ req = Net::HTTP::Put.new(url)
215
+ req.content_type = 'application/x-ssds+xml'
216
+
217
+ if(!version.nil?)
218
+ req['if-match'] = version;
219
+ end
220
+
221
+ req.content_length = xml.to_s.size.to_s
222
+ req.basic_auth @username, @password
223
+ req.body = xml.to_s
224
+ execute_request(req)
225
+ end
226
+
227
+ #determines the correct URL to use for a SSDS request
228
+ def get_request_url(container = nil, id = nil)
229
+ url = '/v1/'
230
+
231
+ #add the container to the url
232
+ if(!container.nil?)
233
+ url = url + container
234
+ end
235
+
236
+ #add the id to the end of the url if it is present
237
+ if(!id.nil?)
238
+ url = url + "/" + id.to_s
239
+ end
240
+
241
+ url
242
+ end
243
+
244
+ def create_post(xml, container=nil)
245
+ url = get_request_url(container)
246
+ req = Net::HTTP::Post.new(url)
247
+ req.content_type = 'application/x-ssds+xml'
248
+ req.content_length = xml.to_s.size.to_s
249
+ req.basic_auth @username, @password
250
+ req.body = xml.to_s
251
+ req
252
+ end
253
+
254
+ end
255
+ end