rgeoserver 0.5.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/.gitignore +12 -0
- data/Gemfile +8 -0
- data/README.rdoc +80 -0
- data/Rakefile +51 -0
- data/VERSION +1 -0
- data/config/config_defaults.yml +9 -0
- data/lib/rgeoserver.rb +45 -0
- data/lib/rgeoserver/catalog.rb +169 -0
- data/lib/rgeoserver/config.rb +7 -0
- data/lib/rgeoserver/coverage.rb +84 -0
- data/lib/rgeoserver/coverages.rb +114 -0
- data/lib/rgeoserver/coveragestore.rb +111 -0
- data/lib/rgeoserver/datastore.rb +110 -0
- data/lib/rgeoserver/datastores.rb +97 -0
- data/lib/rgeoserver/featuretype.rb +136 -0
- data/lib/rgeoserver/geoserver_url_helpers.rb +17 -0
- data/lib/rgeoserver/resource.rb +140 -0
- data/lib/rgeoserver/rest_api_client.rb +103 -0
- data/lib/rgeoserver/version.rb +9 -0
- data/lib/rgeoserver/wmsstore.rb +100 -0
- data/lib/rgeoserver/workspace.rb +74 -0
- data/rgeoserver.gemspec +31 -0
- data/spec/lib/integration_test_spec.rb +122 -0
- data/spec/spec_helper.rb +6 -0
- metadata +210 -0
@@ -0,0 +1,17 @@
|
|
1
|
+
|
2
|
+
module RGeoServer
|
3
|
+
module GeoServerUrlHelpers
|
4
|
+
API_DOCUMENTATION = "http://docs.geoserver.org/latest/en/user/restconfig/rest-config-api.html"
|
5
|
+
|
6
|
+
def url_for base, options = nil
|
7
|
+
return base unless options.is_a? Hash
|
8
|
+
format = options.delete(:format) || 'xml'
|
9
|
+
new_base = base.map{ |key,value| value.nil?? key.to_s : [key.to_s, CGI::escape(value.to_s)].join("/") }.join("/")
|
10
|
+
new_base = new_base.gsub(/\/$/,'')
|
11
|
+
new_base += ".#{format}"
|
12
|
+
"#{new_base}" + (("?#{options.map { |key, value| "#{CGI::escape(key.to_s)}=#{CGI::escape(value.to_s)}"}.join("&") }" if options and not options.empty?) || '')
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,140 @@
|
|
1
|
+
|
2
|
+
module RGeoServer
|
3
|
+
|
4
|
+
class ResourceInfo
|
5
|
+
|
6
|
+
include ActiveModel::Dirty
|
7
|
+
extend ActiveModel::Callbacks
|
8
|
+
|
9
|
+
define_model_callbacks :save, :destroy
|
10
|
+
define_model_callbacks :initialize, :only => :after
|
11
|
+
|
12
|
+
# mapping object parameters to profile elements
|
13
|
+
OBJ_ATTRIBUTES = {:enabled => 'enabled'}
|
14
|
+
OBJ_DEFAULT_ATTRIBUTES = {:enabled => true}
|
15
|
+
|
16
|
+
define_attribute_methods OBJ_ATTRIBUTES.keys
|
17
|
+
|
18
|
+
def self.update_attribute_accessors attributes
|
19
|
+
attributes.each do |attribute, profile_name|
|
20
|
+
class_eval <<-RUBY
|
21
|
+
def #{attribute.to_s}
|
22
|
+
@#{attribute} || profile['#{profile_name.to_s}'] || OBJ_DEFAULT_ATTRIBUTES[:#{attribute}]
|
23
|
+
end
|
24
|
+
|
25
|
+
def #{attribute.to_s}= val
|
26
|
+
#{attribute.to_s}_will_change! unless val == #{attribute.to_s}
|
27
|
+
@#{attribute.to_s} = val
|
28
|
+
end
|
29
|
+
RUBY
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def initialize options
|
34
|
+
@new = true
|
35
|
+
end
|
36
|
+
|
37
|
+
def to_s
|
38
|
+
"#{self.class.name}: #{name}"
|
39
|
+
end
|
40
|
+
|
41
|
+
# Add resource to Geoserver catalog
|
42
|
+
def self.create options, catalog = nil
|
43
|
+
raise GeoServerInvalidRequest, "Can't create a #{self.class.resource_name} resource with empty name" if options[:name].nil?
|
44
|
+
catalog ||= RGeoServer.catalog
|
45
|
+
catalog.add @route, xml(options)
|
46
|
+
self.class.new catalog, options
|
47
|
+
end
|
48
|
+
|
49
|
+
# Modify or save the resource
|
50
|
+
# @param options [Hash]
|
51
|
+
# @return [RGeoServer::ResourceInfo]
|
52
|
+
def save options = {}
|
53
|
+
@previously_changed = changes
|
54
|
+
@changed_attributes.clear
|
55
|
+
run_callbacks :save do
|
56
|
+
if new?
|
57
|
+
@catalog.add(@route, message)
|
58
|
+
clear
|
59
|
+
else
|
60
|
+
@catalog.modify({@route => @name}, message) #unless changes.empty?
|
61
|
+
end
|
62
|
+
|
63
|
+
self
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Purge resource from Geoserver Catalog
|
68
|
+
# @param options [Hash]
|
69
|
+
# @return [RGeoServer::ResourceInfo] `self`
|
70
|
+
def delete options = {}
|
71
|
+
run_callbacks :destroy do
|
72
|
+
@catalog.purge({@route => @name}, options) unless new?
|
73
|
+
clear
|
74
|
+
self
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# Check if this resource already exists
|
79
|
+
# @return [Boolean]
|
80
|
+
def new?
|
81
|
+
profile
|
82
|
+
@new
|
83
|
+
end
|
84
|
+
|
85
|
+
def clear
|
86
|
+
@profile = nil
|
87
|
+
@changed_attributes = {}
|
88
|
+
end
|
89
|
+
|
90
|
+
# Retrieve the resource profile as a hash and cache it
|
91
|
+
# @return [Hash]
|
92
|
+
def profile
|
93
|
+
if @profile
|
94
|
+
return @profile
|
95
|
+
end
|
96
|
+
|
97
|
+
@profile ||= begin
|
98
|
+
h = profile_xml_to_hash(@catalog.search @route => @name )
|
99
|
+
@new = false
|
100
|
+
h
|
101
|
+
rescue RestClient::ResourceNotFound
|
102
|
+
# The resource is new
|
103
|
+
@new = true
|
104
|
+
{}
|
105
|
+
end.freeze
|
106
|
+
end
|
107
|
+
|
108
|
+
def profile= profile_xml
|
109
|
+
@profile = profile_xml_to_hash(profile_xml)
|
110
|
+
end
|
111
|
+
|
112
|
+
def profile_xml_to_ng profile_xml
|
113
|
+
Nokogiri::XML(profile_xml).xpath(self.class.member_xpath)
|
114
|
+
end
|
115
|
+
|
116
|
+
def profile_xml_to_hash profile_xml
|
117
|
+
doc = profile_xml_to_ng profile_xml
|
118
|
+
h = {'name' => doc.at_xpath('//name').text.strip, 'enabled' => @enabled }
|
119
|
+
doc.xpath('//atom:link/@href', "xmlns:atom"=>"http://www.w3.org/2005/Atom" ).each{ |l|
|
120
|
+
target = l.text.match(/([a-zA-Z]+)\.xml$/)[1]
|
121
|
+
if !target.nil? && target != l.parent.parent.name.to_s.downcase
|
122
|
+
begin
|
123
|
+
h[l.parent.parent.name.to_s] << target
|
124
|
+
rescue
|
125
|
+
h[l.parent.parent.name.to_s] = []
|
126
|
+
end
|
127
|
+
else
|
128
|
+
h[l.parent.parent.name.to_s] = begin
|
129
|
+
response = @catalog.fetch_url l.text
|
130
|
+
Nokogiri::XML(response).xpath('//name/text()').collect{ |a| a.text }
|
131
|
+
rescue RestClient::ResourceNotFound
|
132
|
+
[]
|
133
|
+
end.freeze
|
134
|
+
end
|
135
|
+
}
|
136
|
+
h
|
137
|
+
end
|
138
|
+
|
139
|
+
end
|
140
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
|
2
|
+
require 'logger'
|
3
|
+
$logger = Logger.new(STDOUT)
|
4
|
+
$logger.level = Logger::WARN
|
5
|
+
|
6
|
+
|
7
|
+
module RGeoServer
|
8
|
+
module RestApiClient
|
9
|
+
|
10
|
+
include RGeoServer::GeoServerUrlHelpers
|
11
|
+
include ActiveSupport::Benchmarkable
|
12
|
+
|
13
|
+
def client config = {}
|
14
|
+
c = self.config.merge(config)
|
15
|
+
@client ||= RestClient::Resource.new(c[:url], :user => c[:user], :password => c[:password], :headers => c[:headers])
|
16
|
+
end
|
17
|
+
|
18
|
+
def headers format
|
19
|
+
sym = :xml || format.to_sym
|
20
|
+
{:accept => sym, :content_type=> sym}
|
21
|
+
end
|
22
|
+
|
23
|
+
# Search a resource in the catalog
|
24
|
+
# @param [OrderedHash] what
|
25
|
+
# @param [Hash] options
|
26
|
+
def search what, options = {}
|
27
|
+
resources = client[url_for(what, options)]
|
28
|
+
resources.options[:headers] ||= headers(:xml)
|
29
|
+
begin
|
30
|
+
return resources.get
|
31
|
+
rescue RestClient::InternalServerError => e
|
32
|
+
$logger.error e.response
|
33
|
+
$logger.flush if $logger.respond_to? :flush
|
34
|
+
raise GeoServerInvalidRequest, "Error listing #{what.inspect}. See $logger for details"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Fetch an arbitrary URL within the catalog
|
39
|
+
# @param [String] url
|
40
|
+
def fetch_url url
|
41
|
+
url.slice! client.url
|
42
|
+
fetcher = client[url]
|
43
|
+
fetcher.options[:headers] ||= headers(:xml)
|
44
|
+
begin
|
45
|
+
return fetcher.get
|
46
|
+
rescue RestClient::InternalServerError => e
|
47
|
+
$logger.error e.response
|
48
|
+
$logger.flush if $logger.respond_to? :flush
|
49
|
+
raise GeoServerInvalidRequest, "Error fetching URL: #{url}. See $logger for details"
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
# Add resource to the catalog
|
55
|
+
# @param [String] what
|
56
|
+
# @param [String] message
|
57
|
+
# @param [Symbol] method
|
58
|
+
def add what, message, method = :post
|
59
|
+
request = client[url_for(what)]
|
60
|
+
request.options[:headers] ||= headers(:xml)
|
61
|
+
begin
|
62
|
+
return request.send method, message
|
63
|
+
rescue RestClient::InternalServerError => e
|
64
|
+
$logger.error e.response
|
65
|
+
$logger.flush if $logger.respond_to? :flush
|
66
|
+
raise GeoServerInvalidRequest, "Error adding #{what.inspect}. See logger for details"
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
# Modify resource in the catalog
|
72
|
+
# @param [String] what
|
73
|
+
# @param [String] message
|
74
|
+
def modify what, message
|
75
|
+
request = client[url_for(what, {})]
|
76
|
+
request.options[:headers] ||= headers(:xml)
|
77
|
+
$logger.debug "Modifying: \n #{message}"
|
78
|
+
begin
|
79
|
+
return request.put message
|
80
|
+
rescue RestClient::InternalServerError => e
|
81
|
+
$logger.error e.response
|
82
|
+
$logger.flush if $logger.respond_to? :flush
|
83
|
+
raise GeoServerInvalidRequest, "Error modifying #{what.inspect}. See $logger for details"
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
# Purge resource from the catalog. Options can include recurse=true or false
|
89
|
+
# @param [OrderedHash] what
|
90
|
+
# @param [Hash] options
|
91
|
+
def purge what, options
|
92
|
+
request = client[url_for(what, options)]
|
93
|
+
begin
|
94
|
+
return request.delete
|
95
|
+
rescue RestClient::InternalServerError => e
|
96
|
+
$logger.error e.response
|
97
|
+
$logger.flush if $logger.respond_to? :flush
|
98
|
+
raise GeoServerInvalidRequest, "Error deleting #{what.inspect}. See $logger for details"
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
|
2
|
+
module RGeoServer
|
3
|
+
|
4
|
+
class WmsStore < ResourceInfo
|
5
|
+
|
6
|
+
define_attribute_methods [:catalog, :workspace, :name]
|
7
|
+
|
8
|
+
@@r = Confstruct::Configuration.new(
|
9
|
+
:route => "workspaces/%s/wmsstores",
|
10
|
+
:root => "wmsStores",
|
11
|
+
:resource_name => "wmsStore"
|
12
|
+
)
|
13
|
+
|
14
|
+
def self.root
|
15
|
+
@@r.root
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.method
|
19
|
+
:put
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.resource_name
|
23
|
+
@@r.resource_name
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.root_xpath
|
27
|
+
"//#{root}/#{resource_name}"
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.member_xpath
|
31
|
+
"//#{resource_name}"
|
32
|
+
end
|
33
|
+
|
34
|
+
def route
|
35
|
+
@@r.route % @workspace.name
|
36
|
+
end
|
37
|
+
|
38
|
+
def xml options = nil
|
39
|
+
<<-cs
|
40
|
+
<wmsStore>
|
41
|
+
<enabled>true</enabled>
|
42
|
+
<name>#{name}</name>
|
43
|
+
<workspace><name>#{workspace_name}</name></workspace>
|
44
|
+
</wmsStore>
|
45
|
+
cs
|
46
|
+
end
|
47
|
+
|
48
|
+
def name
|
49
|
+
@name
|
50
|
+
end
|
51
|
+
|
52
|
+
def workspace
|
53
|
+
@workspace
|
54
|
+
end
|
55
|
+
|
56
|
+
def catalog
|
57
|
+
@catalog
|
58
|
+
end
|
59
|
+
|
60
|
+
def workspace_name
|
61
|
+
@workspace.name
|
62
|
+
end
|
63
|
+
|
64
|
+
# @param [RGeoServer::Catalog] catalog
|
65
|
+
# @param [RGeoServer::Workspace|String] workspace
|
66
|
+
# @param [String] name
|
67
|
+
def initialize catalog, options
|
68
|
+
super({})
|
69
|
+
_run_initialize_callbacks do
|
70
|
+
@catalog = catalog
|
71
|
+
workspace = options[:workspace] || 'default'
|
72
|
+
if workspace.instance_of? String
|
73
|
+
@workspace = @catalog.get_workspace(workspace)
|
74
|
+
elsif workspace.instance_of? Workspace
|
75
|
+
@workspace = workspace
|
76
|
+
else
|
77
|
+
raise "Not a valid workspace"
|
78
|
+
end
|
79
|
+
@name = options[:name].strip
|
80
|
+
@route = route
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def name= val
|
85
|
+
name_will_change! unless val == @name
|
86
|
+
@name = val
|
87
|
+
end
|
88
|
+
|
89
|
+
def workspace= val
|
90
|
+
workspace_will_change! unless val == @workspace
|
91
|
+
@workspace = val
|
92
|
+
end
|
93
|
+
|
94
|
+
def catalog= val
|
95
|
+
catalog_will_change! unless val == @catalog
|
96
|
+
@catalog = val
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
|
2
|
+
module RGeoServer
|
3
|
+
|
4
|
+
class Workspace < ResourceInfo
|
5
|
+
|
6
|
+
OBJ_ATTRIBUTES = {:enabled => 'enabled', :catalog => 'catalog', :name => 'name' }
|
7
|
+
OBJ_DEFAULT_ATTRIBUTES = {:enabled => true, :catalog => nil, :name => nil }
|
8
|
+
|
9
|
+
define_attribute_methods OBJ_ATTRIBUTES.keys
|
10
|
+
update_attribute_accessors OBJ_ATTRIBUTES
|
11
|
+
|
12
|
+
@@r = Confstruct::Configuration.new(:route => 'workspaces', :resource_name => 'workspace')
|
13
|
+
|
14
|
+
def self.resource_name
|
15
|
+
@@r.resource_name
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.method
|
19
|
+
:post
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.root_xpath
|
23
|
+
"//#{@@r.route}/#{@@r.resource_name}"
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.member_xpath
|
27
|
+
"//#{resource_name}"
|
28
|
+
end
|
29
|
+
|
30
|
+
def route
|
31
|
+
@@r.route
|
32
|
+
end
|
33
|
+
|
34
|
+
def message
|
35
|
+
builder = Nokogiri::XML::Builder.new do |xml|
|
36
|
+
xml.workspace {
|
37
|
+
xml.enabled @enabled if enabled_changed?
|
38
|
+
xml.name @name
|
39
|
+
}
|
40
|
+
end
|
41
|
+
return builder.doc.to_xml
|
42
|
+
end
|
43
|
+
|
44
|
+
# @param [RGeoServer::Catalog] catalog
|
45
|
+
# @param [Hash] options
|
46
|
+
def initialize catalog, options
|
47
|
+
super({})
|
48
|
+
_run_initialize_callbacks do
|
49
|
+
@catalog = catalog
|
50
|
+
@name = options[:name].strip
|
51
|
+
end
|
52
|
+
@route = route
|
53
|
+
end
|
54
|
+
|
55
|
+
def data_stores
|
56
|
+
profile['dataStores'].collect{ |name|
|
57
|
+
DataStore.new @catalog, :workspace => self, :name => name if name
|
58
|
+
}
|
59
|
+
end
|
60
|
+
|
61
|
+
def coverage_stores
|
62
|
+
profile['coverageStores'].collect{ |name|
|
63
|
+
CoverageStore.new @catalog, :workspace => self, :name => name if name
|
64
|
+
}
|
65
|
+
end
|
66
|
+
|
67
|
+
def wms_stores
|
68
|
+
profile['wmsStores'].collect{ |name|
|
69
|
+
WmsStore.new @catalog, :workspace => self, :name => name if name
|
70
|
+
}
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
end
|