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.
@@ -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.
@@ -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
@@ -0,0 +1,4 @@
1
+ Write documentation
2
+ Test the new methods in card class
3
+ test wiki, property definitions api extensively
4
+ subversion configuration api
@@ -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,5 @@
1
+ module Mingle4r
2
+ class User
3
+ extend Mingle4r::CommonClassMethods
4
+ end
5
+ end
@@ -0,0 +1,11 @@
1
+ module Mingle4r
2
+ module Version
3
+ Major = '0'
4
+ Minor = '2'
5
+ Tiny = '7'
6
+
7
+ def self.to_s
8
+ Major + '.' + Minor + '.' + Tiny
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,7 @@
1
+ module Mingle4r
2
+ class Wiki
3
+ extend Mingle4r::CommonClassMethods
4
+ self.collection_name = 'wiki'
5
+ self.element_name = 'page'
6
+ end
7
+ end
@@ -0,0 +1,2 @@
1
+ # to avoid writing ActiveResource::Base
2
+ MingleResource = ActiveResource::Base
@@ -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,7 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe Mingle4r::Version do
4
+ it "should return proper version" do
5
+ Mingle4r::Version::to_s.should == '0.2.7'
6
+ end
7
+ 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
@@ -0,0 +1,7 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe Mingle4r do
4
+ it "should return my name" do
5
+ Mingle4r::AUTHOR.should == 'Asur'
6
+ end
7
+ end
@@ -0,0 +1,3 @@
1
+ require File.dirname(__FILE__) + '/../lib/mingle4r'
2
+ require 'test/unit'
3
+ require 'spec/test/unit'
@@ -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'))
@@ -0,0 +1,7 @@
1
+ require 'rubygems'
2
+ require File.dirname(__FILE__) + '/../../lib/mingle4r'
3
+
4
+ m_c = Mingle4r::MingleClient.new('http://localhost:9090', 'testuser', 'testuser', 'api_test')
5
+
6
+ proj = m_c.project
7
+ puts proj.wiki.size
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.5
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
- - History.txt
67
+ - TODO.txt
36
68
  has_rdoc: false
37
69
  homepage: http://github.com/arusarka/mingle4r/
38
70
  post_install_message:
@@ -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.