feta 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.
@@ -0,0 +1,8 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ .yardoc
6
+ *.rbc
7
+ doc
8
+
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in feta.gemspec
4
+ gemspec
@@ -0,0 +1,80 @@
1
+
2
+ = Feta
3
+
4
+ * http://github.com/dmacvicar/feta
5
+
6
+ == Introduction
7
+
8
+ Library to access FATE
9
+
10
+ Note: The API is still a draft. Be careful.
11
+
12
+ == Features
13
+
14
+ * Simple queries by actor, roles.
15
+ * For SUSE's FATE server, you can use the web url and it will use the
16
+ right keeper server and take your .oscrc credentials.
17
+
18
+ More will be coming, the focus is on providing an easy API. You can always
19
+ get anything else by just doing xpath queries.
20
+
21
+ == Example (API)
22
+
23
+ === Client API
24
+
25
+ client = Feta::Client.new("https://user:password@keeper.host.com")
26
+ query = Feta::Query.new.only_actor("me@mail.com").only_product("openSUSE-11.4")
27
+ features = client.search(query)
28
+
29
+ === ActiveRecord-like API
30
+
31
+ Feta.client = Feta::Client.new("https://user:password@keeper.host.com")
32
+ Feta::Feature.query.only_actor("me@mail.com").only_product("openSUSE-11.4").each do |feature|
33
+ # .. do something with feature
34
+ end
35
+
36
+ == Extending Feta
37
+
38
+ Feta will look in the following places for plugins:
39
+
40
+ * lib/feta/plugins in the gem path
41
+ * $HOME/.local/share/feta/plugins
42
+
43
+ A plugin is a class under Feta::Plugins, only one hook is available for now:
44
+
45
+ class Feta::Plugins::MyPlugin
46
+
47
+ def to_s
48
+ self.class.to_s
49
+ end
50
+
51
+ def order
52
+ # load always last
53
+ (1.0/0)
54
+ end
55
+
56
+ def initialize_hook(url, logger, headers)
57
+ # modify the url, add headers or configure the logger
58
+ end
59
+ end
60
+
61
+ == SUSE plugin
62
+
63
+ One plugin is included which takes the credentials from .oscrc if using the
64
+ SUSE internal keeper server.
65
+
66
+ === Roadmap
67
+
68
+ * Change query methods to make them more similar to the Bicho library
69
+ * Add more data to the Feature class
70
+ * Allow to search by an arbitrary xpath expression
71
+
72
+ == Authors
73
+
74
+ * Duncan Mac-Vicar P. <dmacvicar@suse.de>
75
+
76
+ == License
77
+
78
+ Copyright (c) 2011 SUSE LINUX Products GmbH.
79
+
80
+ Bicho is licensed under the MIT license. See MIT-LICENSE for details.
@@ -0,0 +1,33 @@
1
+ $:.push(File.join(File.dirname(__FILE__), 'lib'))
2
+ require 'bundler'
3
+ Bundler::GemHelper.install_tasks
4
+ require 'feta/version'
5
+ require 'rake/testtask'
6
+
7
+ extra_docs = ['README*', 'TODO*', 'CHANGELOG*']
8
+
9
+ task :default => [:test]
10
+
11
+ Rake::TestTask.new do |t|
12
+ t.libs << File.expand_path('../test', __FILE__)
13
+ t.libs << File.expand_path('../', __FILE__)
14
+ t.test_files = FileList['test/test*.rb']
15
+ t.verbose = true
16
+ end
17
+
18
+ begin
19
+ require 'yard'
20
+ YARD::Rake::YardocTask.new(:doc) do |t|
21
+ t.files = ['lib/**/*.rb', *extra_docs]
22
+ t.options = ['--no-private']
23
+ end
24
+ rescue LoadError
25
+ STDERR.puts "Install yard if you want prettier docs"
26
+ require 'rdoc/task'
27
+ Rake::RDocTask.new(:doc) do |rdoc|
28
+ rdoc.rdoc_dir = "doc"
29
+ rdoc.title = "bicho #{Feta::VERSION}"
30
+ extra_docs.each { |ex| rdoc.rdoc_files.include ex }
31
+ end
32
+ end
33
+
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "feta/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "feta"
7
+ s.version = Feta::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Duncan Mac-Vicar P."]
10
+ s.email = ["dmacvicar@suse.de"]
11
+ s.homepage = "http://github.com/dmacvicar/feta"
12
+ s.summary = %q{Library to access FATE features}
13
+ s.description = %q{Feta is a library to access FATE using the keeper API}
14
+
15
+ s.add_dependency("rest-client", ["~> 1.6"])
16
+ s.add_dependency("nokogiri", ["~> 1.5"])
17
+ s.add_dependency("inifile", ["~> 0.4.1"])
18
+
19
+ s.files = `git ls-files`.split("\n")
20
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
21
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
22
+ s.require_paths = ["lib"]
23
+ end
@@ -0,0 +1,33 @@
1
+ #--
2
+ # Copyright (c) 2011 SUSE LINUX Products GmbH
3
+ #
4
+ # Author: Duncan Mac-Vicar P. <dmacvicar@suse.de>
5
+ #
6
+ # Permission is hereby granted, free of charge, to any person obtaining
7
+ # a copy of this software and associated documentation files (the
8
+ # "Software"), to deal in the Software without restriction, including
9
+ # without limitation the rights to use, copy, modify, merge, publish,
10
+ # distribute, sublicense, and/or sell copies of the Software, and to
11
+ # permit persons to whom the Software is furnished to do so, subject to
12
+ # the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be
15
+ # included in all copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
+ #++
25
+ require 'rubygems'
26
+ require 'feta/version'
27
+ require 'feta/logging'
28
+ require 'feta/client'
29
+ require 'feta/feature'
30
+
31
+ module Feta
32
+ # Your code goes here...
33
+ end
@@ -0,0 +1,119 @@
1
+ #--
2
+ # Copyright (c) 2011 SUSE LINUX Products GmbH
3
+ #
4
+ # Author: Duncan Mac-Vicar P. <dmacvicar@suse.de>
5
+ #
6
+ # Permission is hereby granted, free of charge, to any person obtaining
7
+ # a copy of this software and associated documentation files (the
8
+ # "Software"), to deal in the Software without restriction, including
9
+ # without limitation the rights to use, copy, modify, merge, publish,
10
+ # distribute, sublicense, and/or sell copies of the Software, and to
11
+ # permit persons to whom the Software is furnished to do so, subject to
12
+ # the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be
15
+ # included in all copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
+ #++
25
+ require 'rest_client'
26
+ require 'nokogiri'
27
+ require 'feta/logging'
28
+
29
+ module Feta
30
+
31
+ module Plugins
32
+ end
33
+
34
+ class Client
35
+
36
+ include ::Feta::Logging
37
+
38
+ KEEPER_XML_NS = "http://inttools.suse.de/sxkeeper/schema/keeper"
39
+
40
+ attr_accessor :url
41
+
42
+ # Construct a FATE client
43
+ #
44
+ # @param url [String, URI] URL of the FATE server
45
+ #
46
+ # @note Keeper behind iChain is not yet supported
47
+ #
48
+ def initialize(url)
49
+ RestClient.log = Feta::Logging.logger
50
+ url = URI.parse(url) if not url.is_a?(URI)
51
+ @url = url.clone
52
+ @headers = Hash.new
53
+
54
+ # Scan plugins
55
+ [ File.join(ENV['HOME'], '.local/share/feta/plugins/*.rb'),
56
+ File.join(File.dirname(__FILE__), 'plugins', '*.rb') ].each do |plugin_glob|
57
+ Dir.glob(plugin_glob).each do |plugin|
58
+ logger.debug("Loading file: #{plugin}")
59
+ load plugin
60
+ end
61
+ end
62
+
63
+ #instantiate plugins
64
+ pl_instances = []
65
+ ::Feta::Plugins.constants.each do |cnt|
66
+ pl_class = ::Feta::Plugins.const_get(cnt)
67
+ pl_instances << pl_class.new
68
+ end
69
+
70
+ pl_instances.sort{|x,y| x.order <=> y.order}.each do |pl_instance|
71
+ logger.debug("Loaded: #{pl_instance}")
72
+ pl_instance.initialize_hook(url, logger, @headers)
73
+ end
74
+
75
+ @keeper_url = url
76
+ end
77
+
78
+ # Search for features
79
+ #
80
+ # @param query [Query] Query specifying the search criteria
81
+ # @return [Array<Feature>] List of features
82
+ def search(query)
83
+ url_query = "?query=#{CGI.escape(query.to_xquery)}"
84
+ url = URI.parse("#{@keeper_url}/feature#{url_query}").to_s
85
+
86
+ xml = RestClient.get(url, @headers).body
87
+ features = []
88
+ doc = Nokogiri::XML(xml)
89
+ doc.xpath('//feature', 'k' => KEEPER_XML_NS).each do |feat_element|
90
+ feature = Feature.new(self)
91
+ feature.feature_id = feat_element.xpath('./@k:id', 'k' => KEEPER_XML_NS).first.value
92
+ feature.title = feat_element.xpath('./title', 'k' => KEEPER_XML_NS).first.content
93
+ feat_element.xpath('./actor', 'k' => KEEPER_XML_NS).each do |actor|
94
+ if actor.xpath('./role', 'k' => KEEPER_XML_NS).first.content == "infoprovider"
95
+ feature.infoprovider = actor.xpath('.//email', 'k' => KEEPER_XML_NS).first.content
96
+ end
97
+ end
98
+ feat_element.xpath('./productcontext', 'k' => KEEPER_XML_NS).each do |ctx_element|
99
+ ctx = Feature::ProductContext.new
100
+ product = ctx_element.xpath('./product/name').first.content
101
+ ctx.status = ctx_element.xpath('./status').children.select {|x| x.element?}.first.name.to_sym
102
+ feature.product_contexts[product] = ctx
103
+
104
+ # Priorities
105
+ ctx_element.xpath('./priority').each do |prio_element|
106
+ prio = prio_element.children.select(&:element?).first.name.to_sym
107
+ owner = prio_element.xpath('./owner/role').first.content.to_sym
108
+ ctx.priorities[owner] = prio
109
+ end
110
+ end
111
+ yield feature if block_given?
112
+ features << feature
113
+ end
114
+ features
115
+ end
116
+
117
+ end
118
+
119
+ end
@@ -0,0 +1,56 @@
1
+ #--
2
+ # Copyright (c) 2011 SUSE LINUX Products GmbH
3
+ #
4
+ # Author: Duncan Mac-Vicar P. <dmacvicar@suse.de>
5
+ #
6
+ # Permission is hereby granted, free of charge, to any person obtaining
7
+ # a copy of this software and associated documentation files (the
8
+ # "Software"), to deal in the Software without restriction, including
9
+ # without limitation the rights to use, copy, modify, merge, publish,
10
+ # distribute, sublicense, and/or sell copies of the Software, and to
11
+ # permit persons to whom the Software is furnished to do so, subject to
12
+ # the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be
15
+ # included in all copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
+ #++
25
+
26
+ module Feta
27
+ # This module allows the [Feature] and other
28
+ # classes to offer an ActiveRecord like query
29
+ # API by allowing to set a default [Client]
30
+ # to make operations.
31
+ module CommonClient
32
+ def self.common_client=(client)
33
+ @common_client = client
34
+ end
35
+
36
+ def self.common_client
37
+ @common_client || (raise "No common client set")
38
+ end
39
+
40
+ def common_client
41
+ CommonClient.common_client
42
+ end
43
+ end
44
+
45
+ # Sets the common client to be used by
46
+ # the library
47
+ def self.client=(client)
48
+ CommonClient.common_client = client
49
+ end
50
+
51
+ # Current client used by the library
52
+ def self.client
53
+ CommonClient.common_client
54
+ end
55
+
56
+ end
@@ -0,0 +1,101 @@
1
+ #!/usr/bin/env ruby
2
+ require 'cgi'
3
+ require 'rubygems'
4
+ require "nokogiri"
5
+ require 'open-uri'
6
+ require 'uri'
7
+ require 'feta/query'
8
+
9
+ # ?query=/feature[actor[person/email='dmacvicar@novell.com' and role='infoprovider']]
10
+
11
+ module Feta
12
+ #
13
+ #
14
+ # Small DSL into fate features
15
+ #
16
+ # Some examples:
17
+ # Feature.query.only_actor(x).with_role.each do {|feature| ... }
18
+ #
19
+ # Features responds to the following issue methods, shared with
20
+ # bugs:
21
+ # id, title, url
22
+ #
23
+ class Feature
24
+
25
+ attr_accessor :feature_id, :title, :products, :developers, :infoprovider
26
+
27
+ # A feature different statuses and other properties with regard
28
+ # to different products
29
+ #
30
+ # @return [Hash] key is the product, value is a {ProductContext}
31
+ attr_accessor :product_contexts
32
+
33
+ class ProductContext
34
+
35
+ def initialize
36
+ @priorities = Hash.new
37
+ end
38
+
39
+ # Status of the feature for a specific product
40
+ # (+:evaluation+, +:implementation+, etc)
41
+ attr_accessor :status
42
+
43
+ # If status is +:evaluation+ then the status has a owner
44
+ # (+:teamleader+, +:projectmanager+, etc)
45
+ attr_accessor :status_owner
46
+
47
+ # For an specific product, a feature has
48
+ # @return [Hash] key is the owner, value is the priority
49
+ attr_accessor :priorities
50
+ end
51
+
52
+ def id
53
+ feature_id
54
+ end
55
+
56
+ def initialize(client)
57
+ @client = client
58
+ @product_contexts = Hash.new
59
+ end
60
+
61
+ def self.find(what=nil)
62
+ return Query.new.with_id(what).each.to_a.first if what
63
+ Query.new
64
+ end
65
+
66
+ def self.query
67
+ Query.new
68
+ end
69
+
70
+ def feature_url
71
+ "#{@client.url}/#{id}"
72
+ end
73
+
74
+ # @param product [String] Product name
75
+ # @return [Symbol] status for +product+ or nil
76
+ # if the product is not in the feature
77
+ def status_for_product(product)
78
+ product_contexts[product.to_s].status
79
+ end
80
+
81
+ # @return [Symbol] the priority of this feature
82
+ # given by +role+ for +product+
83
+ def priority_for_product_and_role(product, role)
84
+ product_contexts[product.to_s].priorities[role.to_sym]
85
+ end
86
+
87
+ # @return [Fixnum] numeric priority for this feature
88
+ # use it for sorting
89
+ def numeric_priority_for_product_and_role(product, role)
90
+ case priority_for_product_and_role(product, role)
91
+ when :neutral then 0
92
+ when :desirable then 1
93
+ when :important then 2
94
+ when :mandatory then 3
95
+ else -1
96
+ end
97
+ end
98
+
99
+ end
100
+
101
+ end # module feta
@@ -0,0 +1,43 @@
1
+ #--
2
+ # Copyright (c) 2011 SUSE LINUX Products GmbH
3
+ #
4
+ # Author: Duncan Mac-Vicar P. <dmacvicar@suse.de>
5
+ #
6
+ # Permission is hereby granted, free of charge, to any person obtaining
7
+ # a copy of this software and associated documentation files (the
8
+ # "Software"), to deal in the Software without restriction, including
9
+ # without limitation the rights to use, copy, modify, merge, publish,
10
+ # distribute, sublicense, and/or sell copies of the Software, and to
11
+ # permit persons to whom the Software is furnished to do so, subject to
12
+ # the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be
15
+ # included in all copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
+ #++
25
+ require 'logger'
26
+
27
+ module Feta
28
+ module Logging
29
+
30
+ def self.logger=(logger)
31
+ @logger = logger
32
+ end
33
+
34
+ def self.logger
35
+ @logger ||= Logger.new('/dev/null')
36
+ end
37
+
38
+ def logger
39
+ Logging.logger
40
+ end
41
+
42
+ end
43
+ end
@@ -0,0 +1,71 @@
1
+ #--
2
+ # Copyright (c) 2011 SUSE LINUX Products GmbH
3
+ #
4
+ # Author: Duncan Mac-Vicar P. <dmacvicar@suse.de>
5
+ #
6
+ # Permission is hereby granted, free of charge, to any person obtaining
7
+ # a copy of this software and associated documentation files (the
8
+ # "Software"), to deal in the Software without restriction, including
9
+ # without limitation the rights to use, copy, modify, merge, publish,
10
+ # distribute, sublicense, and/or sell copies of the Software, and to
11
+ # permit persons to whom the Software is furnished to do so, subject to
12
+ # the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be
15
+ # included in all copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
+ #++
25
+ require 'inifile'
26
+ require 'uri'
27
+
28
+ module Feta
29
+ module Plugins
30
+
31
+ # Rewrites the fate.novell.com url to the
32
+ # right keeper server and inserts
33
+ # the osc credentials from .oscrc
34
+ #
35
+ class SUSE
36
+
37
+ OSCRC_CREDENTIALS = "https://api.opensuse.org"
38
+
39
+ def to_s
40
+ self.class.to_s
41
+ end
42
+
43
+ def self.oscrc_credentials
44
+ oscrc = IniFile.new(File.join(ENV['HOME'], '.oscrc'))
45
+ if oscrc.has_section?(OSCRC_CREDENTIALS)
46
+ user = oscrc[OSCRC_CREDENTIALS]['user']
47
+ pass = oscrc[OSCRC_CREDENTIALS]['pass']
48
+ if user && pass
49
+ return {:user => user, :password => pass}
50
+ else
51
+ raise "No .oscrc credentials for bnc"
52
+ end
53
+ end
54
+ end
55
+
56
+ def order
57
+ 1
58
+ end
59
+
60
+ def initialize_hook(url, logger, headers)
61
+ return if not url.host.include?('fate.novell.com')
62
+ auth = SUSE.oscrc_credentials
63
+ url.host = url.host.gsub(/fate\.novell.com/, 'keeper.novell.com')
64
+ url.user = auth[:user]
65
+ url.password = auth[:password]
66
+ url.path = '/sxkeeper' if url.path.empty?
67
+ logger.debug("#{self} : Rewrote url to '#{url.to_s.gsub(/#{url.user}:#{url.password}/, "USER:PASS")}'")
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,100 @@
1
+ #--
2
+ # Copyright (c) 2011 SUSE LINUX Products GmbH
3
+ #
4
+ # Author: Duncan Mac-Vicar P. <dmacvicar@suse.de>
5
+ #
6
+ # Permission is hereby granted, free of charge, to any person obtaining
7
+ # a copy of this software and associated documentation files (the
8
+ # "Software"), to deal in the Software without restriction, including
9
+ # without limitation the rights to use, copy, modify, merge, publish,
10
+ # distribute, sublicense, and/or sell copies of the Software, and to
11
+ # permit persons to whom the Software is furnished to do so, subject to
12
+ # the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be
15
+ # included in all copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
+ #++
25
+ require 'feta/common_client'
26
+
27
+ module Feta
28
+
29
+ # A query to the keeper server
30
+ class Query
31
+
32
+ include Enumerable
33
+
34
+ def initialize
35
+ @products = []
36
+ @actor = nil
37
+ @role = nil
38
+ @feature_id = nil
39
+ end
40
+
41
+ # Queries only features where this actor is present
42
+ # @param actor [String] Actor email
43
+ def only_actor(actor)
44
+ @actor = actor
45
+ self
46
+ end
47
+
48
+ # Restrict the actor's role to +role+
49
+ # @param role [String] Actor's role
50
+ def with_role(role)
51
+ @role = role
52
+ self
53
+ end
54
+
55
+ # Queries only features for these product
56
+ # @param product [String] Product name
57
+ def only_product(product)
58
+ only_products([product])
59
+ end
60
+
61
+ # Queries only features for these products
62
+ # @param products [Array<String>] Product name list
63
+ def only_products(products)
64
+ @products = []
65
+ @products = products.flatten
66
+ self
67
+ end
68
+
69
+ def with_id(id)
70
+ @feature_id = id
71
+ self
72
+ end
73
+
74
+ # Converts the query to an xpath expression
75
+ # @return [String] xpath expression representing the query
76
+ # @private
77
+ def to_xquery
78
+ id_query = @feature_id ? "@k:id=#{@feature_id}" : ""
79
+ role_query = @role ? " and role='#{@role}'" : ""
80
+ actor_query = @actor ? "actor[person/email='#{@actor}'#{role_query}]" : ""
81
+ prods = @products.collect {|p| "product/name='#{p}'"}.join(" or ")
82
+ product_query = (!@products.empty?) ? "productcontext[#{prods}]" : ""
83
+ conditions = [id_query, actor_query, product_query].reject{ |x| x.empty? }.join(' and ')
84
+ query = "/feature[#{conditions}]"
85
+ end
86
+
87
+ # Iterates through the result of the current query.
88
+ #
89
+ # @note Requires Feta.client to be set
90
+ #
91
+ # @yield [Feta::Feature]
92
+ def each
93
+ ret = Feta.client.search(self)
94
+ return ret.each if not block_given?
95
+ ret.each { |feature| yield feature }
96
+ end
97
+
98
+ end
99
+
100
+ end
@@ -0,0 +1,3 @@
1
+ module Feta
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,8 @@
1
+ $: << File.join(File.dirname(__FILE__), "..", "lib")
2
+ require 'test/unit'
3
+ require 'feta'
4
+
5
+ if ENV["DEBUG"]
6
+ Feta::Logging.logger = Logger.new(STDERR)
7
+ Feta::Logging.logger.level = Logger::DEBUG
8
+ end
@@ -0,0 +1,32 @@
1
+ require File.join(File.dirname(__FILE__), 'helper')
2
+
3
+ class Query_test < Test::Unit::TestCase
4
+
5
+ include ::Feta::Logging
6
+
7
+ def test_active_record_style
8
+
9
+ # No client set yet
10
+ assert_raise RuntimeError do
11
+ Feta::Feature.query.only_actor("dmacvicar@novell.com").with_role("teamlead").each do |feature|
12
+ puts feature
13
+ end
14
+ end
15
+
16
+ Feta.client = Feta::Client.new("https://fate.novell.com")
17
+ query = Feta::Feature.query.only_actor("dmacvicar@novell.com").only_product("openSUSE-11.4")
18
+
19
+ ret = query.each.to_a
20
+
21
+ assert ret.collect(&:id).include?("303793")
22
+
23
+ feature = ret.collect.select {|x| x.id == "303793"}.first
24
+
25
+ assert_equal :rejected, feature.status_for_product("openSUSE-11.2")
26
+ assert_equal :important, feature.priority_for_product_and_role("openSUSE-11.2", :productmanager)
27
+ assert_nil feature.priority_for_product_and_role("openSUSE-11.2", :nobody)
28
+
29
+ assert_equal :done, feature.status_for_product("openSUSE-11.4")
30
+ end
31
+
32
+ end
metadata ADDED
@@ -0,0 +1,126 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: feta
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
+ - Duncan Mac-Vicar P.
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-08-10 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: rest-client
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ~>
27
+ - !ruby/object:Gem::Version
28
+ hash: 3
29
+ segments:
30
+ - 1
31
+ - 6
32
+ version: "1.6"
33
+ type: :runtime
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: nokogiri
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ hash: 5
44
+ segments:
45
+ - 1
46
+ - 5
47
+ version: "1.5"
48
+ type: :runtime
49
+ version_requirements: *id002
50
+ - !ruby/object:Gem::Dependency
51
+ name: inifile
52
+ prerelease: false
53
+ requirement: &id003 !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ~>
57
+ - !ruby/object:Gem::Version
58
+ hash: 13
59
+ segments:
60
+ - 0
61
+ - 4
62
+ - 1
63
+ version: 0.4.1
64
+ type: :runtime
65
+ version_requirements: *id003
66
+ description: Feta is a library to access FATE using the keeper API
67
+ email:
68
+ - dmacvicar@suse.de
69
+ executables: []
70
+
71
+ extensions: []
72
+
73
+ extra_rdoc_files: []
74
+
75
+ files:
76
+ - .gitignore
77
+ - Gemfile
78
+ - README.rdoc
79
+ - Rakefile
80
+ - feta.gemspec
81
+ - lib/feta.rb
82
+ - lib/feta/client.rb
83
+ - lib/feta/common_client.rb
84
+ - lib/feta/feature.rb
85
+ - lib/feta/logging.rb
86
+ - lib/feta/plugins/suse.rb
87
+ - lib/feta/query.rb
88
+ - lib/feta/version.rb
89
+ - test/helper.rb
90
+ - test/test_query.rb
91
+ homepage: http://github.com/dmacvicar/feta
92
+ licenses: []
93
+
94
+ post_install_message:
95
+ rdoc_options: []
96
+
97
+ require_paths:
98
+ - lib
99
+ required_ruby_version: !ruby/object:Gem::Requirement
100
+ none: false
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ hash: 3
105
+ segments:
106
+ - 0
107
+ version: "0"
108
+ required_rubygems_version: !ruby/object:Gem::Requirement
109
+ none: false
110
+ requirements:
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ hash: 3
114
+ segments:
115
+ - 0
116
+ version: "0"
117
+ requirements: []
118
+
119
+ rubyforge_project:
120
+ rubygems_version: 1.8.6
121
+ signing_key:
122
+ specification_version: 3
123
+ summary: Library to access FATE features
124
+ test_files:
125
+ - test/helper.rb
126
+ - test/test_query.rb