dm-keeper-adapter 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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