fireeagle 0.0.2 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +4 -0
- data/License.txt +1 -1
- data/Manifest.txt +11 -9
- data/README.txt +38 -10
- data/Rakefile +4 -123
- data/config/hoe.rb +70 -0
- data/config/requirements.rb +17 -0
- data/lib/fireeagle.rb +32 -42
- data/lib/fireeagle/client.rb +343 -0
- data/lib/fireeagle/location.rb +62 -66
- data/lib/fireeagle/response.rb +31 -0
- data/lib/fireeagle/user.rb +29 -35
- data/lib/fireeagle/version.rb +2 -2
- data/spec/fireeagle_location_spec.rb +60 -0
- data/spec/fireeagle_response_spec.rb +65 -0
- data/spec/fireeagle_spec.rb +150 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +128 -0
- data/tasks/environment.rake +7 -0
- data/tasks/rspec.rake +21 -0
- metadata +91 -58
- data/lib/fireeagle/apibase.rb +0 -6
- data/lib/fireeagle/application.rb +0 -37
- data/lib/fireeagle/base.rb +0 -89
- data/scripts/txt2html +0 -67
- data/test/test_fireeagle.rb +0 -30
- data/test/test_fireeagle_application.rb +0 -46
- data/test/test_fireeagle_location.rb +0 -97
- data/test/test_fireeagle_user.rb +0 -103
- data/test/test_helper.rb +0 -14
data/spec/spec.opts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--colour
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
begin
|
2
|
+
require 'spec'
|
3
|
+
rescue LoadError
|
4
|
+
require 'rubygems'
|
5
|
+
gem 'rspec'
|
6
|
+
require 'spec'
|
7
|
+
end
|
8
|
+
|
9
|
+
XML_ERROR_RESPONSE = <<-RESPONSE
|
10
|
+
<?xml version="1.0" encoding="utf-8"?>
|
11
|
+
<rsp stat="fail">
|
12
|
+
<err code="11" msg="Something bad happened" />
|
13
|
+
</rsp>
|
14
|
+
RESPONSE
|
15
|
+
|
16
|
+
XML_LOCATION_RESPONSE = <<-RESPONSE
|
17
|
+
<?xml version="1.0" encoding="utf-8"?>
|
18
|
+
<rsp stat="ok" xmlns:georss="http://www.georss.org/georss">
|
19
|
+
<user token="16w3z6ysudxt">
|
20
|
+
<location-hierarchy>
|
21
|
+
<location best-guess="false">
|
22
|
+
<georss:box>38.5351715088 -121.7948684692 38.575668335 -121.6747894287</georss:box>
|
23
|
+
<level>3</level>
|
24
|
+
<level-name>city</level-name>
|
25
|
+
<located-at>2008-01-22T14:23:11-08:00</located-at>
|
26
|
+
<name>Davis, CA</name>
|
27
|
+
<place-id>u4L9ZOObApTdx1q3</place-id>
|
28
|
+
</location>
|
29
|
+
<location best-guess="true">
|
30
|
+
<georss:box>38.3131217957 -122.4230804443 38.9261016846 -121.5012969971</georss:box>
|
31
|
+
<level>4</level>
|
32
|
+
<level-name>region</level-name>
|
33
|
+
<located-at>2008-01-22T18:45:26-08:00</located-at>
|
34
|
+
<name>Yolo County, California</name>
|
35
|
+
<place-id>YUYMh9CbBJ61mgFe</place-id>
|
36
|
+
</location>
|
37
|
+
<location best-guess="false">
|
38
|
+
<georss:box>32.5342788696 -124.4150238037 42.0093803406 -114.1308135986</georss:box>
|
39
|
+
<level>5</level>
|
40
|
+
<level-name>state</level-name>
|
41
|
+
<located-at>2008-01-22T18:45:26-08:00</located-at>
|
42
|
+
<name>California</name>
|
43
|
+
<place-id>SVrAMtCbAphCLAtP</place-id>
|
44
|
+
</location>
|
45
|
+
<location best-guess="false">
|
46
|
+
<georss:box>18.9108390808 -167.2764129639 72.8960571289 -66.6879425049</georss:box>
|
47
|
+
<level>6</level>
|
48
|
+
<level-name>country</level-name>
|
49
|
+
<located-at>2008-01-22T18:45:26-08:00</located-at>
|
50
|
+
<name>United States</name>
|
51
|
+
<place-id>4KO02SibApitvSBieQ</place-id>
|
52
|
+
</location>
|
53
|
+
</location-hierarchy>
|
54
|
+
</user>
|
55
|
+
</rsp>
|
56
|
+
RESPONSE
|
57
|
+
|
58
|
+
XML_SUCCESS_RESPONSE = <<-RESPONSE
|
59
|
+
<?xml version="1.0" encoding="utf-8"?>
|
60
|
+
<rsp stat="ok">
|
61
|
+
<user token="16w3z6ysudxt"/>
|
62
|
+
</rsp>
|
63
|
+
RESPONSE
|
64
|
+
|
65
|
+
XML_LOCATION_CHUNK = <<-RESPONSE
|
66
|
+
<location best-guess="false">
|
67
|
+
<georss:box>38.5351715088 -121.7948684692 38.575668335 -121.6747894287</georss:box>
|
68
|
+
<level>3</level>
|
69
|
+
<level-name>city</level-name>
|
70
|
+
<located-at>2008-01-22T14:23:11-08:00</located-at>
|
71
|
+
<name>Davis, CA</name>
|
72
|
+
<place-id>u4L9ZOObApTdx1q3</place-id>
|
73
|
+
</location>
|
74
|
+
RESPONSE
|
75
|
+
|
76
|
+
XML_EXACT_LOCATION_CHUNK = <<-RESPONSE
|
77
|
+
<location>
|
78
|
+
<georss:point>38.5351715088 -121.7948684692</georss:box>
|
79
|
+
</location>
|
80
|
+
RESPONSE
|
81
|
+
|
82
|
+
XML_LOOKUP_RESPONSE = <<-RESPONSE
|
83
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
84
|
+
<rsp stat="ok">
|
85
|
+
<querystring>q=30022</querystring>
|
86
|
+
<locations start="0" total="9" count="9">
|
87
|
+
<location>
|
88
|
+
<name>Alpharetta, GA 30022</name>
|
89
|
+
<place-id>IrhZMHuYA5s1fFi4Qw</place-id>
|
90
|
+
</location>
|
91
|
+
<location>
|
92
|
+
<name>Hannover, Region Hannover, Deutschland</name>
|
93
|
+
<place-id>88Hctc2bBZlhvlwbUg</place-id>
|
94
|
+
</location>
|
95
|
+
<location>
|
96
|
+
<name>Nîmes, Gard, France</name>
|
97
|
+
<place-id>Sut8q82bBZkF0s1eTg</place-id>
|
98
|
+
</location>
|
99
|
+
<location>
|
100
|
+
<name>Ceggia, Venezia, Italia</name>
|
101
|
+
<place-id>s9ulRieYA5TkNK9otw</place-id>
|
102
|
+
</location>
|
103
|
+
<location>
|
104
|
+
<name>Comitán de Domínguez, Comitan de Dominguez, México</name>
|
105
|
+
<place-id>.51HvYKbBZnSAeNHWw</place-id>
|
106
|
+
</location>
|
107
|
+
<location>
|
108
|
+
<name>Platanos Aitoloakarnanias, Etolia Kai Akarnania, Greece</name>
|
109
|
+
<place-id>CmfJ2H.YA5QKpS56HQ</place-id>
|
110
|
+
</location>
|
111
|
+
<location>
|
112
|
+
<name>Kraków, Kraków, Polska</name>
|
113
|
+
<place-id>9bYc0l.bA5vPTGscQg</place-id>
|
114
|
+
</location>
|
115
|
+
<location>
|
116
|
+
<name>Nakuru, Kenya</name>
|
117
|
+
<place-id>VDprypWYA5sujnZphA</place-id>
|
118
|
+
</location>
|
119
|
+
<location>
|
120
|
+
<name>Fez, Al Magreb</name>
|
121
|
+
<place-id>BxOaGgSYA5R40Nm1RA</place-id>
|
122
|
+
</location>
|
123
|
+
</locations>
|
124
|
+
</rsp>
|
125
|
+
RESPONSE
|
126
|
+
|
127
|
+
|
128
|
+
require File.dirname(__FILE__) + '/../lib/fireeagle'
|
data/tasks/rspec.rake
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
begin
|
2
|
+
require 'spec'
|
3
|
+
rescue LoadError
|
4
|
+
require 'rubygems'
|
5
|
+
require 'spec'
|
6
|
+
end
|
7
|
+
begin
|
8
|
+
require 'spec/rake/spectask'
|
9
|
+
rescue LoadError
|
10
|
+
puts <<-EOS
|
11
|
+
To use rspec for testing you must install rspec gem:
|
12
|
+
gem install rspec
|
13
|
+
EOS
|
14
|
+
exit(0)
|
15
|
+
end
|
16
|
+
|
17
|
+
desc "Run the specs under spec/models"
|
18
|
+
Spec::Rake::SpecTask.new do |t|
|
19
|
+
t.spec_opts = ['--options', "spec/spec.opts"]
|
20
|
+
t.spec_files = FileList['spec/*_spec.rb']
|
21
|
+
end
|
metadata
CHANGED
@@ -1,80 +1,113 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
|
-
rubygems_version: 0.9.2
|
3
|
-
specification_version: 1
|
4
2
|
name: fireeagle
|
5
3
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.0
|
7
|
-
date: 2007-06-22 00:00:00 -04:00
|
8
|
-
summary: Fire Eagle is a site that keeps track of your current location and helps you share it with other sites and services safely. There are hundreds of potential applications. This gem exposes the FireEagle API as Ruby Classes
|
9
|
-
require_paths:
|
10
|
-
- lib
|
11
|
-
email: jnewland@gmail.com
|
12
|
-
homepage: http://fireeagle.rubyforge.org
|
13
|
-
rubyforge_project: fireeagle
|
14
|
-
description: Fire Eagle is a site that keeps track of your current location and helps you share it with other sites and services safely. There are hundreds of potential applications. This gem exposes the FireEagle API as Ruby Classes
|
15
|
-
autorequire:
|
16
|
-
default_executable:
|
17
|
-
bindir: bin
|
18
|
-
has_rdoc: true
|
19
|
-
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
-
requirements:
|
21
|
-
- - ">"
|
22
|
-
- !ruby/object:Gem::Version
|
23
|
-
version: 0.0.0
|
24
|
-
version:
|
4
|
+
version: 0.6.0
|
25
5
|
platform: ruby
|
26
|
-
signing_key:
|
27
|
-
cert_chain:
|
28
|
-
post_install_message:
|
29
6
|
authors:
|
30
7
|
- Jesse Newland
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2008-03-05 00:00:00 -05:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: oauth
|
17
|
+
version_requirement:
|
18
|
+
version_requirements: !ruby/object:Gem::Requirement
|
19
|
+
requirements:
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 0.2.1
|
23
|
+
version:
|
24
|
+
- !ruby/object:Gem::Dependency
|
25
|
+
name: json
|
26
|
+
version_requirement:
|
27
|
+
version_requirements: !ruby/object:Gem::Requirement
|
28
|
+
requirements:
|
29
|
+
- - ">="
|
30
|
+
- !ruby/object:Gem::Version
|
31
|
+
version: 1.1.1
|
32
|
+
version:
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: hpricot
|
35
|
+
version_requirement:
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: "0.6"
|
41
|
+
version:
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: GeoRuby
|
44
|
+
version_requirement:
|
45
|
+
version_requirements: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - ">="
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: 1.3.2
|
50
|
+
version:
|
51
|
+
description: Ruby wrapper for Yahoo!'s FireEagle
|
52
|
+
email:
|
53
|
+
- jnewland@gmail.com
|
54
|
+
executables: []
|
55
|
+
|
56
|
+
extensions: []
|
57
|
+
|
58
|
+
extra_rdoc_files:
|
59
|
+
- History.txt
|
60
|
+
- License.txt
|
61
|
+
- Manifest.txt
|
62
|
+
- README.txt
|
31
63
|
files:
|
64
|
+
- config/hoe.rb
|
65
|
+
- config/requirements.rb
|
32
66
|
- History.txt
|
33
67
|
- License.txt
|
34
68
|
- Manifest.txt
|
35
69
|
- README.txt
|
36
70
|
- Rakefile
|
37
71
|
- lib/fireeagle.rb
|
38
|
-
- lib/fireeagle/
|
39
|
-
- lib/fireeagle/apibase.rb
|
40
|
-
- lib/fireeagle/base.rb
|
72
|
+
- lib/fireeagle/client.rb
|
41
73
|
- lib/fireeagle/location.rb
|
74
|
+
- lib/fireeagle/response.rb
|
42
75
|
- lib/fireeagle/user.rb
|
43
76
|
- lib/fireeagle/version.rb
|
44
|
-
- scripts/txt2html
|
45
77
|
- setup.rb
|
46
|
-
-
|
47
|
-
-
|
48
|
-
-
|
49
|
-
-
|
50
|
-
-
|
51
|
-
|
52
|
-
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
- test/test_helper.rb
|
78
|
+
- spec/fireeagle_location_spec.rb
|
79
|
+
- spec/fireeagle_response_spec.rb
|
80
|
+
- spec/fireeagle_spec.rb
|
81
|
+
- spec/spec.opts
|
82
|
+
- spec/spec_helper.rb
|
83
|
+
- tasks/environment.rake
|
84
|
+
- tasks/rspec.rake
|
85
|
+
has_rdoc: true
|
86
|
+
homepage: http://fireeagle.rubyforge.org
|
87
|
+
post_install_message:
|
57
88
|
rdoc_options:
|
58
89
|
- --main
|
59
90
|
- README.txt
|
60
|
-
|
61
|
-
-
|
62
|
-
|
63
|
-
|
64
|
-
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
91
|
+
require_paths:
|
92
|
+
- lib
|
93
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - ">="
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: "0"
|
98
|
+
version:
|
99
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: "0"
|
104
|
+
version:
|
69
105
|
requirements: []
|
70
106
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
- !ruby/object:Gem::Version
|
79
|
-
version: 0.5.145
|
80
|
-
version:
|
107
|
+
rubyforge_project: fireeagle
|
108
|
+
rubygems_version: 1.0.1
|
109
|
+
signing_key:
|
110
|
+
specification_version: 2
|
111
|
+
summary: Ruby wrapper for Yahoo!'s FireEagle
|
112
|
+
test_files: []
|
113
|
+
|
data/lib/fireeagle/apibase.rb
DELETED
@@ -1,37 +0,0 @@
|
|
1
|
-
class FireEagle::Application < FireEagle::APIBase
|
2
|
-
#A user must authorize (or grant permissions to) an application before the application can write or read the user's location. This authorization process begins by directing a user to the authorization page, where the user logs in and chooses which permissions to grant to the requesting application. The authorization page generates a user token, specific to the requesting application, which can be returned to the requesting application in several ways:
|
3
|
-
#
|
4
|
-
#1. User token passed back to requesting application via callback URL (if the callback parameter is supplied)
|
5
|
-
#2. User token displayed onscreen (this is good for testing, but will likely be phased out or modified in the future)
|
6
|
-
#3. User token is exchanged at a later time (good for mobile devices where a user may not want to enter their Yahoo! username/password)
|
7
|
-
#
|
8
|
-
def authorize_url(callback_url = nil)
|
9
|
-
if callback_url.nil?
|
10
|
-
return "http://#{FireEagle::API_DOMAIN}/authorize.php?appid=#{@fireeagle.token}"
|
11
|
-
else
|
12
|
-
return "http://#{FireEagle::API_DOMAIN}/authorize.php?appid=#{@fireeagle.token}&callback=#{CGI::escape(callback_url)}"
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
# URL needed to obtain a token to pass to exchange_token
|
17
|
-
def token_url
|
18
|
-
"http://#{FireEagle::API_DOMAIN}/displayToken.php?appid=#{@fireeagle.token}"
|
19
|
-
end
|
20
|
-
|
21
|
-
#Applications which do not maintain a user repository (for example, a lightweight mobile application) may not wish to use the callback mechanism described above. These applications still must send users to authorize.php, but do not expect an immediate response (though at the moment, the authorize page will display the user token for easy testing and debugging). After the user has authorized an application, the application can later retrieve the user token in exchange for a short code obtained by the user from the Fire Eagle site. The user would retrieve a short code from the FE site and enter the code into the application which would then use the exchangeToken.php to exchange the short code for a full user token.
|
22
|
-
#
|
23
|
-
#To obtain a short code users visit http://fireeagle.research.yahoo.com/displayToken.php?appid=TOKEN, where app token is the application token.
|
24
|
-
#
|
25
|
-
#Returnes an instance of FireEagle::User
|
26
|
-
def exchange_token(shortcode = nil)
|
27
|
-
raise FireEagle::ArgumentError, "Shortcode required" if shortcode.nil?
|
28
|
-
request("exchangeToken", :shortcode => shortcode.to_s)
|
29
|
-
end
|
30
|
-
|
31
|
-
private
|
32
|
-
|
33
|
-
def parse_response(doc)
|
34
|
-
raise FireEagle::FireEagleException, doc.at("/resultset/errormessage").innerText unless doc.at("/resultset").nil?
|
35
|
-
FireEagle::User.new_from_xml(@fireeagle, doc)
|
36
|
-
end
|
37
|
-
end
|
data/lib/fireeagle/base.rb
DELETED
@@ -1,89 +0,0 @@
|
|
1
|
-
#The base class for interfacing w/ FireEagle
|
2
|
-
#
|
3
|
-
# @fireeagle = Fireagle::Base.new(:token => "foo", :secret => "bar")
|
4
|
-
#
|
5
|
-
#Fire Eagle (FE) is a system providing centralized management of user location information. FE allows 3rd party developers to update and/or access user's location data.
|
6
|
-
#
|
7
|
-
#There are 3 data entities at Fire Eagle's core:
|
8
|
-
#
|
9
|
-
#* User: Users can grant 3rd party applications privileges to read or write the user's location data. Users identify themselves to the FE system with their Yahoo! username/password. Applications identify a user with an application-specific user token (referred to in the API as a userid) generated by FE.
|
10
|
-
#* Application: An application is identified by system-wide application token. Applications use this token (referred to in the API as an appid) and an application-specific secret to identify themselves to FE when reading or writing user location data.
|
11
|
-
#* Location: FE stores the most recent location supplied by each application for each user. Applications can specify location in a number of ways: e.g. a latitude/longitude pair, a postal code, a street address, a GSM cell tower identifier, or an identifier from an external system (Plazes, Upcoming.org, etc.). See the Update API section for all possible location input options.
|
12
|
-
#
|
13
|
-
#Only one location per user-application pair is stored, thus FE provides access only to a user's current location, not a location history of where the user has been. When a user's location is requested FE combines location info from one or more applications into a current best guess location record which is returned in XML format -- see the Query API Response section, below for details.
|
14
|
-
class FireEagle::Base
|
15
|
-
|
16
|
-
attr_reader :token, :secret
|
17
|
-
|
18
|
-
#Create a new FireEagle::Base object.
|
19
|
-
#
|
20
|
-
# @fireeagle = Fireagle::Base.new(:token => "foo", :secret => "bar")
|
21
|
-
def initialize(options = {})
|
22
|
-
options = { :token => nil, :secret => nil }.merge(options)
|
23
|
-
raise FireEagle::ArgumentError, "Token and Secret required" if options[:token].nil? || options[:secret].nil?
|
24
|
-
@token = options[:token]
|
25
|
-
@secret = options[:secret]
|
26
|
-
@fireeagle = self
|
27
|
-
end
|
28
|
-
|
29
|
-
# Return an instance of FireEagle::Application
|
30
|
-
def application() @application ||= FireEagle::Application.new(self) end
|
31
|
-
|
32
|
-
private
|
33
|
-
|
34
|
-
# do the required api signing
|
35
|
-
def encode_and_sign(params = {})
|
36
|
-
|
37
|
-
#check for the params
|
38
|
-
raise ArgumentError, "Params required" if params == {} || !params.is_a?(Hash)
|
39
|
-
|
40
|
-
#remove any provided sig
|
41
|
-
params.delete(:sig)
|
42
|
-
|
43
|
-
#add the app token id
|
44
|
-
params[:appid] = @fireeagle.token
|
45
|
-
|
46
|
-
#sort and URL encode the params
|
47
|
-
normalized_params = {}
|
48
|
-
params.each_pair { |name, value| normalized_params[name.to_s] = value.to_s }
|
49
|
-
|
50
|
-
#build the string to sign
|
51
|
-
sig = "#{@fireeagle.secret}"
|
52
|
-
normalized_params.sort.each { |name, value| sig << "#{name}#{value}" }
|
53
|
-
|
54
|
-
#sign it
|
55
|
-
normalized_params['sig'] = Digest::SHA1.hexdigest(sig)
|
56
|
-
|
57
|
-
#create and return the request string
|
58
|
-
request_string = "?" + normalized_params.sort.collect { |name, value| "#{name}=#{CGI::escape(value)}" }.join('&')
|
59
|
-
end
|
60
|
-
|
61
|
-
# request method which is used by all public methods
|
62
|
-
def request(action = nil,params = {})
|
63
|
-
raise ArgumentError, "Action name required" if action.nil?
|
64
|
-
|
65
|
-
#reject crap
|
66
|
-
params.reject { |key, value| value.nil? or (value.is_a?(String) and value.empty?)}
|
67
|
-
#merge timestamp
|
68
|
-
params.merge!( { :timestamp => Time.now.to_i } )
|
69
|
-
|
70
|
-
path = "#{FireEagle::API_PATH}#{action}.php#{encode_and_sign(params)}"
|
71
|
-
|
72
|
-
puts path if FireEagle::DEBUG
|
73
|
-
|
74
|
-
response = Net::HTTP.get_response(FireEagle::API_DOMAIN, path)
|
75
|
-
|
76
|
-
parse_response(Hpricot(response.body))
|
77
|
-
end
|
78
|
-
|
79
|
-
def parse_response(doc)
|
80
|
-
raise FireEagleException, doc.at("/resultset/errormessage").innerText unless !doc.at("/resultset").nil? and doc.at("/resultset/error").innerText.to_i == "0"
|
81
|
-
doc.at("/result")
|
82
|
-
end
|
83
|
-
|
84
|
-
def fireeagle
|
85
|
-
fireeagle
|
86
|
-
end
|
87
|
-
|
88
|
-
|
89
|
-
end
|