arusarka-mingle4r 0.2.5 → 0.2.7
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE +21 -0
- data/Rakefile +23 -0
- data/TODO.txt +4 -0
- data/lib/mingle4r.rb +23 -0
- data/lib/mingle4r/card.rb +139 -0
- data/lib/mingle4r/card/attachment.rb +47 -0
- data/lib/mingle4r/common_class_methods.rb +219 -0
- data/lib/mingle4r/common_dyn_class_instance_methods.rb +40 -0
- data/lib/mingle4r/helpers.rb +33 -0
- data/lib/mingle4r/mingle_client.rb +106 -0
- data/lib/mingle4r/project.rb +64 -0
- data/lib/mingle4r/property_definition.rb +18 -0
- data/lib/mingle4r/user.rb +5 -0
- data/lib/mingle4r/version.rb +11 -0
- data/lib/mingle4r/wiki.rb +7 -0
- data/lib/mingle_resource.rb +2 -0
- data/spec/mingle4r/card/attachment_spec.rb +48 -0
- data/spec/mingle4r/card_spec.rb +40 -0
- data/spec/mingle4r/common_class_methods_spec.rb +104 -0
- data/spec/mingle4r/helpers_spec.rb +20 -0
- data/spec/mingle4r/project_spec.rb +20 -0
- data/spec/mingle4r/property_definition_spec.rb +30 -0
- data/spec/mingle4r/version_spec.rb +7 -0
- data/spec/mingle4r/wiki_spec.rb +11 -0
- data/spec/mingle4r_spec.rb +7 -0
- data/spec/spec_helper.rb +3 -0
- data/test/integration/test.rb +39 -0
- data/test/integration/test_adding_comment.rb +9 -0
- data/test/integration/test_card_filter.rb +7 -0
- data/test/integration/test_card_transition.rb +9 -0
- data/test/integration/test_getting_users.rb +10 -0
- data/test/integration/test_uploading_attachment.rb +9 -0
- data/test/integration/test_wiki.rb +7 -0
- metadata +34 -2
- data/History.txt +0 -33
data/MIT-LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2009 Arusarka Haldar
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'rake'
|
5
|
+
require 'spec/rake/spectask'
|
6
|
+
require 'rake/gempackagetask'
|
7
|
+
|
8
|
+
namespace :gem do
|
9
|
+
desc "Builds the gem"
|
10
|
+
task :build do
|
11
|
+
file_name = "#{File.dirname(__FILE__)}/mingle4r.gemspec"
|
12
|
+
spec = Gem::Specification.load(file_name)
|
13
|
+
Gem::Builder.new(spec).build
|
14
|
+
file = FileList["#{File.dirname(__FILE__)}/mingle4r*.gem"]
|
15
|
+
mv file.to_a, "#{File.dirname(__FILE__)}/pkg"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# task : run specs
|
20
|
+
Spec::Rake::SpecTask.new do |t|
|
21
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
22
|
+
t.verbose = true
|
23
|
+
end
|
data/TODO.txt
ADDED
data/lib/mingle4r.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
$:.unshift File.expand_path(File.dirname(__FILE__))
|
2
|
+
|
3
|
+
require 'logger'
|
4
|
+
require 'rubygems'
|
5
|
+
require 'active_resource'
|
6
|
+
require 'net/http'
|
7
|
+
|
8
|
+
# This module should be used to interact with mingle. Look in the readme for examples.
|
9
|
+
module Mingle4r
|
10
|
+
AUTHOR = 'Asur'
|
11
|
+
end
|
12
|
+
|
13
|
+
require 'mingle_resource'
|
14
|
+
require 'mingle4r/version'
|
15
|
+
require 'mingle4r/common_class_methods'
|
16
|
+
require 'mingle4r/common_dyn_class_instance_methods'
|
17
|
+
require 'mingle4r/helpers'
|
18
|
+
require 'mingle4r/user'
|
19
|
+
require 'mingle4r/card'
|
20
|
+
require 'mingle4r/project'
|
21
|
+
require 'mingle4r/property_definition'
|
22
|
+
require 'mingle4r/wiki'
|
23
|
+
require 'mingle4r/mingle_client'
|
@@ -0,0 +1,139 @@
|
|
1
|
+
require 'mingle4r/card/attachment'
|
2
|
+
|
3
|
+
module Mingle4r
|
4
|
+
class Card
|
5
|
+
module ClassMethods
|
6
|
+
def find_without_pagination(*args)
|
7
|
+
scope = args.slice!(0)
|
8
|
+
options = args.slice!(0) || {}
|
9
|
+
options[:params] ||= {}
|
10
|
+
options[:params].merge!({:page => 'all'})
|
11
|
+
|
12
|
+
# call ActiveResource::Base::find with proper options
|
13
|
+
find(scope, options)
|
14
|
+
end
|
15
|
+
|
16
|
+
# applies an mql filter on card types. Look at https://mingle05.thoughtworks.com/help/mql_reference.html
|
17
|
+
# for reference
|
18
|
+
def apply_filter(filter_string)
|
19
|
+
find_without_pagination(:all, :params => {'filters[mql]'.to_sym => filter_string})
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
module InstanceMethods
|
24
|
+
def attachments(refresh = false)
|
25
|
+
return @attachments if(!refresh && @attachments_cached)
|
26
|
+
attachment_site = File.join(self.class.site.to_s, "cards/#{self.number()}").to_s
|
27
|
+
Mingle4r::Card::Attachment.site = attachment_site
|
28
|
+
Mingle4r::Card::Attachment.user = self.class.user
|
29
|
+
Mingle4r::Card::Attachment.password = self.class.password
|
30
|
+
attachment_class = Mingle4r::Card::Attachment.send(:create_resource_class)
|
31
|
+
@attachments = attachment_class.find(:all)
|
32
|
+
@attachments_cached = true
|
33
|
+
@attachments
|
34
|
+
end
|
35
|
+
|
36
|
+
def upload_attachment(file_path)
|
37
|
+
attachment_uri = URI.parse(File.join(self.class.site.to_s, "cards/#{self.number()}/attachments.xml"))
|
38
|
+
|
39
|
+
http = Net::HTTP.new(attachment_uri.host, attachment_uri.port)
|
40
|
+
http.use_ssl = attachment_uri.is_a?(URI::HTTPS)
|
41
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE if http.use_ssl
|
42
|
+
|
43
|
+
basic_encode = 'Basic ' + ["#{self.class.user}:#{self.class.password}"].pack('m').delete("\r\n")
|
44
|
+
|
45
|
+
post_headers = {
|
46
|
+
'Authorization' => basic_encode,
|
47
|
+
'Content-Type' => 'multipart/form-data; boundary=----------XnJLe9ZIbbGUYtzPQJ16u1'
|
48
|
+
}
|
49
|
+
|
50
|
+
file_content = IO.read(file_path)
|
51
|
+
|
52
|
+
post_body = <<EOS
|
53
|
+
------------XnJLe9ZIbbGUYtzPQJ16u1\r
|
54
|
+
Content-Disposition: form-data; name="file"; filename="#{File.basename(file_path)}"\r
|
55
|
+
Content-Type: application/octet-stream\r
|
56
|
+
Content-Length: #{file_content.size}\r
|
57
|
+
\r
|
58
|
+
#{file_content}\r
|
59
|
+
------------XnJLe9ZIbbGUYtzPQJ16u1--\r
|
60
|
+
EOS
|
61
|
+
|
62
|
+
http.post(attachment_uri.path, post_body, post_headers)
|
63
|
+
end
|
64
|
+
|
65
|
+
# returns back the version of the card given. If an invalid version is given, the latest
|
66
|
+
# version is returned, takes a number or :next or :before
|
67
|
+
def at_version(version_no)
|
68
|
+
version_2_find = 0
|
69
|
+
case version_no
|
70
|
+
when :before
|
71
|
+
version_2_find = self.version.to_i - 1
|
72
|
+
when :next
|
73
|
+
version_2_find = self.version.to_i + 1
|
74
|
+
else
|
75
|
+
version_2_find = version_no.to_i
|
76
|
+
end
|
77
|
+
self.class.find(self.number, :params => {:version => version_2_find})
|
78
|
+
end
|
79
|
+
|
80
|
+
# adds a comment to a card.
|
81
|
+
def add_comment(comment)
|
82
|
+
comment_uri = URI.parse(File.join(self.class.site.to_s, "cards/add_comment?card_id=#{self.id}"))
|
83
|
+
|
84
|
+
http = Net::HTTP.new(comment_uri.host, comment_uri.port)
|
85
|
+
http.use_ssl = comment_uri.is_a?(URI::HTTPS)
|
86
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE if http.use_ssl
|
87
|
+
|
88
|
+
basic_encode = 'Basic ' + ["#{self.class.user}:#{self.class.password}"].pack('m').delete("\r\n")
|
89
|
+
|
90
|
+
post_headers = {
|
91
|
+
'Authorization' => basic_encode,
|
92
|
+
'Content-Type' => 'application/x-www-form-urlencoded; charset=UTF-8'
|
93
|
+
}
|
94
|
+
|
95
|
+
post_body = "comment=#{comment}&card_id=#{self.id}"
|
96
|
+
|
97
|
+
http.post(comment_uri.path, post_body, post_headers)
|
98
|
+
end
|
99
|
+
|
100
|
+
# TODO - Check if working with https
|
101
|
+
# Executes the given transition on the card.
|
102
|
+
# Example :
|
103
|
+
# defect_card.execute_transition(:transition_name => 'Close Defect', :Owner => nil, :Status => 'Closed', :transition_comment => comment)
|
104
|
+
# transition_comment is mandatory if the transition is set that way.
|
105
|
+
# after transition 'Owner' would have value 'Not Set' and 'Status' would be 'Closed' for defect card
|
106
|
+
def execute_transition(args_hash)
|
107
|
+
project_id = File.basename(self.class.site.to_s)
|
108
|
+
transition_name = args_hash.delete(:transition_name)
|
109
|
+
raise 'Transition name for given' unless transition_name
|
110
|
+
transition_uri = URI.parse(File.join(self.class.site.to_s, 'transition_executions.xml'))
|
111
|
+
|
112
|
+
# create the form data
|
113
|
+
form_data = {
|
114
|
+
'transition_execution[transition]' => transition_name,
|
115
|
+
'transition_execution[card]' => self.number.to_s
|
116
|
+
}
|
117
|
+
comment = args_hash.delete(:transition_comment)
|
118
|
+
form_data['transition_execution[comment]'] = Helpers.encode2html(comment) if comment
|
119
|
+
args_hash.each do |key, value|
|
120
|
+
form_data['transition_execution[properties][][name]'] = Helpers.encode2html(key.to_s)
|
121
|
+
form_data['transition_execution[properties][][value]'] = Helpers.encode2html(value.to_s)
|
122
|
+
end
|
123
|
+
|
124
|
+
req = Net::HTTP::Post.new(transition_uri.path)
|
125
|
+
req.basic_auth self.class.user, self.class.password
|
126
|
+
req.set_form_data(form_data)
|
127
|
+
|
128
|
+
Net::HTTP.new(transition_uri.host, transition_uri.port).start { |http| http.request(req) }
|
129
|
+
end
|
130
|
+
|
131
|
+
private
|
132
|
+
def boundary
|
133
|
+
'----------XnJLe9ZIbbGUYtzPQJ16u1'
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
extend Mingle4r::CommonClassMethods
|
138
|
+
end
|
139
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Mingle4r
|
2
|
+
class Card
|
3
|
+
class Attachment
|
4
|
+
module InstanceMethods
|
5
|
+
# downloads the attachment. It an additional file path is given it saves it at the
|
6
|
+
# given path. The given path should be writable
|
7
|
+
def download(file_name = nil)
|
8
|
+
collection_uri = self.class.site
|
9
|
+
rel_down_url = self.url
|
10
|
+
base_url = "#{collection_uri.scheme}://#{collection_uri.host}:#{collection_uri.port}/"
|
11
|
+
down_uri = URI.join(base_url, rel_down_url)
|
12
|
+
req = Net::HTTP::Get.new(down_uri.path)
|
13
|
+
req.basic_auth self.class.user, self.class.password
|
14
|
+
begin
|
15
|
+
res = Net::HTTP.start(down_uri.host, down_uri.port) { |http| http.request(req) }
|
16
|
+
file_name ||= self.file_name()
|
17
|
+
File.open(file_name, 'w') { |f| f.print(res.body) }
|
18
|
+
rescue Exception => e
|
19
|
+
e.message
|
20
|
+
end
|
21
|
+
end # download
|
22
|
+
|
23
|
+
# alias for file_name
|
24
|
+
def name
|
25
|
+
file_name()
|
26
|
+
end
|
27
|
+
|
28
|
+
# so that active resource tries to find by proper id
|
29
|
+
def id
|
30
|
+
name()
|
31
|
+
end
|
32
|
+
|
33
|
+
# This method had to be overriden.
|
34
|
+
# normal active resource destroy doesn't work as mingle site for deleting attachments doesn't end with .xml.
|
35
|
+
def destroy
|
36
|
+
connection = self.send(:connection)
|
37
|
+
# deletes the attachment by removing .xml at the end
|
38
|
+
connection.delete(self.send(:element_path).gsub(/\.xml\z/, ''))
|
39
|
+
end
|
40
|
+
alias_method :delete, :destroy
|
41
|
+
end #module InstanceMethods
|
42
|
+
|
43
|
+
extend Mingle4r::CommonClassMethods
|
44
|
+
|
45
|
+
end # class Attachment
|
46
|
+
end # class Card
|
47
|
+
end # module Mingle4r
|
@@ -0,0 +1,219 @@
|
|
1
|
+
module Mingle4r
|
2
|
+
# ActiveResource makes connecting to rest resources very easy. However it has one problem
|
3
|
+
# and a big one at that. If you try setting the authentication credentials or the site or
|
4
|
+
# collection name, element name for the class for the second time it doesn't work. E.g.
|
5
|
+
#
|
6
|
+
# class Person < ActiveResource::base
|
7
|
+
# self.site = 'http://localhost:9090/'
|
8
|
+
# end
|
9
|
+
#
|
10
|
+
# After sometime you change it to
|
11
|
+
#
|
12
|
+
# Person.site = 'https://org-server/my_proj/'
|
13
|
+
# Person.user = 'admin'
|
14
|
+
# Person.password = 'secret'
|
15
|
+
#
|
16
|
+
# Then you do
|
17
|
+
#
|
18
|
+
# Person.find(:all) => It bombs
|
19
|
+
#
|
20
|
+
# This module provides a mechanism by which you can get rid of this problem. Extend this
|
21
|
+
# class in the actual class itself. Do not extend the extended class from ActiveResource::Base.
|
22
|
+
#
|
23
|
+
# E.g.
|
24
|
+
#
|
25
|
+
# class Person
|
26
|
+
# extend CommonClassMethods
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# set the credentials
|
30
|
+
#
|
31
|
+
# Person.site = 'http://localhost:8080'
|
32
|
+
# Person.user = 'foo'
|
33
|
+
# Person.password = 'bar'
|
34
|
+
#
|
35
|
+
# Thats it. Now create some objects
|
36
|
+
#
|
37
|
+
# asur = Person.new(:name => 'Asur', :job => 'fooling around', :status => 'Single and ready 2 mingle')
|
38
|
+
# asur.save
|
39
|
+
#
|
40
|
+
# Now change the class attributes
|
41
|
+
#
|
42
|
+
# Person.site = 'https://org-server/mingle'
|
43
|
+
# Person.collection_name = 'boring_people'
|
44
|
+
#
|
45
|
+
# Now instantiate an object
|
46
|
+
#
|
47
|
+
# rakhshas = Person.new(:name => 'Rakhshas', :job => 'eating people', :status => 'just woke up and hungry')
|
48
|
+
# rakhshas.save => Voila !!!!!!! it works
|
49
|
+
#
|
50
|
+
# CUSTOMIZATIONS
|
51
|
+
# --------------
|
52
|
+
#
|
53
|
+
# No amount of wrapping can provide very detailed customizations. Either you have a lot of methods
|
54
|
+
# that are not being used or there is hardly anything at all. To oversome this problem this module
|
55
|
+
# was written to provide only those methods which are common to most active resource objects.
|
56
|
+
# However if you want to have a little more control over your active resource objects its very easy.
|
57
|
+
# Here's how you would do it normally
|
58
|
+
#
|
59
|
+
# class Person < ActiveResource::Base
|
60
|
+
# def self.count
|
61
|
+
# find(:all).size
|
62
|
+
# end
|
63
|
+
#
|
64
|
+
# def occupation
|
65
|
+
# return job if job
|
66
|
+
# 'Unemployed'
|
67
|
+
# end
|
68
|
+
# end
|
69
|
+
#
|
70
|
+
# To do the same thing, here's how you do it using this library
|
71
|
+
#
|
72
|
+
# class Person
|
73
|
+
# module ClassMethods
|
74
|
+
# def count
|
75
|
+
# find(:all).size
|
76
|
+
# end
|
77
|
+
# end
|
78
|
+
#
|
79
|
+
# module InstanceMethods
|
80
|
+
# def occupation
|
81
|
+
# return job if job
|
82
|
+
# 'Unemployed'
|
83
|
+
# end
|
84
|
+
# end
|
85
|
+
# extend CommonClassMethods
|
86
|
+
# end
|
87
|
+
#
|
88
|
+
# The instance methods will be available as instance methods in the objects created, class methods
|
89
|
+
# will be available as class methods in the class of the object.
|
90
|
+
|
91
|
+
module CommonClassMethods
|
92
|
+
attr_reader :site, :user, :password
|
93
|
+
|
94
|
+
# creates an object of the class in which this module is extended
|
95
|
+
def new(args = {})
|
96
|
+
# @resource_class = create_resource_class() # should this be commented
|
97
|
+
@resource_class.new(args)
|
98
|
+
end
|
99
|
+
|
100
|
+
# sets the site for the class in which this module is extended
|
101
|
+
def site=(site)
|
102
|
+
if site != self.site
|
103
|
+
@site = site
|
104
|
+
uri = URI.parse(site)
|
105
|
+
@user = URI.decode(uri.user) if(uri.user)
|
106
|
+
@password = URI.decode(uri.password) if(uri.password)
|
107
|
+
@resource_class = self.send(:create_resource_class)
|
108
|
+
end
|
109
|
+
@site
|
110
|
+
end
|
111
|
+
|
112
|
+
# sets the user for the class in which this module is extended
|
113
|
+
def user=(user)
|
114
|
+
if user != self.user
|
115
|
+
@user = user
|
116
|
+
@resource_class = self.send(:create_resource_class) if(site)
|
117
|
+
end
|
118
|
+
@user
|
119
|
+
end
|
120
|
+
|
121
|
+
# sets the password for the class in which this module is extended
|
122
|
+
def password=(password)
|
123
|
+
if password != self.password
|
124
|
+
@password = password
|
125
|
+
@resource_class = self.send(:create_resource_class) if(site)
|
126
|
+
end
|
127
|
+
@password
|
128
|
+
end
|
129
|
+
|
130
|
+
# sets the collection name for the class in which this module is extended
|
131
|
+
def collection_name=(collection_name)
|
132
|
+
if collection_name != self.collection_name
|
133
|
+
@collection_name = collection_name
|
134
|
+
end
|
135
|
+
@collection_name
|
136
|
+
end
|
137
|
+
|
138
|
+
# sets the elment name for the class in which this module is extended
|
139
|
+
def element_name=(element_name)
|
140
|
+
if element_name != self.element_name
|
141
|
+
@element_name = element_name
|
142
|
+
end
|
143
|
+
@element_name
|
144
|
+
end
|
145
|
+
|
146
|
+
# collection name for the class in which this module is extended
|
147
|
+
def collection_name
|
148
|
+
@collection_name || class_name.underscore.pluralize
|
149
|
+
end
|
150
|
+
|
151
|
+
# element name for the class in which this module is extended
|
152
|
+
def element_name
|
153
|
+
@element_name || class_name.underscore
|
154
|
+
end
|
155
|
+
|
156
|
+
# def all_attributes_set?
|
157
|
+
# site && user && password
|
158
|
+
# end
|
159
|
+
|
160
|
+
# routes to active resource find
|
161
|
+
def find(*args)
|
162
|
+
scope = args.slice!(0)
|
163
|
+
options = args.slice!(0) || {}
|
164
|
+
obj = @resource_class.find(scope, options)
|
165
|
+
obj
|
166
|
+
end
|
167
|
+
|
168
|
+
private
|
169
|
+
# creates an active resource class dynamically. All the attributes are set automatically. Avoid calling
|
170
|
+
# this method directly
|
171
|
+
def create_resource_class
|
172
|
+
# raise exceptions if any of site, user or password is not set
|
173
|
+
raise "Please set the site for #{self} class before using create_resource_class()." unless(self.site)
|
174
|
+
|
175
|
+
created_class = Class.new(MingleResource)
|
176
|
+
|
177
|
+
# set the resource options
|
178
|
+
created_class.site = self.site
|
179
|
+
created_class.user = self.user
|
180
|
+
created_class.password = self.password
|
181
|
+
created_class.collection_name = self.collection_name
|
182
|
+
created_class.element_name = self.element_name
|
183
|
+
|
184
|
+
created_class_name = "#{self}::#{class_name}#{Mingle4r::Helpers.fast_token()}"
|
185
|
+
eval "#{created_class_name} = created_class"
|
186
|
+
|
187
|
+
# includes a module called InstanceMethods in the class created dynamically
|
188
|
+
# if it is defined inside the wrapper class
|
189
|
+
inst_meth_mod_name = instance_methods_module_name()
|
190
|
+
created_class.send(:include, self.const_get(inst_meth_mod_name.to_sym)) if inst_meth_mod_name
|
191
|
+
created_class.send(:include, Mingle4r::CommonDynClassInstanceMethods)
|
192
|
+
|
193
|
+
# extends the class created dynamically with a module called ClassMethods if
|
194
|
+
# it is defined inside the wrapper class
|
195
|
+
class_meth_mod_name = class_methods_module_name()
|
196
|
+
created_class.extend(self.const_get(class_meth_mod_name)) if class_meth_mod_name
|
197
|
+
|
198
|
+
created_class
|
199
|
+
end
|
200
|
+
|
201
|
+
def class_name
|
202
|
+
self.name.split('::')[-1]
|
203
|
+
end
|
204
|
+
|
205
|
+
def instance_methods_module_name
|
206
|
+
inst_meth_mod_name = 'InstanceMethods'
|
207
|
+
self.constants.detect { |const| const.split('::')[-1] =~ /#{inst_meth_mod_name}/ }
|
208
|
+
end
|
209
|
+
|
210
|
+
def class_methods_module_name
|
211
|
+
class_meth_mod_name = 'ClassMethods'
|
212
|
+
self.constants.detect { |const| const.split('::')[-1] =~ /#{class_meth_mod_name}/ }
|
213
|
+
end
|
214
|
+
|
215
|
+
def method_missing(meth_id, *args, &block)
|
216
|
+
@resource_class.send(meth_id, *args, &block)
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Mingle4r
|
2
|
+
# common dynamic class instance methods
|
3
|
+
module CommonDynClassInstanceMethods
|
4
|
+
# gets the value of a property. The property name can be given in several ways.
|
5
|
+
# suppose we are trying to get the property 'Iteration Number' as in mingle
|
6
|
+
#
|
7
|
+
# 1) give the name as seen card.property_value('Iteration Number')
|
8
|
+
#
|
9
|
+
# 2) give the property method name i.e. in the active resource object it would
|
10
|
+
# become by default 'cp_iteration_number' attribute unless set differently in
|
11
|
+
# Mingle
|
12
|
+
#
|
13
|
+
# 3) give the method a 'space separated' / 'camelcased' method name. e.g. - 'Iteration Number'
|
14
|
+
# 'IterationNumber'
|
15
|
+
def property_value(prop_name)
|
16
|
+
begin
|
17
|
+
column_name = PropertyDefinition.column_name_for(prop_name.to_s)
|
18
|
+
self.send(column_name.to_sym)
|
19
|
+
rescue NoMethodError
|
20
|
+
if self.attributes.has_key?(prop_name)
|
21
|
+
self.attributes[prop_name]
|
22
|
+
else
|
23
|
+
# try calling a underscorized method. eg 'Iteration Number' becomes
|
24
|
+
# 'iteration_number'
|
25
|
+
method_id = (prop_name.split(' ').map { |substring| substring.downcase }).join('_').underscore.to_sym
|
26
|
+
self.send(method_id)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def custom_properties
|
32
|
+
custom_props = []
|
33
|
+
props = attributes.keys
|
34
|
+
PropertyDefinition.find(:all).each do |prop|
|
35
|
+
custom_props << {prop.name => prop.column_name} if(props.include?(prop.column_name))
|
36
|
+
end
|
37
|
+
custom_props
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Mingle4r
|
2
|
+
module Helpers
|
3
|
+
def Helpers.fast_token
|
4
|
+
values = [
|
5
|
+
rand(0x0010000),
|
6
|
+
rand(0x0010000),
|
7
|
+
rand(0x0010000),
|
8
|
+
rand(0x0010000),
|
9
|
+
rand(0x0010000),
|
10
|
+
rand(0x1000000),
|
11
|
+
rand(0x1000000),
|
12
|
+
]
|
13
|
+
"%04x%04x%04x%04x%04x%06x%06x" % values
|
14
|
+
end
|
15
|
+
|
16
|
+
def Helpers.encode2html(string)
|
17
|
+
html_char_map = {
|
18
|
+
'[' => '%5B', ']' => '%5D',
|
19
|
+
'(' => '%28', ')' => '%29',
|
20
|
+
',' => '%2C', ' ' => '%20',
|
21
|
+
'=' => '%3D', '\'' => '%27',
|
22
|
+
'<' => '%3C', '>' => '%3E',
|
23
|
+
}
|
24
|
+
|
25
|
+
string.strip! if string
|
26
|
+
encoded_string = ''
|
27
|
+
string.each_char do |char|
|
28
|
+
encoded_string << (html_char_map[char] || char)
|
29
|
+
end
|
30
|
+
encoded_string
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
module Mingle4r
|
2
|
+
class MingleClient
|
3
|
+
attr_reader :site, :user, :password, :proj_id
|
4
|
+
|
5
|
+
def initialize(site, user, password, proj_id = nil)
|
6
|
+
@site = site
|
7
|
+
@user = user
|
8
|
+
@password = password
|
9
|
+
@proj_id = proj_id
|
10
|
+
set_resource_attributes()
|
11
|
+
end
|
12
|
+
|
13
|
+
def site=(site)
|
14
|
+
if site != self.site
|
15
|
+
@site = site
|
16
|
+
uri = URI.parse(site)
|
17
|
+
@user = URI.decode(uri.user) if(uri.user)
|
18
|
+
@password = URI.decode(uri.password) if(uri.password)
|
19
|
+
set_resource_attributes()
|
20
|
+
end
|
21
|
+
@site
|
22
|
+
end
|
23
|
+
|
24
|
+
def user=(user)
|
25
|
+
if user != self.user
|
26
|
+
@user = user
|
27
|
+
set_resource_attributes()
|
28
|
+
end
|
29
|
+
@user
|
30
|
+
end
|
31
|
+
|
32
|
+
def password=(password)
|
33
|
+
if password != self.password
|
34
|
+
@password = password
|
35
|
+
set_resource_attributes()
|
36
|
+
end
|
37
|
+
@password
|
38
|
+
end
|
39
|
+
|
40
|
+
def proj_id=(proj_id)
|
41
|
+
if proj_id != @proj_id
|
42
|
+
@proj_id = proj_id
|
43
|
+
set_resource_attributes()
|
44
|
+
end
|
45
|
+
@proj_id
|
46
|
+
end
|
47
|
+
|
48
|
+
def valid_credentials?
|
49
|
+
Project.site = site
|
50
|
+
Project.user = user
|
51
|
+
Project.password = password
|
52
|
+
begin
|
53
|
+
Project.find(:all)
|
54
|
+
true
|
55
|
+
rescue Exception => e
|
56
|
+
e.message
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def project
|
61
|
+
raise 'proj_id attribute not set' unless @proj_id
|
62
|
+
@project = Mingle4r::Project.find(@proj_id) unless(@project && (@proj_id == @project.identifier))
|
63
|
+
@project
|
64
|
+
end
|
65
|
+
|
66
|
+
def projects
|
67
|
+
Mingle4r::Project.find(:all)
|
68
|
+
end
|
69
|
+
|
70
|
+
def cards
|
71
|
+
raise 'proj_id attribute not set' unless @proj_id
|
72
|
+
@project = Mingle4r::Project.find(@proj_id) unless(@project && (@proj_id == @project.identifier))
|
73
|
+
@project.cards
|
74
|
+
end
|
75
|
+
|
76
|
+
def users
|
77
|
+
Mingle4r::User.find(:all)
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
def set_resource_attributes
|
82
|
+
set_project_attributes
|
83
|
+
set_user_attributes
|
84
|
+
end
|
85
|
+
|
86
|
+
def set_project_attributes
|
87
|
+
Project.site = @site
|
88
|
+
Project.user = @user
|
89
|
+
Project.password = @password
|
90
|
+
end
|
91
|
+
|
92
|
+
# def set_property_definition_attributes
|
93
|
+
# raise 'Project Id (proj_id attribute) not given.' unless @proj_id
|
94
|
+
# properties_site = File.join(@site.to_s, "projects/#{@proj_id}")
|
95
|
+
# PropertyDefinition.site = properties_site
|
96
|
+
# PropertyDefinition.user = @user
|
97
|
+
# PropertyDefinition.password = @password
|
98
|
+
# end
|
99
|
+
|
100
|
+
def set_user_attributes
|
101
|
+
User.site = @site
|
102
|
+
User.user = user
|
103
|
+
User.password = @password
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module Mingle4r
|
2
|
+
class Project
|
3
|
+
module InstanceMethods
|
4
|
+
# returns the cards for the project. To hit the resource server without returning
|
5
|
+
# cached results pass true as an argument.
|
6
|
+
def cards(refresh = false)
|
7
|
+
return @cards if(!refresh && @cards_cached)
|
8
|
+
cards_site = File.join(self.class.site.to_s, "projects/#{self.identifier()}")
|
9
|
+
Mingle4r::Card.site = cards_site
|
10
|
+
Mingle4r::Card.user = self.class.user
|
11
|
+
Mingle4r::Card.password = self.class.password
|
12
|
+
card_class = Mingle4r::Card.send(:create_resource_class)
|
13
|
+
@cards = card_class.find_without_pagination(:all)
|
14
|
+
@cards_cached = true
|
15
|
+
@cards
|
16
|
+
end
|
17
|
+
|
18
|
+
# returns the users for the project. To hit the resource server without returning
|
19
|
+
# cached results pass true as an argument.
|
20
|
+
def users(refresh = false)
|
21
|
+
return @users if(!refresh && @users_cached)
|
22
|
+
users_site = File.join(self.class.site.to_s, "projects/#{self.identifier()}")
|
23
|
+
Mingle4r::User.site = users_site
|
24
|
+
Mingle4r::User.user = self.class.user
|
25
|
+
Mingle4r::User.password = self.class.password
|
26
|
+
Mingle4r::User.element_name = nil # reset
|
27
|
+
user_class = Mingle4r::User.send(:create_resource_class)
|
28
|
+
@users = user_class.find(:all)
|
29
|
+
@users_cached = true
|
30
|
+
@users
|
31
|
+
end
|
32
|
+
|
33
|
+
# returns the wikis for the project. To hit the resource server without returning
|
34
|
+
# cached results pass true as an argument.
|
35
|
+
def wikis(refresh = false)
|
36
|
+
return @wikis if(!refresh && @wikis_cached)
|
37
|
+
wiki_site = File.join(self.class.site.to_s, "projects/#{self.identifier()}")
|
38
|
+
Mingle4r::Wiki.site = wiki_site
|
39
|
+
Mingle4r::Wiki.user = self.class.user
|
40
|
+
Mingle4r::Wiki.password = self.class.password
|
41
|
+
wiki_class = Mingle4r::Wiki.send(:create_resource_class)
|
42
|
+
@wikis = wiki_class.find(:all)
|
43
|
+
@wikis_cached = true
|
44
|
+
@wikis
|
45
|
+
end
|
46
|
+
|
47
|
+
# returns the property definitions for the project. To hit the resource server
|
48
|
+
# pass true as an argument
|
49
|
+
def property_definitions(refresh = false)
|
50
|
+
return @prop_definitions if(!refresh && @prop_definitions_cached)
|
51
|
+
properties_site = File.join(@site.to_s, "projects/#{self.identifier}")
|
52
|
+
PropertyDefinition.site = properties_site
|
53
|
+
PropertyDefinition.user = @user
|
54
|
+
PropertyDefinition.password = @password
|
55
|
+
prop_defn_class = Mingle4r::PropertyDefinition.send(:create_resource_class)
|
56
|
+
@prop_definitions = prop_defn_class.find(:all)
|
57
|
+
@prop_definitions_cached = true
|
58
|
+
@prop_definitions
|
59
|
+
end
|
60
|
+
end # module InstanceMethods
|
61
|
+
|
62
|
+
extend Mingle4r::CommonClassMethods
|
63
|
+
end # class Project
|
64
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Mingle4r
|
2
|
+
class PropertyDefinition
|
3
|
+
# module InstanceMethods
|
4
|
+
# end
|
5
|
+
#
|
6
|
+
# module ClassMethods
|
7
|
+
# end
|
8
|
+
#
|
9
|
+
extend Mingle4r::CommonClassMethods
|
10
|
+
|
11
|
+
self.element_name = 'record'
|
12
|
+
|
13
|
+
def self.column_name_for(prop_name)
|
14
|
+
property_def = @resource_class.find(:all).detect { |prop| prop.name == prop_name }
|
15
|
+
property_def ? property_def.column_name : nil
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../spec_helper'
|
2
|
+
|
3
|
+
describe Mingle4r::Card::Attachment do
|
4
|
+
it "should be able to create a class inherited from ActiveResource::Base" do
|
5
|
+
attachment_class = mock(Object)
|
6
|
+
|
7
|
+
attachment_class.should_receive(:site=).any_number_of_times
|
8
|
+
attachment_class.should_receive(:user=).any_number_of_times
|
9
|
+
attachment_class.should_receive(:password=).any_number_of_times
|
10
|
+
attachment_class.should_receive(:collection_name=).any_number_of_times
|
11
|
+
attachment_class.should_receive(:element_name=).any_number_of_times
|
12
|
+
attachment_class.should_receive(:include).any_number_of_times
|
13
|
+
Class.should_receive(:new).with(MingleResource).any_number_of_times.and_return(attachment_class)
|
14
|
+
|
15
|
+
Mingle4r::Card::Attachment.site = ''
|
16
|
+
Mingle4r::Card::Attachment.user = ''
|
17
|
+
Mingle4r::Card::Attachment.password = ''
|
18
|
+
|
19
|
+
Mingle4r::Card::Attachment.send(:create_resource_class).should == attachment_class
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should be able to download file to a location" do
|
23
|
+
Mingle4r::Card::Attachment.site = 'http://localhost/test'
|
24
|
+
Mingle4r::Card::Attachment.user = 'test'
|
25
|
+
Mingle4r::Card::Attachment.password = 'password'
|
26
|
+
|
27
|
+
attachment_obj = Mingle4r::Card::Attachment.new
|
28
|
+
attachment_obj.should_receive(:url).and_return('/attachment')
|
29
|
+
attachment_obj.should_receive(:file_name).and_return('attach')
|
30
|
+
|
31
|
+
request_obj = mock(Object)
|
32
|
+
request_obj.should_receive(:basic_auth)
|
33
|
+
|
34
|
+
response_obj = mock(Object)
|
35
|
+
response_obj.should_receive(:body).and_return('test text')
|
36
|
+
|
37
|
+
Net::HTTP::Get.should_receive(:new).and_return(request_obj)
|
38
|
+
Net::HTTP.should_receive(:start).and_return(response_obj)
|
39
|
+
|
40
|
+
|
41
|
+
attachment_obj.download()
|
42
|
+
|
43
|
+
File.exist?('attach').should == true
|
44
|
+
file_contents = IO.read('attach')
|
45
|
+
file_contents.should == 'test text'
|
46
|
+
File.delete('attach')
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
describe Mingle4r::Card do
|
4
|
+
it "should be able to call active resource find method with proper params" do
|
5
|
+
Mingle4r::Card.site = 'http://localhost/projects/test'
|
6
|
+
Mingle4r::Card.user = 'user'
|
7
|
+
Mingle4r::Card.password = 'password'
|
8
|
+
|
9
|
+
card_class = Mingle4r::Card.send(:create_resource_class)
|
10
|
+
card_class.should_receive(:find).with(:all, :params => {:page => 'all'})
|
11
|
+
card_class.find_without_pagination(:all)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should set attributes in attachment class properly" do
|
15
|
+
Mingle4r::Card.site = 'http://localhost/projects/test'
|
16
|
+
Mingle4r::Card.user = 'user'
|
17
|
+
Mingle4r::Card.password = 'password'
|
18
|
+
|
19
|
+
card_obj = Mingle4r::Card.new
|
20
|
+
card_obj.should_receive(:number).and_return(10)
|
21
|
+
|
22
|
+
attachment_class_mock = mock(Class)
|
23
|
+
Mingle4r::Card::Attachment.should_receive(:create_resource_class).any_number_of_times.and_return(attachment_class_mock)
|
24
|
+
|
25
|
+
attachment_class_mock.should_receive(:find)
|
26
|
+
card_obj.attachments
|
27
|
+
|
28
|
+
Mingle4r::Card::Attachment.site.should == 'http://localhost/projects/test/cards/10'
|
29
|
+
Mingle4r::Card::Attachment.user.should == 'user'
|
30
|
+
Mingle4r::Card::Attachment.password.should == 'password'
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should return the http body boundary" do
|
34
|
+
Mingle4r::Card.site = 'http://localhost/projects/test'
|
35
|
+
Mingle4r::Card.user = 'user'
|
36
|
+
Mingle4r::Card.password = 'password'
|
37
|
+
|
38
|
+
Mingle4r::Card.new.send(:boundary).should == '----------XnJLe9ZIbbGUYtzPQJ16u1'
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
class DummyClass
|
4
|
+
module InstanceMethods
|
5
|
+
end
|
6
|
+
|
7
|
+
# module ClassMethods
|
8
|
+
# end
|
9
|
+
extend Mingle4r::CommonClassMethods
|
10
|
+
end
|
11
|
+
|
12
|
+
describe DummyClass do
|
13
|
+
it "should be able to set the site properly" do
|
14
|
+
url = 'http://localhost/test'
|
15
|
+
DummyClass.site = url
|
16
|
+
DummyClass.site.should == url
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should be able to set the user properly" do
|
20
|
+
user = 'testuser'
|
21
|
+
DummyClass.user = user
|
22
|
+
DummyClass.user.should == user
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should be able to set the password properly" do
|
26
|
+
password = 'password'
|
27
|
+
DummyClass.password = password
|
28
|
+
DummyClass.password.should == password
|
29
|
+
end
|
30
|
+
|
31
|
+
xit "should understand that the attributes have been set" do
|
32
|
+
DummyClass.site = 'http://localhost:8080'
|
33
|
+
DummyClass.user = 'test'
|
34
|
+
DummyClass.password = 'password'
|
35
|
+
assert DummyClass.all_attributes_set?
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should be able to create an instance" do
|
39
|
+
dummy_obj = DummyClass.new
|
40
|
+
dummy_obj.should_not == nil
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should be able to create a class inherited from ActiveResource::Base" do
|
44
|
+
dummy_obj = DummyClass.new
|
45
|
+
dummy_obj.class.superclass.should == ActiveResource::Base
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should create same class when site is not changed" do
|
49
|
+
class DummyClass
|
50
|
+
class << self
|
51
|
+
attr_reader :resource_class
|
52
|
+
end
|
53
|
+
end
|
54
|
+
site = 'http://localhost/foo'
|
55
|
+
DummyClass.site = site
|
56
|
+
DummyClass.user = 'test'
|
57
|
+
DummyClass.password = 'password'
|
58
|
+
class1 = DummyClass.resource_class
|
59
|
+
DummyClass.site = site
|
60
|
+
class2 = DummyClass.resource_class
|
61
|
+
class1.should == class2
|
62
|
+
end
|
63
|
+
|
64
|
+
it "should create different classes when site is changed" do
|
65
|
+
class DummyClass
|
66
|
+
class << self
|
67
|
+
attr_reader :resource_class
|
68
|
+
end
|
69
|
+
end
|
70
|
+
site1 = 'http://localhost/foo'
|
71
|
+
site2 = 'http://localhost/bar'
|
72
|
+
DummyClass.site = site1
|
73
|
+
DummyClass.user = 'test'
|
74
|
+
DummyClass.password = 'password'
|
75
|
+
class1 = DummyClass.resource_class
|
76
|
+
DummyClass.site = site2
|
77
|
+
class2 = DummyClass.resource_class
|
78
|
+
class1.should_not == class2
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should be able to find the instance methods module in the class" do
|
82
|
+
DummyClass.send(:instance_methods_module_name).should == 'InstanceMethods'
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should not be able to find class methods module in the class" do
|
86
|
+
DummyClass.send(:class_methods_module_name).should be_nil
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should return the class name only without nesting of namespaces" do
|
90
|
+
DummyClass.send(:class_name).should == 'DummyClass'
|
91
|
+
end
|
92
|
+
|
93
|
+
it "should call find with proper params" do
|
94
|
+
# open access to @resource var
|
95
|
+
class << DummyClass
|
96
|
+
attr_writer :resource_class
|
97
|
+
end
|
98
|
+
|
99
|
+
mock_class = mock(Class)
|
100
|
+
DummyClass.resource_class = mock_class
|
101
|
+
mock_class.should_receive(:find).with(:all, :from => 'http://localhost/test', :params => {:foo => 'bar'})
|
102
|
+
DummyClass.find(:all, :from => 'http://localhost/test', :params => {:foo => 'bar'})
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
describe Mingle4r::Helpers do
|
4
|
+
it "should return a token" do
|
5
|
+
assert Mingle4r::Helpers::fast_token
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should not return the same token" do
|
9
|
+
token1 = Mingle4r::Helpers::fast_token
|
10
|
+
token2 = Mingle4r::Helpers::fast_token
|
11
|
+
assert_not_equal(token1, token2)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should return a string in proper html format" do
|
15
|
+
string = 'Hello(whosoever) world []'
|
16
|
+
html_result = Mingle4r::Helpers::encode2html(string)
|
17
|
+
html_expected = 'Hello%28whosoever%29%20world%20%5B%5D'
|
18
|
+
html_result.should == html_expected
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
describe Mingle4r::Project do
|
4
|
+
it "should return the set the Card class attributes properly" do
|
5
|
+
Mingle4r::Project.site = 'http://localhost'
|
6
|
+
Mingle4r::Project.user = 'test'
|
7
|
+
Mingle4r::Project.password = 'password'
|
8
|
+
|
9
|
+
proj = Mingle4r::Project.new(:identifier => 'test')
|
10
|
+
card_class_mock = mock(Class)
|
11
|
+
Mingle4r::Card.should_receive(:create_resource_class).any_number_of_times.and_return(card_class_mock)
|
12
|
+
card_class_mock.should_receive(:find_without_pagination).with(:all)
|
13
|
+
|
14
|
+
proj.cards
|
15
|
+
|
16
|
+
Mingle4r::Card.site.should == 'http://localhost/projects/test'
|
17
|
+
Mingle4r::Card.user.should == 'test'
|
18
|
+
Mingle4r::Card.password.should == 'password'
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
require 'ostruct'
|
3
|
+
|
4
|
+
describe Mingle4r::PropertyDefinition do
|
5
|
+
before(:all) do
|
6
|
+
Mingle4r::PropertyDefinition.site = 'http://localhost:8080/projects/test'
|
7
|
+
Mingle4r::PropertyDefinition.user = 'test'
|
8
|
+
Mingle4r::PropertyDefinition.password = 'secret'
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should return the element name properly" do
|
12
|
+
Mingle4r::PropertyDefinition::element_name.should == 'record'
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should return the property value" do
|
16
|
+
class Mingle4r::PropertyDefinition
|
17
|
+
class << self
|
18
|
+
attr_accessor :resource_class
|
19
|
+
end
|
20
|
+
end
|
21
|
+
resource_class = mock(Class)
|
22
|
+
property_1 = { :name => 'Test Property', :column_name => 'test_property_name' }
|
23
|
+
property_2 = { :name => 'Added in Iteration number', :column_name => 'added_in_iteration_number'}
|
24
|
+
find_results = [OpenStruct.new(property_1), OpenStruct.new(property_2)]
|
25
|
+
Mingle4r::PropertyDefinition.resource_class = resource_class
|
26
|
+
resource_class.should_receive(:find).with(:all).any_number_of_times.and_return(find_results)
|
27
|
+
Mingle4r::PropertyDefinition.column_name_for('Test Property').should == 'test_property_name'
|
28
|
+
Mingle4r::PropertyDefinition.column_name_for('Added in Iteration number').should == 'added_in_iteration_number'
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
describe Mingle4r::Wiki do
|
4
|
+
it "should return the collection name properly" do
|
5
|
+
Mingle4r::Wiki::collection_name.should == 'wiki'
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should return the element name properly" do
|
9
|
+
Mingle4r::Wiki::element_name.should == 'page'
|
10
|
+
end
|
11
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
require '../../lib/mingle4r'
|
2
|
+
|
3
|
+
|
4
|
+
class Mingle4r::Project
|
5
|
+
class << self
|
6
|
+
attr_reader :resource_class
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
m_c = Mingle4r::MingleClient.new('http://localhost:9090', 'testuser', 'testuser', 'api_test')
|
11
|
+
|
12
|
+
|
13
|
+
proj = m_c.project
|
14
|
+
puts proj.users.size
|
15
|
+
cards = m_c.cards
|
16
|
+
card = cards[2]
|
17
|
+
puts '------------------------------'
|
18
|
+
puts Mingle4r::PropertyDefinition.site
|
19
|
+
puts Mingle4r::PropertyDefinition.user
|
20
|
+
puts Mingle4r::PropertyDefinition.password
|
21
|
+
puts Mingle4r::PropertyDefinition.new.class.collection_path
|
22
|
+
|
23
|
+
puts card.property_value('Name')
|
24
|
+
puts '||||||||||||||||||||||||||||||'
|
25
|
+
puts card.custom_properties
|
26
|
+
puts card.number
|
27
|
+
puts card.description
|
28
|
+
# card.description = 'test'
|
29
|
+
# card.save
|
30
|
+
attachment = card.attachments[0]
|
31
|
+
# puts attachment.url
|
32
|
+
|
33
|
+
# delete attachments
|
34
|
+
Mingle4r::Card::Attachment.site = 'http://localhost:9090/projects/api_test/cards/18'
|
35
|
+
Mingle4r::Card::Attachment.user = 'testuser'
|
36
|
+
Mingle4r::Card::Attachment.password = 'testuser'
|
37
|
+
|
38
|
+
attachment = Mingle4r::Card::Attachment.find(:all)[0]
|
39
|
+
# attachment.delete
|
@@ -0,0 +1,9 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require File.dirname(__FILE__) + '/../../lib/mingle4r'
|
3
|
+
|
4
|
+
Mingle4r::Card.site = 'http://localhost:9090/projects/api_test'
|
5
|
+
Mingle4r::Card.user = 'testuser'
|
6
|
+
Mingle4r::Card.password = 'testuser'
|
7
|
+
card = Mingle4r::Card.find(18)
|
8
|
+
|
9
|
+
puts card.add_comment('testingcomment')
|
@@ -0,0 +1,7 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require File.dirname(__FILE__) + '/../../lib/mingle4r'
|
3
|
+
|
4
|
+
Mingle4r::Card.site = 'http://localhost:9090/projects/api_test'
|
5
|
+
Mingle4r::Card.user = 'testuser'
|
6
|
+
Mingle4r::Card.password = 'testuser'
|
7
|
+
puts (Mingle4r::Card.apply_filter('Type IS Defect')).size
|
@@ -0,0 +1,9 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require File.dirname(__FILE__) + '/../../lib/mingle4r'
|
3
|
+
|
4
|
+
Mingle4r::Card.site = 'http://localhost:9090/projects/api_test'
|
5
|
+
Mingle4r::Card.user = 'testuser'
|
6
|
+
Mingle4r::Card.password = 'testuser'
|
7
|
+
card = Mingle4r::Card.find(21)
|
8
|
+
|
9
|
+
card.execute_transition(:transition_name => 'Close Defect')
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require File.dirname(__FILE__) + '/../../lib/mingle4r'
|
3
|
+
|
4
|
+
Mingle4r::User.site = 'http://localhost:9090/projects/api_test'
|
5
|
+
Mingle4r::User.user = 'testuser'
|
6
|
+
Mingle4r::User.password = 'testuser'
|
7
|
+
|
8
|
+
users = Mingle4r::User.find(:all)
|
9
|
+
|
10
|
+
puts users[0].user
|
@@ -0,0 +1,9 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require File.dirname(__FILE__) + '/../../lib/mingle4r'
|
3
|
+
|
4
|
+
Mingle4r::Card.site = 'http://localhost:9090/projects/api_test'
|
5
|
+
Mingle4r::Card.user = 'testuser'
|
6
|
+
Mingle4r::Card.password = 'testuser'
|
7
|
+
|
8
|
+
card = Mingle4r::Card.find(26)
|
9
|
+
puts card.upload_attachment(File.join(File.dirname(__FILE__), '/../../README'))
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: arusarka-mingle4r
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- asur
|
@@ -31,8 +31,40 @@ extensions: []
|
|
31
31
|
extra_rdoc_files:
|
32
32
|
- README
|
33
33
|
files:
|
34
|
+
- lib/mingle4r/card.rb
|
35
|
+
- lib/mingle4r/card/attachment.rb
|
36
|
+
- lib/mingle4r/common_class_methods.rb
|
37
|
+
- lib/mingle4r/common_dyn_class_instance_methods.rb
|
38
|
+
- lib/mingle4r/helpers.rb
|
39
|
+
- lib/mingle4r/mingle_client.rb
|
40
|
+
- lib/mingle4r/project.rb
|
41
|
+
- lib/mingle4r/property_definition.rb
|
42
|
+
- lib/mingle4r/user.rb
|
43
|
+
- lib/mingle4r/version.rb
|
44
|
+
- lib/mingle4r/wiki.rb
|
45
|
+
- lib/mingle4r.rb
|
46
|
+
- lib/mingle_resource.rb
|
47
|
+
- spec/mingle4r/card/attachment_spec.rb
|
48
|
+
- spec/mingle4r/card_spec.rb
|
49
|
+
- spec/mingle4r/common_class_methods_spec.rb
|
50
|
+
- spec/mingle4r/helpers_spec.rb
|
51
|
+
- spec/mingle4r/project_spec.rb
|
52
|
+
- spec/mingle4r/property_definition_spec.rb
|
53
|
+
- spec/mingle4r/version_spec.rb
|
54
|
+
- spec/mingle4r/wiki_spec.rb
|
55
|
+
- spec/mingle4r_spec.rb
|
56
|
+
- spec/spec_helper.rb
|
57
|
+
- test/integration/test.rb
|
58
|
+
- test/integration/test_adding_comment.rb
|
59
|
+
- test/integration/test_card_filter.rb
|
60
|
+
- test/integration/test_card_transition.rb
|
61
|
+
- test/integration/test_getting_users.rb
|
62
|
+
- test/integration/test_uploading_attachment.rb
|
63
|
+
- test/integration/test_wiki.rb
|
64
|
+
- MIT-LICENSE
|
65
|
+
- Rakefile
|
34
66
|
- README
|
35
|
-
-
|
67
|
+
- TODO.txt
|
36
68
|
has_rdoc: false
|
37
69
|
homepage: http://github.com/arusarka/mingle4r/
|
38
70
|
post_install_message:
|
data/History.txt
DELETED
@@ -1,33 +0,0 @@
|
|
1
|
-
0.2.2
|
2
|
-
-----
|
3
|
-
|
4
|
-
* added add_comment to card class. allows adding comment to a card
|
5
|
-
|
6
|
-
0.2.1
|
7
|
-
-----
|
8
|
-
|
9
|
-
* added option to relationship methods(e.g. - cards, users, attachments) to return cached or
|
10
|
-
updated results
|
11
|
-
* property_definitions is a relationship method of project instead of mingle client.
|
12
|
-
|
13
|
-
0.2.0
|
14
|
-
-----
|
15
|
-
|
16
|
-
* added class method apply_filter to Card class. allows an MQL filter
|
17
|
-
* added instance method execute_transition to Card class. allows to execute a transition on a card.
|
18
|
-
|
19
|
-
0.1.6
|
20
|
-
-----
|
21
|
-
|
22
|
-
* added instance method at_version to Card class. gets the particular version for the card
|
23
|
-
|
24
|
-
0.1.5
|
25
|
-
-----
|
26
|
-
|
27
|
-
* Rest Api's added - wiki
|
28
|
-
|
29
|
-
0.0.1
|
30
|
-
-----
|
31
|
-
|
32
|
-
* Rest Api's - Card, Project, User, Attachment, Property Definition
|
33
|
-
* Mingle Client - supports getting cards, project etc.
|