dm-keeper-adapter 0.0.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 ADDED
@@ -0,0 +1,6 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ Gemfile
5
+ pkg/*
6
+ *~
data/README.rdoc ADDED
@@ -0,0 +1,40 @@
1
+ = dm-keeper-adapter
2
+
3
+ A DataMapper adapter for features.opensuse.org
4
+
5
+ == Usage
6
+
7
+ This adapter allows easy access to the openSUSE feature database using
8
+ DataMapper. It supports features, relationtrees, productlines, and
9
+ products.
10
+
11
+ features.opensuse.org is the web frontend, the database server
12
+ provides an xquery API at https://keeper.novell.com/sxkeeper, which is
13
+ used by this adapter - hence the name.
14
+
15
+ == Install
16
+
17
+ Install it with
18
+
19
+ gem install dm-keeper-adapter
20
+
21
+ == Code
22
+
23
+ require 'rubygems'
24
+ require 'dm-core'
25
+ require 'dm-keeper-adapter'
26
+
27
+ DataMapper::Logger.new($stdout, :debug)
28
+
29
+ # Retrieves credentials from ~/.oscrc if exists
30
+ # Otherwise add user:pass@ before keeper.novell.com
31
+ keeper = DataMapper.setup(:default, :adapter => 'keeper', :url => 'https://keeper.novell.com/sxkeeper')
32
+
33
+ require 'keeper/feature'
34
+ DataMapper.finalize
35
+
36
+ f = Feature.get(311545)
37
+
38
+ == TODO
39
+
40
+ Real DataMapper queries mapped to XPath
data/Rakefile ADDED
@@ -0,0 +1,29 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
3
+
4
+ extra_docs = ['README*', 'TODO*', 'CHANGELOG*']
5
+
6
+ task :default => [:test]
7
+
8
+ Rake::TestTask.new do |t|
9
+ t.libs << File.expand_path('../test', __FILE__)
10
+ t.libs << File.expand_path('../', __FILE__)
11
+ t.test_files = FileList['test/test*.rb']
12
+ t.verbose = true
13
+ end
14
+
15
+ begin
16
+ require 'yard'
17
+ YARD::Rake::YardocTask.new(:doc) do |t|
18
+ t.files = ['lib/**/*.rb', *extra_docs]
19
+ t.options = ['--no-private']
20
+ end
21
+ rescue LoadError
22
+ STDERR.puts "Install yard if you want prettier docs"
23
+ require 'rdoc/task'
24
+ Rake::RDocTask.new(:doc) do |rdoc|
25
+ rdoc.rdoc_dir = "doc"
26
+ rdoc.title = "dm-keeper-adapter #{DataMapper::Adapters::KeeperAdapter::VERSION}"
27
+ extra_docs.each { |ex| rdoc.rdoc_files.include ex }
28
+ end
29
+ end
@@ -0,0 +1,27 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "dm-keeper-adapter/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "dm-keeper-adapter"
7
+ s.version = DataMapper::Adapters::KeeperAdapter::VERSION
8
+
9
+ s.platform = Gem::Platform::RUBY
10
+ s.authors = ["Klaus Kämpf"]
11
+ s.email = ["kkaempf@suse.de"]
12
+ s.homepage = ""
13
+ s.summary = %q{A datamapper adapter for FATE (aka keeper.novell.com)}
14
+ s.description = %q{Use it in Ruby applications to access FATE}
15
+
16
+ # get credentials from ~/.oscrc
17
+ s.add_dependency("inifile", ["~> 0.4.1"])
18
+ # parse xml response
19
+ s.add_dependency("nokogiri", ["~> 1.4"])
20
+
21
+ s.rubyforge_project = "dm-keeper-adapter"
22
+
23
+ s.files = `git ls-files`.split("\n")
24
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
25
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
26
+ s.require_paths = ["lib"]
27
+ end
@@ -0,0 +1,78 @@
1
+ require 'rubygems'
2
+ require 'dm-core'
3
+ require 'dm-core/adapters/abstract_adapter'
4
+
5
+ require "dm-keeper-adapter/create"
6
+ require "dm-keeper-adapter/read"
7
+ require "dm-keeper-adapter/update"
8
+ require "dm-keeper-adapter/delete"
9
+ require "dm-keeper-adapter/misc"
10
+
11
+
12
+ module DataMapper
13
+ class Property
14
+ autoload :XML, "property/xml"
15
+ end
16
+ end
17
+
18
+
19
+ module DataMapper::Adapters
20
+
21
+ class KeeperAdapter < AbstractAdapter
22
+ OSCRC_CREDENTIALS = "https://api.opensuse.org"
23
+
24
+ def initialize(name, options)
25
+ super
26
+ require 'net/https'
27
+ require 'uri'
28
+ @uri = URI.parse(options[:url])
29
+ @username = options[:username]
30
+ @password = options[:password]
31
+ unless @username && @password
32
+ require 'inifile'
33
+ oscrc = IniFile.new(File.join(ENV['HOME'], '.oscrc'))
34
+ if oscrc.has_section?(OSCRC_CREDENTIALS)
35
+ @username = oscrc[OSCRC_CREDENTIALS]['user']
36
+ @password = oscrc[OSCRC_CREDENTIALS]['pass']
37
+ raise "No .oscrc credentials for keeper.novell.com" unless @username && @password
38
+ end
39
+ end
40
+ @connection = Net::HTTP.new( @uri.host, @uri.port )
41
+ # from http://www.rubyinside.com/nethttp-cheat-sheet-2940.html
42
+ @connection.use_ssl = true if @uri.scheme == "https"
43
+ @connection.verify_mode = OpenSSL::SSL::VERIFY_NONE
44
+
45
+ end
46
+
47
+ def get(path)
48
+ # STDERR.puts "Get #{@uri.path} + #{path}"
49
+ request = Net::HTTP::Get.new @uri.path + path
50
+ request.basic_auth @username, @password
51
+ response = @connection.request request
52
+ raise ArgumentError unless response
53
+ raise( RuntimeError, "Server returned #{response.code}" ) unless response.code.to_i == 200
54
+ # $stderr.puts "#{response.inspect}"
55
+ # response.each { |k,v| $stderr.puts "#{k}: #{v}" }
56
+ # $stderr.puts "Encoding >#{response['content-encoding']}<"
57
+ # check
58
+ # content-type: text/xml;charset=UTF-8
59
+ # content-encoding: gzip
60
+ body = response.body
61
+ if response['content-encoding'] == "gzip"
62
+ require 'zlib'
63
+ require 'stringio'
64
+ # http://stackoverflow.com/questions/1361892/how-to-decompress-gzip-string-in-ruby
65
+ body = Zlib::GzipReader.new(StringIO.new(body)).read
66
+ end
67
+ case response['content-type']
68
+ when /xml/
69
+ Nokogiri::XML body
70
+ when /html/
71
+ Nokogiri::HTML body
72
+ else
73
+ raise TypeError, "Unknown content-type #{response['content-type']}"
74
+ end
75
+ end
76
+
77
+ end
78
+ end
@@ -0,0 +1,21 @@
1
+ # create.rb
2
+ module DataMapper::Adapters
3
+ class KeeperAdapter < AbstractAdapter
4
+ def create(resources)
5
+ table_groups = group_resources_by_table(resources)
6
+ table_groups.each do |table, resources|
7
+ # make
8
+ # class User
9
+ # property :id, Serial
10
+ # end
11
+ # work
12
+ resources.each do |resource|
13
+ initialize_serial(resource,
14
+ worksheet_record_cound(table)+1)
15
+ post_resource_to_worksheet(resource,table)
16
+ end
17
+ end
18
+ resources.size
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,12 @@
1
+ # delete.rb
2
+ module DataMapper::Adapters
3
+ class KeeperAdapter < AbstractAdapter
4
+ def delete(collection)
5
+ each_resource_with_edit_url(collection) do |resource, edit_url|
6
+ connection.delete(edit_url, 'If-Match' => "*")
7
+ end
8
+ # return count
9
+ collection.size
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,9 @@
1
+ # misc.rb
2
+ module DataMapper::Adapters
3
+ class KeeperAdapter < AbstractAdapter
4
+ # Auto-migration
5
+ # DataMapper.auto_migrate!
6
+ # or
7
+ # DataMapper.auto_update!
8
+ end
9
+ end
@@ -0,0 +1,168 @@
1
+ # read.rb
2
+ module DataMapper::Adapters
3
+ class KeeperAdapter < AbstractAdapter
4
+ require 'nokogiri'
5
+ def read(query)
6
+ records = records_for(query)
7
+ # query.filter_records(records)
8
+ end
9
+
10
+ private
11
+ # taken from https://github.com/whoahbot/dm-redis-adapter/
12
+
13
+ ##
14
+ # Retrieves records for a particular model.
15
+ #
16
+ # @param [DataMapper::Query] query
17
+ # The query used to locate the resources
18
+ #
19
+ # @return [Array]
20
+ # An array of hashes of all of the records for a particular model
21
+ #
22
+ # @api private
23
+ def records_for(query)
24
+ # $stderr.puts "records_for(#{query})"
25
+ # $stderr.puts "records_for(#{query.inspect})"
26
+ records = []
27
+ if query.conditions.nil?
28
+ # return all
29
+ else
30
+ query.conditions.operands.each do |operand|
31
+ if operand.is_a?(DataMapper::Query::Conditions::OrOperation)
32
+ operand.each do |op|
33
+ records = records + perform_query(query, op)
34
+ end
35
+ else
36
+ records = perform_query(query, operand)
37
+ end
38
+ end
39
+ end
40
+ records
41
+ end #def
42
+
43
+ ##
44
+ # Find records that match have a matching value
45
+ #
46
+ # @param [DataMapper::Query] query
47
+ # The query used to locate the resources
48
+ #
49
+ # @param [DataMapper::Operation] the operation for the query
50
+ #
51
+ # @return [Array]
52
+ # An array of hashes of all of the records for a particular model
53
+ #
54
+ # @api private
55
+ def perform_query(query, operand)
56
+ # $stderr.puts "perform_query(#{query},#{operand})"
57
+ records = []
58
+
59
+ if operand.is_a?(DataMapper::Query::Conditions::NotOperation)
60
+ subject = operand.first.subject
61
+ value = operand.first.value
62
+ elsif operand.subject.is_a?(DataMapper::Associations::ManyToOne::Relationship)
63
+ subject = operand.subject.child_key.first
64
+ value = operand.value[operand.subject.parent_key.first.name]
65
+ else
66
+ subject = operand.subject
67
+ value = operand.value
68
+ end
69
+
70
+ if subject.is_a?(DataMapper::Associations::ManyToOne::Relationship)
71
+ subject = subject.child_key.first
72
+ end
73
+
74
+ # typical queries
75
+ #
76
+ # ?query=/feature[
77
+ # productcontext[
78
+ # not (status[done or rejected or duplicate or unconfirmed])
79
+ # ]
80
+ # and
81
+ # actor[
82
+ # (person/userid='kkaempf@novell.com' or person/email='kkaempf@novell.com' or person/fullname='kkaempf@novell.com')
83
+ # and
84
+ # role='projectmanager'
85
+ # ]
86
+ # ]
87
+ #
88
+ # "/#{container}[actor/role='infoprovider']
89
+ #
90
+ # query=/feature[title='Foo%20bar%20baz']
91
+ #
92
+ # query=/feature[contains(title,'Foo')]
93
+ # query=/feature[contains(title,'Foo')]/title
94
+ # query=/feature[contains(title,'Foo')]/@k:id
95
+ #
96
+
97
+ # $stderr.puts "perform_query(subject #{subject.inspect},#{value.inspect})"
98
+ container = query.model.to_s.downcase
99
+ if query.model.key.include?(subject)
100
+ # get single <feature>
101
+ records << node_to_record(query.model, get("/#{container}/#{value}").root)
102
+ else
103
+ # query, get <collection>[<object><feature>...]*
104
+ xpath = "/#{container}["
105
+ # ...
106
+ xpath << "]"
107
+ collection = get("/#{container}?query=#{xpath}")
108
+ collection.xpath("/#{container}").each do |feature|
109
+ records << node_to_record(query.model, feature)
110
+ end
111
+ end
112
+
113
+ records
114
+ end # def
115
+
116
+ ##
117
+ # Convert feature (as xml) into record (as hash of key/value pairs)
118
+ #
119
+ # @param [Nokogiri::XML::Node] node
120
+ # A node
121
+ #
122
+ # @return [Hash]
123
+ # A hash of all of the properties for a particular record
124
+ #
125
+ # @api private
126
+ def node_to_record(model, node)
127
+ record = { }
128
+ xpathmap = model.xpathmap rescue { }
129
+ xmlnamespaces = model.xmlnamespaces rescue nil
130
+ # $stderr.puts "node_to_record(#{model}:#{node.class})"
131
+ model.properties.each do |property|
132
+ xpath = xpathmap[property.name] || property.name
133
+ key = property.name.to_s
134
+ children = node.xpath("./#{xpath}", xmlnamespaces)
135
+ # $stderr.puts "Property found: #{property.inspect}"
136
+ case children.size
137
+ when 0: next
138
+ when 1: value = children.text
139
+ else
140
+ value = children.to_xml
141
+ end
142
+ # $stderr.puts "Key #{key}, Value #{value} <#{property.class}>"
143
+ case property
144
+ when DataMapper::Property::Date
145
+ require 'parsedate'
146
+ record[key] = Time.utc(ParseDate.parsedate(value))
147
+ when DataMapper::Property::Integer
148
+ record[key] = value.to_s
149
+ when DataMapper::Property::String
150
+ record[key] = value.to_s
151
+ else
152
+ raise TypeError, "#{property} unsupported"
153
+ end
154
+ end
155
+ model.relationships.each do |rel|
156
+ # $stderr.puts "Rel ? #{rel.inspect}"
157
+ children = node.xpath("./#{rel.child_model_name.downcase}")
158
+ value = []
159
+ while n = children.shift
160
+ value << node_to_record(rel.child_model, n)
161
+ end
162
+ record[rel.name.to_s] = value
163
+ # $stderr.puts "#{rel.name} -> #{value.inspect}"
164
+ end
165
+ record
166
+ end
167
+ end # class
168
+ end # module
@@ -0,0 +1,12 @@
1
+ # update.rb
2
+ module DataMapper::Adapters
3
+ class KeeperAdapter < AbstractAdapter
4
+ def update(attributes, collection)
5
+ each_resource_with_edit_url(collection) do |resource, edit_url|
6
+ put_updated_resource(edit_url, resource)
7
+ end
8
+ # return count
9
+ collection.size
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,8 @@
1
+ require 'dm-core'
2
+ require 'dm-core/adapters/abstract_adapter'
3
+
4
+ module DataMapper::Adapters
5
+ class KeeperAdapter < AbstractAdapter
6
+ VERSION = "0.0.1"
7
+ end
8
+ end
@@ -0,0 +1,58 @@
1
+ require "dm-keeper-adapter"
2
+ require "nokogiri"
3
+
4
+ module Keeper
5
+ class ContainerEntry
6
+ attr_reader :name, :description, :size, :postmaster
7
+ #
8
+ # parse
9
+ # <tr class="title">
10
+ # <td colspan="2"><b>product</b></td></tr>
11
+ # <tr><td><b>Description:</b></td><td>Container for storing product definitions from NPP.</td></tr>
12
+ # <tr><td><b>Documents:</b></td><td>#325</td></tr>
13
+ # <tr><td><b>Update notification class: </b></td><td>null</td></tr>
14
+ # <tr><td><b>Read-only: </b></td><td>false</td></tr>
15
+ # <tr><td><b>Notification debug: </b></td><td>false</td></tr>
16
+ # <tr><td><b>Notification postmaster: </b></td><td>tschmidt@suse.de</td></tr>
17
+ #
18
+ def initialize node
19
+ # $stderr.puts "ContainerEntry(#{node})"
20
+ @name = node.xpath("./td/b").text
21
+ while node = node.next do # next <tr>
22
+ break if node['class']
23
+ key = value = nil
24
+ node.children.each do |child| # iterate over <td>
25
+ if child.child.name == "b" # <td><b>
26
+ key = child.child.text
27
+ else
28
+ value = child.text
29
+ end
30
+ if key && value
31
+ case key
32
+ when "Description:": @description = value
33
+ when "Documents:": @size = value.to_i
34
+ when "Notification postmaster: ": @postmaster = value
35
+ end
36
+ key = value = nil
37
+ end
38
+ end
39
+ end
40
+ end
41
+ def to_s
42
+ "#{@name}: #{@description}"
43
+ end
44
+ end
45
+ class Container
46
+ def initialize adapter
47
+ @adapter = adapter
48
+ @entries = []
49
+ node = adapter.get "/info"
50
+ node.xpath("//tr[@class='title']").each do |node|
51
+ @entries << ContainerEntry.new(node)
52
+ end
53
+ end
54
+ def each
55
+ @entries.each { |e| yield e }
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,31 @@
1
+ #
2
+ # Feature for datamapper
3
+ #
4
+ require 'rubygems'
5
+ require 'dm-core'
6
+
7
+ class Feature
8
+ include DataMapper::Resource
9
+
10
+ def self.xpathmap
11
+ { :id => "@k:id", :milestone => "productcontext/milestone/name", :actors => "actor",
12
+ :requester => "actor[role='requester']/person/email",
13
+ :productmgr => "actor[role='productmanager']/person/email",
14
+ :projectmgr => "actor[role='projectmanager']/person/email",
15
+ :engmgr => "actor[role='teamleader']/person/email",
16
+ :developer => "actor[role='developer']/person/email"
17
+ }
18
+ end
19
+ def self.xmlnamespaces
20
+ { "k" => "http://inttools.suse.de/sxkeeper/schema/keeper" }
21
+ end
22
+
23
+ property :id, Integer, :key => true
24
+ property :title, String
25
+ property :requester, String
26
+ property :productmgr, String
27
+ property :projectmgr, String
28
+ property :engmgr, String
29
+ property :developer, String
30
+ property :milestone, String
31
+ end
@@ -0,0 +1,14 @@
1
+ require 'rubygems'
2
+ require 'dm-core'
3
+ class Product
4
+ include DataMapper::Resource
5
+ property :id, Integer, :key => true
6
+ has 1, :productline
7
+ property :fatename, String
8
+ property :shortname, String
9
+ property :longname, String
10
+ property :bugzillaname, String
11
+ has n, :milestone
12
+ has n, :component
13
+ property :releasedate, Date
14
+ end
@@ -0,0 +1,7 @@
1
+ require 'rubygems'
2
+ require 'dm-core'
3
+ class Productline
4
+ include DataMapper::Resource
5
+ property :id, Integer, :key => true
6
+ property :name, String
7
+ end
@@ -0,0 +1,13 @@
1
+ require 'rubygems'
2
+ require 'dm-core'
3
+ class Relation
4
+ include DataMapper::Resource
5
+ def self.xpathmap
6
+ { :target => "@target", :sort_position => "@sortPosition" }
7
+ end
8
+ property :target, Integer, :key => true
9
+ property :sort_position, Integer
10
+ has 1, :parent, self
11
+ belongs_to :relation, :required => false
12
+ belongs_to :relationtree, :required => false
13
+ end
@@ -0,0 +1,20 @@
1
+ require 'rubygems'
2
+ require 'dm-core'
3
+ require 'dm-types'
4
+
5
+ require 'keeper/relation'
6
+
7
+ class Relationtree
8
+ include DataMapper::Resource
9
+ def self.xpathmap
10
+ { :id => "@k:id" }
11
+ end
12
+ def self.xmlnamespaces
13
+ { "k" => "http://inttools.suse.de/sxkeeper/schema/keeper" }
14
+ end
15
+ property :id, Integer, :key => true
16
+ property :title, String
17
+ property :description, String
18
+ # has n, :relations
19
+ property :relations, Csv
20
+ end
@@ -0,0 +1,99 @@
1
+ module Keeper
2
+ class Containers
3
+
4
+ def initialize(name, options)
5
+ super
6
+ require 'net/https'
7
+ require 'uri'
8
+ @uri = URI.parse(options[:url])
9
+ @username = options[:username]
10
+ @password = options[:password]
11
+ @connection = Net::HTTP.new( @uri.host, @uri.port )
12
+ # from http://www.rubyinside.com/nethttp-cheat-sheet-2940.html
13
+ @connection.use_ssl = true if @uri.scheme == "https"
14
+ @connection.verify_mode = OpenSSL::SSL::VERIFY_NONE
15
+
16
+ end
17
+
18
+ def categories
19
+ @categories ||= values_of "list_categories", "/value"
20
+ end
21
+ def partners
22
+ @partners ||= values_of "list_partner", "/value"
23
+ end
24
+ def roles
25
+ @roles ||= values_of "list_roles"
26
+ end
27
+ def viewparts
28
+ @viewparts ||= values_of("list_view_parts")
29
+ end
30
+ def xmlfields
31
+ @xmlfields ||= values_of("list_xmlfields")
32
+ end
33
+
34
+ def get(path)
35
+ # $stderr.puts "Get #{@uri.path} + #{path}"
36
+ request = Net::HTTP::Get.new @uri.path + path
37
+ request.basic_auth @username, @password
38
+ response = @connection.request request
39
+ raise ArgumentError unless response
40
+ raise( RuntimeError, "Server returned #{response.code}" ) unless response.code.to_i == 200
41
+ # $stderr.puts "#{response.inspect}"
42
+ # response.each { |k,v| $stderr.puts "#{k}: #{v}" }
43
+ # $stderr.puts "Encoding >#{response['content-encoding']}<"
44
+ # check
45
+ # content-type: text/xml;charset=UTF-8
46
+ # content-encoding: gzip
47
+ raise TypeError unless response['content-type'] =~ /xml/
48
+ body = response.body
49
+ if response['content-encoding'] == "gzip"
50
+ require 'zlib'
51
+ require 'stringio'
52
+ # http://stackoverflow.com/questions/1361892/how-to-decompress-gzip-string-in-ruby
53
+ body = Zlib::GzipReader.new(StringIO.new(body)).read
54
+ end
55
+ Nokogiri::XML body
56
+ end
57
+
58
+ private
59
+ def values_of id, detail = nil
60
+ # STDERR.puts "----------------------\nextract_values #{id}\n#-------------------"
61
+
62
+ if detail
63
+ result = []
64
+ else
65
+ result = {}
66
+ end
67
+
68
+ valuelist.xpath("//valuelist[@id='#{id}']/item#{detail}").each do |node|
69
+ if detail
70
+ # node = <value>foo</value>
71
+ result << node.text
72
+ else
73
+ # node = <item><value>...</value><property name='...'>...</property>
74
+ # with multiple properties
75
+ props = {}
76
+ # puts "Node #{node}"
77
+ # puts "Value #{node.xpath('./value').text}"
78
+ node.xpath("./property").each do |prop|
79
+ # puts "prop #{prop}"
80
+ # puts "name #{prop['name']}"
81
+ # puts "content #{prop.content}"
82
+ props[prop['name']] = prop.text
83
+ end
84
+ # puts "Props #{props.inspect}"
85
+ result[node.xpath("./value").text] = props
86
+ end
87
+ end
88
+ raise RuntimeError if result.empty?
89
+ result
90
+ end
91
+
92
+ def valuelist
93
+ @valuelist ||= get "/valuelist"
94
+ raise RuntimeError if @valuelist.nil?
95
+ @valuelist
96
+ end
97
+
98
+ end
99
+ end
@@ -0,0 +1,18 @@
1
+ require 'dm-core'
2
+ require 'nokogiri'
3
+
4
+ module DataMapper
5
+ class Property
6
+ class Xml < Object
7
+ primitive String
8
+ def dump(value)
9
+ value.to_xml
10
+ end
11
+ def load(value)
12
+ Nokogiri::XML value
13
+ end
14
+ end
15
+
16
+ XML = Xml
17
+ end
18
+ end
@@ -0,0 +1,12 @@
1
+ = Sample scripts
2
+
3
+ keeper.novell.com also provides structure information about the
4
+ entities stored in its database.
5
+
6
+ == generate_class.rb
7
+ generate_class.rb is a sample Ruby script showing how to use this
8
+ information to generate Ruby classes for use with DataMapper.
9
+
10
+ == show-containers.rb
11
+ show-containers.rb displays information about the containers (aka
12
+ database tables) provides by keeper.novell.com
@@ -0,0 +1,10 @@
1
+ #!/bin/env ruby
2
+ #
3
+ # Generate/update class definition for 'Feature'
4
+
5
+ require 'lib/dm-keeper-adapter'
6
+
7
+ adapter = DataMapper.setup(:default, :adapter => 'keeper',
8
+ :url => 'https://keeper.novell.com/sxkeeper')
9
+
10
+ adapter.update_featureclass "feature.rb"
@@ -0,0 +1,52 @@
1
+ #
2
+ # generate or update the class definition for 'Feature'
3
+ #
4
+ def update_featureclass path
5
+ partsrevision = 0
6
+ xmlfieldsrevision = 0
7
+ if File.exists? path
8
+ require path
9
+ partsrevision = Feature.partsrevision
10
+ xmlfieldsrevision = Feature.xmlfieldsrevision
11
+ end
12
+ current_partsrevision = revision_of('list_view_parts').to_i
13
+ current_xmlfieldsrevision = revision_of('list_xmlfields').to_i
14
+ if partsrevision < current_partsrevision || xmlfieldsrevision < current_xmlfieldsrevision
15
+ # class needs regeneration
16
+ File.open path, "w+" do |f|
17
+ f.puts("# File generated by dm-keeper-adapter on #{Time.new.asctime}")
18
+ f.puts("require 'rubygems'")
19
+ f.puts("require 'dm-core'")
20
+ f.puts("class Feature")
21
+ f.puts(" def Feature.partsrevision")
22
+ f.puts(" #{current_partsrevision}")
23
+ f.puts(" end")
24
+ f.puts(" def Feature.xmlfieldsrevision")
25
+ f.puts(" #{current_xmlfieldsrevision}")
26
+ f.puts(" end")
27
+ f.puts(" include DataMapper::Resource\n")
28
+ f.puts(" property :id, Integer, :key => true")
29
+ properties = []
30
+ viewparts.each_key do |k|
31
+ n = k.tr(" ","_").downcase
32
+ properties << n
33
+ f.puts(" property :#{n}, String")
34
+ end
35
+ xmlfields.each_key do |k|
36
+ n = k.tr(" ","_").downcase
37
+ next if n == '.'
38
+ next if properties.include? n
39
+ f.puts(" property :#{n}, String")
40
+ end
41
+ f.puts("end")
42
+ end
43
+ else
44
+ puts "#{path} is up to date"
45
+ end
46
+ end
47
+
48
+ def revision_of id
49
+ node = valuelist.xpath("//valuelist[@id='#{id}']").first
50
+ node.attribute_with_ns('revision', 'http://inttools.suse.de/sxkeeper/schema/keeper').value
51
+ end
52
+
@@ -0,0 +1,22 @@
1
+ #
2
+ # show-containers.rb
3
+ #
4
+ # Demo script to extract available 'containers' (aka database tables)
5
+ # from FATE (keeper.suse.de) using DataMapper
6
+ #
7
+
8
+ $: << File.join(File.dirname(__FILE__), "..", "dm-keeper-adapter", "lib")
9
+
10
+ require 'rubygems'
11
+ require 'dm-core'
12
+ require 'keeper/container'
13
+
14
+ DataMapper::Logger.new($stdout, :debug)
15
+ keeper = DataMapper.setup(:default, :adapter => 'keeper',
16
+ :url => 'https://keeper.novell.com/sxkeeper')
17
+
18
+ c = Keeper::Container.new keeper
19
+ c.each do |e|
20
+ puts e
21
+ end
22
+
data/test/helper.rb ADDED
@@ -0,0 +1,3 @@
1
+ $: << File.join(File.dirname(__FILE__), "..", "lib")
2
+ require 'test/unit'
3
+ require 'dm-keeper-adapter'
@@ -0,0 +1,19 @@
1
+ require File.join(File.dirname(__FILE__), 'helper')
2
+
3
+ class Get_bug_test < Test::Unit::TestCase
4
+
5
+ def test_get_bug
6
+ DataMapper::Logger.new(STDOUT, :debug)
7
+ keeper = DataMapper.setup(:default,
8
+ :adapter => 'keeper',
9
+ :url => 'https://keeper.novell.com/sxkeeper')
10
+
11
+ require 'keeper/feature'
12
+ DataMapper.finalize
13
+
14
+ feature = Feature.get(312814)
15
+ assert feature
16
+ puts "Feature #{feature.inspect}"
17
+ end
18
+
19
+ end
@@ -0,0 +1,19 @@
1
+ require File.join(File.dirname(__FILE__), 'helper')
2
+
3
+ class Get_relationtree_test < Test::Unit::TestCase
4
+
5
+ def test_get_relationtree
6
+ DataMapper::Logger.new(STDOUT, :debug)
7
+ keeper = DataMapper.setup(:default,
8
+ :adapter => 'keeper',
9
+ :url => 'https://keeper.novell.com/sxkeeper')
10
+
11
+ require 'keeper/relationtree'
12
+ DataMapper.finalize
13
+
14
+ tree = Relationtree.get(1137)
15
+ assert tree
16
+ puts "Relationtree #{tree.inspect}"
17
+ end
18
+
19
+ end
metadata ADDED
@@ -0,0 +1,125 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dm-keeper-adapter
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - "Klaus K\xC3\xA4mpf"
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-09-27 00:00:00 +02:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: inifile
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ hash: 13
30
+ segments:
31
+ - 0
32
+ - 4
33
+ - 1
34
+ version: 0.4.1
35
+ type: :runtime
36
+ version_requirements: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ name: nokogiri
39
+ prerelease: false
40
+ requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ hash: 7
46
+ segments:
47
+ - 1
48
+ - 4
49
+ version: "1.4"
50
+ type: :runtime
51
+ version_requirements: *id002
52
+ description: Use it in Ruby applications to access FATE
53
+ email:
54
+ - kkaempf@suse.de
55
+ executables: []
56
+
57
+ extensions: []
58
+
59
+ extra_rdoc_files: []
60
+
61
+ files:
62
+ - .gitignore
63
+ - README.rdoc
64
+ - Rakefile
65
+ - dm-keeper-adapter.gemspec
66
+ - lib/dm-keeper-adapter.rb
67
+ - lib/dm-keeper-adapter/create.rb
68
+ - lib/dm-keeper-adapter/delete.rb
69
+ - lib/dm-keeper-adapter/misc.rb
70
+ - lib/dm-keeper-adapter/read.rb
71
+ - lib/dm-keeper-adapter/update.rb
72
+ - lib/dm-keeper-adapter/version.rb
73
+ - lib/keeper/container.rb
74
+ - lib/keeper/feature.rb
75
+ - lib/keeper/product.rb
76
+ - lib/keeper/productline.rb
77
+ - lib/keeper/relation.rb
78
+ - lib/keeper/relationtree.rb
79
+ - lib/keeper/valuelist.rb
80
+ - lib/property/xml.rb
81
+ - script/README.rdoc
82
+ - script/generate_class.rb
83
+ - script/genfeatureclass.rb
84
+ - script/show-containers.rb
85
+ - test/helper.rb
86
+ - test/test_get_feature.rb
87
+ - test/test_get_relationtree.rb
88
+ has_rdoc: true
89
+ homepage: ""
90
+ licenses: []
91
+
92
+ post_install_message:
93
+ rdoc_options: []
94
+
95
+ require_paths:
96
+ - lib
97
+ required_ruby_version: !ruby/object:Gem::Requirement
98
+ none: false
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ hash: 3
103
+ segments:
104
+ - 0
105
+ version: "0"
106
+ required_rubygems_version: !ruby/object:Gem::Requirement
107
+ none: false
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ hash: 3
112
+ segments:
113
+ - 0
114
+ version: "0"
115
+ requirements: []
116
+
117
+ rubyforge_project: dm-keeper-adapter
118
+ rubygems_version: 1.5.2
119
+ signing_key:
120
+ specification_version: 3
121
+ summary: A datamapper adapter for FATE (aka keeper.novell.com)
122
+ test_files:
123
+ - test/helper.rb
124
+ - test/test_get_feature.rb
125
+ - test/test_get_relationtree.rb