taskmapper-unfuddle 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm 1.9.2@taskmapper-unfuddle --create
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.8.7
4
+ - 1.9.2
data/Gemfile ADDED
@@ -0,0 +1,16 @@
1
+ source "http://rubygems.org"
2
+ # Add dependencies required to use your gem here.
3
+ # Example:
4
+ # gem "activesupport", ">= 2.3.5"
5
+
6
+ gem "taskmapper", "~> 0.8"
7
+ gem "addressable", "~> 2.2"
8
+ # Add dependencies to develop your gem here.
9
+ # Include everything needed to run rake, tests, features, etc.
10
+ group :development do
11
+ gem "rspec", "~> 2.8"
12
+ gem "bundler", "~> 1.1"
13
+ gem "jeweler", "~> 1.6"
14
+ gem "simplecov", "~> 0.5", :platforms => :ruby_19
15
+ gem "rcov", "~> 1.0", :platforms => :ruby_18
16
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,57 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ activemodel (3.2.3)
5
+ activesupport (= 3.2.3)
6
+ builder (~> 3.0.0)
7
+ activeresource (3.2.3)
8
+ activemodel (= 3.2.3)
9
+ activesupport (= 3.2.3)
10
+ activesupport (3.2.3)
11
+ i18n (~> 0.6)
12
+ multi_json (~> 1.0)
13
+ addressable (2.2.8)
14
+ builder (3.0.0)
15
+ diff-lcs (1.1.3)
16
+ git (1.2.5)
17
+ hashie (1.2.0)
18
+ i18n (0.6.0)
19
+ jeweler (1.8.3)
20
+ bundler (~> 1.0)
21
+ git (>= 1.2.5)
22
+ rake
23
+ rdoc
24
+ json (1.7.3)
25
+ multi_json (1.3.5)
26
+ rake (0.9.2.2)
27
+ rcov (1.0.0)
28
+ rdoc (3.12)
29
+ json (~> 1.4)
30
+ rspec (2.10.0)
31
+ rspec-core (~> 2.10.0)
32
+ rspec-expectations (~> 2.10.0)
33
+ rspec-mocks (~> 2.10.0)
34
+ rspec-core (2.10.0)
35
+ rspec-expectations (2.10.0)
36
+ diff-lcs (~> 1.1.3)
37
+ rspec-mocks (2.10.1)
38
+ simplecov (0.6.4)
39
+ multi_json (~> 1.0)
40
+ simplecov-html (~> 0.5.3)
41
+ simplecov-html (0.5.3)
42
+ taskmapper (0.8.0)
43
+ activeresource (~> 3.0)
44
+ activesupport (~> 3.0)
45
+ hashie (~> 1.2)
46
+
47
+ PLATFORMS
48
+ ruby
49
+
50
+ DEPENDENCIES
51
+ addressable (~> 2.2)
52
+ bundler (~> 1.1)
53
+ jeweler (~> 1.6)
54
+ rcov (~> 1.0)
55
+ rspec (~> 2.8)
56
+ simplecov (~> 0.5)
57
+ taskmapper (~> 0.8)
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Hybrid Group
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,28 @@
1
+ # taskmapper-unfuddle
2
+
3
+ TaskMapper provider for Unfuddle.
4
+
5
+ ## Usage
6
+
7
+ Instantiate the TaskMapper instance
8
+
9
+ unfuddle = TaskMapper.new(:unfuddle, :username => "user", :password => "p4ss!", :account => 'unfud')
10
+
11
+ if this gives you trouble when trying to access projects or tickets, you can set the protocol explicitly. By default,
12
+ as of version 0.4.0, the protocol is 'https'. Some older projects may need to set to 'http'.
13
+
14
+ unfuddle = TaskMapper.new(:unfuddle, :username => "user", :password => "p4ss!", :account => 'unfud', :protocol => 'http')
15
+
16
+ ## Note on Patches/Pull Requests
17
+
18
+ * Fork the project.
19
+ * Make your feature addition or bug fix.
20
+ * Add tests for it. This is important so I don't break it in a
21
+ future version unintentionally.
22
+ * Commit, do not mess with rakefile, version, or history.
23
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
24
+ * Send me a pull request. Bonus points for topic branches.
25
+
26
+ ## Copyright
27
+
28
+ Copyright (c) 2010 [Hybrid Group](http://hybridgroup.com). See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,42 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "taskmapper-unfuddle"
8
+ gem.summary = %Q{The Unfuddle provider for taskmapper.}
9
+ gem.description = %Q{Unfuddle provider for taskmapper implemented with ActiveResource}
10
+ gem.email = "luis@hybridgroup.com"
11
+ gem.homepage = "http://github.com/hybridgroup/taskmapper-unfuddle"
12
+ gem.authors = ["Luis Hurtado"]
13
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
14
+ end
15
+ Jeweler::GemcutterTasks.new
16
+ rescue LoadError
17
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
18
+ end
19
+
20
+ require 'rspec/core'
21
+ require 'rspec/core/rake_task'
22
+ RSpec::Core::RakeTask.new(:spec) do |spec|
23
+ spec.pattern = FileList['spec/**/*_spec.rb']
24
+ end
25
+
26
+ RSpec::Core::RakeTask.new(:rcov) do |spec|
27
+ spec.pattern = 'spec/**/*_spec.rb'
28
+ spec.rcov = true
29
+ end
30
+
31
+ task :default => :spec
32
+
33
+ require 'rake/rdoctask' # TODO This produces a warning
34
+ require 'rdoc/task'
35
+ Rake::RDocTask.new do |rdoc|
36
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
37
+
38
+ rdoc.rdoc_dir = 'rdoc'
39
+ rdoc.title = "taskmapper-kanbanpad#{version}"
40
+ rdoc.rdoc_files.include('README*')
41
+ rdoc.rdoc_files.include('lib/**/*.rb')
42
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.7.0
@@ -0,0 +1,45 @@
1
+ module TaskMapper::Provider
2
+ module Unfuddle
3
+ # The comment class for taskmapper-unfuddle
4
+ #
5
+ # Do any mapping between TaskMapper and your system's comment model here
6
+ # versions of the ticket.
7
+ #
8
+ class Comment < TaskMapper::Provider::Base::Comment
9
+ API = UnfuddleAPI::Comment # The class to access the api's comments
10
+ # declare needed overloaded methods here
11
+
12
+ def initialize(*options)
13
+ @system_data ||= {}
14
+ @cache ||= {}
15
+ first = options.shift
16
+ case first
17
+ when Hash
18
+ super first.to_hash
19
+ else
20
+ @system_data[:client] = first
21
+ super first.attributes.merge!(
22
+ :project_id => first.prefix_options[:project_id],
23
+ :ticket_id => first.prefix_options[:ticket_id])
24
+ end
25
+ end
26
+
27
+ def author
28
+ @author ||= begin
29
+ UnfuddleAPI::People.find(self[:author_id]).username
30
+ rescue
31
+ ''
32
+ end
33
+ end
34
+
35
+ def created_at
36
+ @created_at ||= self[:created_at] ? Time.parse(self[:created_at]) : nil
37
+ end
38
+
39
+ def updated_at
40
+ @updated_at ||= self[:updated_at] ? Time.parse(self[:updated_at]) : nil
41
+ end
42
+
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,50 @@
1
+ module TaskMapper::Provider
2
+ module Unfuddle
3
+ # Project class for taskmapper-unfuddle
4
+ #
5
+ #
6
+ class Project < TaskMapper::Provider::Base::Project
7
+ API = UnfuddleAPI::Project # The class to access the api's projects
8
+ # declare needed overloaded methods here
9
+
10
+ def initialize(*options)
11
+ @system_data ||= {}
12
+ @cache ||= {}
13
+ first = options.shift
14
+ case first
15
+ when Hash
16
+ super(first.to_hash)
17
+ else
18
+ @system_data[:client] = first
19
+ super(first.attributes)
20
+ end
21
+ end
22
+
23
+ def name
24
+ self.title
25
+ end
26
+
27
+ def created_at
28
+ @created_at ||= self[:created_at] ? Time.parse(self[:created_at]) : nil
29
+ end
30
+
31
+ def updated_at
32
+ @updated_at ||= self[:updated_at] ? Time.parse(self[:updated_at]) : nil
33
+ end
34
+
35
+ # copy from this.copy(that) copies that into this
36
+ def copy(project)
37
+ project.tickets.each do |ticket|
38
+ copy_ticket = self.ticket!(:title => ticket.title, :description => ticket.description)
39
+ ticket.comments.each do |comment|
40
+ copy_ticket.comment!(:body => comment.body)
41
+ sleep 1
42
+ end
43
+ end
44
+ end
45
+
46
+ end
47
+ end
48
+ end
49
+
50
+
@@ -0,0 +1,53 @@
1
+ module TaskMapper::Provider
2
+ module Unfuddle
3
+ # Ticket class for taskmapper-unfuddle
4
+ #
5
+
6
+ class Ticket < TaskMapper::Provider::Base::Ticket
7
+ API = UnfuddleAPI::Ticket # The class to access the api's tickets
8
+ # declare needed overloaded methods here
9
+
10
+ def initialize(*options)
11
+ @system_data ||= {}
12
+ @cache ||= {}
13
+ first = options.shift
14
+ case first
15
+ when Hash
16
+ super first.to_hash
17
+ else
18
+ @system_data[:client] = first
19
+ super first.attributes.merge! :project_id => first.prefix_options[:project_id]
20
+ end
21
+ end
22
+
23
+ def title
24
+ self.summary
25
+ end
26
+
27
+ def created_at
28
+ @created_at ||= self[:created_at] ? Time.parse(self[:created_at]) : nil
29
+ end
30
+
31
+ def updated_at
32
+ @updated_at ||= self[:updated_at] ? Time.parse(self[:updated_at]) : nil
33
+ end
34
+
35
+ def assignee
36
+ @assignee ||= begin
37
+ UnfuddleAPI::People.find(self[:assignee_id]).username
38
+ rescue
39
+ ''
40
+ end
41
+ end
42
+
43
+ def requestor
44
+ @requestor ||= begin
45
+ UnfuddleAPI::People.find(self[:reporter_id]).username
46
+ rescue
47
+ ''
48
+ end
49
+ end
50
+
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,39 @@
1
+ module TaskMapper::Provider
2
+ # This is the Unfuddle Provider for taskmapper
3
+ module Unfuddle
4
+ include TaskMapper::Provider::Base
5
+ TICKET_API = UnfuddleAPI::Ticket # The class to access the api's tickets
6
+ PROJECT_API = UnfuddleAPI::Project # The class to access the api's projects
7
+
8
+ # This is for cases when you want to instantiate using TaskMapper::Provider::Unfuddle.new(auth)
9
+ def self.new(auth = {})
10
+ TaskMapper.new(:unfuddle, auth)
11
+ end
12
+
13
+ # Providers must define an authorize method. This is used to initialize and set authentication
14
+ # parameters to access the API
15
+ def authorize(auth = {})
16
+ @authentication ||= TaskMapper::Authenticator.new(auth)
17
+ auth = @authentication
18
+ if (auth.account.nil? and auth.subdomain.nil?) or auth.username.nil? or auth.password.nil?
19
+ raise "Please provide at least an account (subdomain), username and password)"
20
+ end
21
+ UnfuddleAPI.protocol = auth.protocol if auth.protocol?
22
+ UnfuddleAPI.account = auth.account || auth.subdomain
23
+ UnfuddleAPI.authenticate(auth.username, auth.password)
24
+ end
25
+
26
+ # declare needed overloaded methods here
27
+
28
+ def valid?
29
+ begin
30
+ PROJECT_API.find(:first)
31
+ true
32
+ rescue
33
+ false
34
+ end
35
+ end
36
+ end
37
+ end
38
+
39
+
@@ -0,0 +1,5 @@
1
+ require File.dirname(__FILE__) + '/unfuddle/unfuddle-api'
2
+
3
+ %w{ unfuddle ticket project comment }.each do |f|
4
+ require File.dirname(__FILE__) + '/provider/' + f + '.rb';
5
+ end
@@ -0,0 +1,153 @@
1
+ require 'rubygems'
2
+
3
+ begin
4
+ require 'uri'
5
+ require 'addressable/uri'
6
+
7
+ module URI
8
+ def decode(*args)
9
+ Addressable::URI.decode(*args)
10
+ end
11
+
12
+ def escape(*args)
13
+ Addressable::URI.escape(*args)
14
+ end
15
+
16
+ def parse(*args)
17
+ Addressable::URI.parse(*args)
18
+ end
19
+ end
20
+ rescue LoadError => e
21
+ puts "Install the Addressable gem (with dependencies) to support accounts with subdomains."
22
+ puts "# sudo gem install addressable --development"
23
+ puts e.message
24
+ end
25
+
26
+ require 'active_support'
27
+ require 'active_resource'
28
+
29
+ # Ruby lib for working with the Unfuddle API's XML interface.
30
+ # The first thing you need to set is the account name. This is the same
31
+ # as the web address for your account.
32
+ #
33
+ # UnfuddleAPI.account = 'activereload'
34
+ #
35
+ # Then, you should set the authentication with HTTP Basic Authentication.
36
+ #
37
+ # # with basic authentication
38
+ # UnfuddleAPI.authenticate('rick', 'spacemonkey')
39
+ #
40
+ #
41
+ # This library is a small wrapper around the REST interface. You should read the docs at
42
+ # http://unfuddle.com/docs/api
43
+ #
44
+ module UnfuddleAPI
45
+ class Error < StandardError; end
46
+ class << self
47
+ attr_accessor :username, :password, :host_format, :domain_format, :protocol, :port
48
+ attr_reader :account
49
+
50
+ # Sets the account name, and updates all the resources with the new domain.
51
+ def account=(name)
52
+ resources.each do |klass|
53
+ klass.site = klass.site_format % (host_format % [protocol, domain_format % name, ":#{port}"])
54
+ end
55
+ @account = name
56
+ end
57
+
58
+ # Sets up basic authentication credentials for all the resources.
59
+ def authenticate(username, password)
60
+ @username = username
61
+ @password = password
62
+ self::Base.user = username
63
+ self::Base.password = password
64
+ end
65
+
66
+ def resources
67
+ @resources ||= []
68
+ end
69
+ end
70
+
71
+ self.host_format = '%s://%s%s/api/v1'
72
+ self.domain_format = '%s.unfuddle.com'
73
+ self.protocol = 'https'
74
+ self.port = ''
75
+
76
+ class Base < ActiveResource::Base
77
+ self.format = :xml
78
+ def self.inherited(base)
79
+ UnfuddleAPI.resources << base
80
+ class << base
81
+ attr_accessor :site_format
82
+ end
83
+ base.site_format = '%s'
84
+ super
85
+ end
86
+ end
87
+
88
+ # Find projects
89
+ #
90
+ # UnfuddleAPI::Project.find(:all) # find all projects for the current account.
91
+ # UnfuddleAPI::Project.find(44) # find individual project by ID
92
+ #
93
+ # Creating a Project
94
+ #
95
+ # project = UnfuddleAPI::Project.new(:name => 'Ninja Whammy Jammy')
96
+ # project.save
97
+ # # => true
98
+ #
99
+ #
100
+ # Updating a Project
101
+ #
102
+ # project = UnfuddleAPI::Project.find(44)
103
+ # project.name = "Lighthouse Issues"
104
+ # project.public = false
105
+ # project.save
106
+ #
107
+ # Finding tickets
108
+ #
109
+ # project = LighthouseAPI::Project.find(44)
110
+ # project.tickets
111
+ #
112
+ class Project < Base
113
+ def tickets(options = {})
114
+ Ticket.find(:all, :params => options.update(:project_id => id))
115
+ end
116
+
117
+ def messages(options = {})
118
+ Message.find(:all, :params => options.update(:project_id => id))
119
+ end
120
+
121
+ def milestones(options = {})
122
+ Milestone.find(:all, :params => options.update(:project_id => id))
123
+ end
124
+ end
125
+
126
+ # Find tickets
127
+ #
128
+ # UnfuddleAPI::Ticket.find(:all, :params => { :project_id => 44 })
129
+ # UnfuddleAPI::Ticket.find(:all, :params => { :project_id => 44, :q => "status:closed" })
130
+ #
131
+ # project = UnfuddleAPI::Project.find(44)
132
+ # project.tickets
133
+ # project.tickets(:q => "status:closed")
134
+ # project.tickets(:params => {:status => 'closed'})
135
+ #
136
+ #
137
+ #
138
+ class Ticket < Base
139
+ site_format << '/projects/:project_id'
140
+ end
141
+
142
+ class Comment < Base
143
+ site_format << '/projects/:project_id/tickets/:ticket_id'
144
+ end
145
+
146
+ class Message < Base
147
+ site_format << '/projects/:project_id'
148
+ end
149
+
150
+ class People < Base
151
+ end
152
+
153
+ end
@@ -0,0 +1,76 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe TaskMapper::Provider::Unfuddle::Comment do
4
+ before(:each) do
5
+ @headers = {'Authorization' => 'Basic Zm9vOjAwMDAwMA==', 'Accept' => 'application/xml'}
6
+ @headers_post_put = {'Authorization' => 'Basic Zm9vOjAwMDAwMA==', 'Content-Type' => 'application/xml'}
7
+ ActiveResource::HttpMock.respond_to do |mock|
8
+ mock.get '/api/v1/projects/33042.xml', @headers, fixture_for('projects/33042'), 200
9
+ mock.get '/api/v1/projects/33042/tickets/476834.xml', @headers, fixture_for('tickets/476834'), 200
10
+ end
11
+ @project = taskmapper.project(project_id)
12
+ @ticket = @project.ticket(ticket_id)
13
+ end
14
+ let(:project_id) { 33042 }
15
+ let(:ticket_id) { 476834 }
16
+ let(:taskmapper) { TaskMapper.new(:unfuddle, :account => 'taskmapper', :password => '000000', :username => 'foo') }
17
+ let(:comment_class) { TaskMapper::Provider::Unfuddle::Comment }
18
+
19
+ describe "Retrieving all comments" do
20
+ before(:each) do
21
+ ActiveResource::HttpMock.respond_to do |mock|
22
+ mock.get '/api/v1/projects/33042/tickets/476834/comments.xml', @headers, fixture_for('comments'), 200
23
+ mock.get '/api/v1/projects/33042/tickets/476834/comments/0.xml', @headers, fixture_for('comments/0'), 200
24
+ mock.get '/api/v1/projects/33042/tickets/476834/comments/2.xml', @headers, fixture_for('comments/2'), 200
25
+ mock.get '/api/v1/projects/33042/tickets/476834/comments/3.xml', @headers, fixture_for('comments/3'), 200
26
+ end
27
+ end
28
+
29
+ context "when calling #comments to a ticket instance" do
30
+ subject { @ticket.comments }
31
+ it { should be_an_instance_of Array }
32
+ it { subject.first.should be_an_instance_of comment_class }
33
+ end
34
+
35
+ context "when calling #comments with an array of id's" do
36
+ subject { @ticket.comments([0,2,3]) }
37
+ it { should be_an_instance_of Array }
38
+ it { subject.first.id.should be_eql 0 }
39
+ it { subject.last.id.should be_eql 3 }
40
+ end
41
+
42
+ context "when calling #comments with a hash of attributes" do
43
+ subject { @ticket.comments :parent_id => @ticket.id }
44
+ it { should be_an_instance_of Array }
45
+ it { subject.first.should be_an_instance_of comment_class }
46
+ it { subject.first.id.should be_eql 2 }
47
+ end
48
+ end
49
+
50
+ describe "Retrieve a single comment" do
51
+ before(:each) do
52
+ ActiveResource::HttpMock.respond_to do |mock|
53
+ mock.get '/api/v1/projects/33042/tickets/476834/comments/0.xml', @headers, fixture_for('comments/0'), 200
54
+ mock.get '/api/v1/projects/33042/tickets/476834/comments.xml', @headers, fixture_for('comments'), 200
55
+ end
56
+ end
57
+
58
+ context "when calling #comment with an id" do
59
+ subject { @ticket.comment 0 }
60
+ it { should be_an_instance_of comment_class }
61
+ it { subject.id.should be_eql 0 }
62
+ end
63
+
64
+ context "when calling #comment with an attribute hash" do
65
+ subject { @ticket.comment :parent_id => @ticket.id }
66
+ it { should be_an_instance_of comment_class }
67
+ it { subject.id.should be_eql 2 }
68
+ end
69
+ end
70
+
71
+ it "should be able to create a comment" do
72
+ pending
73
+ @comment = @ticket.comment!(:body => 'New comment created.', :body_format => 'markdown')
74
+ @comment.should be_an_instance_of(@klass)
75
+ end
76
+ end
@@ -0,0 +1,11 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <comment>
3
+ <author-id type="integer">47763</author-id>
4
+ <body>I found Devise as a very complete solution for authentication, also found that it takes care of sessions expiration(Timeoutable) and sign in tracking(Trackable) too (http://github.com/plataformatec/devise), and the better is that it seems not to be a very difficult task to get it implemented in the app.</body>
5
+ <body-format>markdown</body-format>
6
+ <id type="integer">0</id>
7
+ <parent-id type="integer">476834</parent-id>
8
+ <parent-type>Ticket</parent-type>
9
+ <created-at>2009-11-26T17:19:22Z</created-at>
10
+ <updated-at>2009-11-26T17:19:22Z</updated-at>
11
+ </comment>
@@ -0,0 +1,11 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <comment>
3
+ <author-id type="integer">47763</author-id>
4
+ <body>This is comment 2.</body>
5
+ <body-format>markdown</body-format>
6
+ <id type="integer">2</id>
7
+ <parent-id type="integer">476834</parent-id>
8
+ <parent-type>Ticket</parent-type>
9
+ <created-at>2009-11-26T17:19:22Z</created-at>
10
+ <updated-at>2009-11-26T17:19:22Z</updated-at>
11
+ </comment>
@@ -0,0 +1,11 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <comment>
3
+ <author-id type="integer">47763</author-id>
4
+ <body>This is comment 3.</body>
5
+ <body-format>markdown</body-format>
6
+ <id type="integer">3</id>
7
+ <parent-id type="integer">476834</parent-id>
8
+ <parent-type>Ticket</parent-type>
9
+ <created-at>2009-11-26T17:19:22Z</created-at>
10
+ <updated-at>2009-11-26T17:19:22Z</updated-at>
11
+ </comment>
@@ -0,0 +1,11 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <comment>
3
+ <author-id type="integer">47763</author-id>
4
+ <body>New comment created.</body>
5
+ <body-format>markdown</body-format>
6
+ <id type="integer">4</id>
7
+ <parent-id type="integer">476834</parent-id>
8
+ <parent-type>Ticket</parent-type>
9
+ <created-at>2009-11-26T17:19:22Z</created-at>
10
+ <updated-at>2009-11-26T17:19:22Z</updated-at>
11
+ </comment>
@@ -0,0 +1,13 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <comments type="array">
3
+ <comment>
4
+ <author-id type="integer">47763</author-id>
5
+ <body>I found Devise as a very complete solution for authentication, also found that it takes care of sessions expiration(Timeoutable) and sign in tracking(Trackable) too (http://github.com/plataformatec/devise), and the better is that it seems not to be a very difficult task to get it implemented in the app.</body>
6
+ <body-format>markdown</body-format>
7
+ <id type="integer">2</id>
8
+ <parent-id type="integer">476834</parent-id>
9
+ <parent-type>Ticket</parent-type>
10
+ <created-at>2009-11-26T17:19:22Z</created-at>
11
+ <updated-at>2009-11-26T17:19:22Z</updated-at>
12
+ </comment>
13
+ </comments>