taskmapper 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. data/.document +5 -0
  2. data/.travis.yml +4 -0
  3. data/Gemfile +12 -0
  4. data/Gemfile.lock +52 -0
  5. data/LICENSE +20 -0
  6. data/NOTES +18 -0
  7. data/README.md +296 -0
  8. data/Rakefile +40 -0
  9. data/TODO +1 -0
  10. data/VERSION +1 -0
  11. data/bin/tm +7 -0
  12. data/examples/tm_example.rb +11 -0
  13. data/examples/tm_example_2.rb +11 -0
  14. data/examples/tm_example_3.rb +21 -0
  15. data/examples/tm_example_4.rb +17 -0
  16. data/lib/taskmapper.rb +53 -0
  17. data/lib/taskmapper/authenticator.rb +2 -0
  18. data/lib/taskmapper/cli/commands/config.rb +100 -0
  19. data/lib/taskmapper/cli/commands/console.rb +35 -0
  20. data/lib/taskmapper/cli/commands/generate.rb +112 -0
  21. data/lib/taskmapper/cli/commands/generate/provider.rb +5 -0
  22. data/lib/taskmapper/cli/commands/generate/provider/comment.rb +14 -0
  23. data/lib/taskmapper/cli/commands/generate/provider/project.rb +26 -0
  24. data/lib/taskmapper/cli/commands/generate/provider/provider.rb +25 -0
  25. data/lib/taskmapper/cli/commands/generate/provider/ticket.rb +12 -0
  26. data/lib/taskmapper/cli/commands/help.rb +9 -0
  27. data/lib/taskmapper/cli/commands/help/config +27 -0
  28. data/lib/taskmapper/cli/commands/help/console +13 -0
  29. data/lib/taskmapper/cli/commands/help/generate +19 -0
  30. data/lib/taskmapper/cli/commands/help/help +7 -0
  31. data/lib/taskmapper/cli/commands/help/project +13 -0
  32. data/lib/taskmapper/cli/commands/help/ticket +14 -0
  33. data/lib/taskmapper/cli/commands/project.rb +140 -0
  34. data/lib/taskmapper/cli/commands/ticket.rb +145 -0
  35. data/lib/taskmapper/cli/common.rb +28 -0
  36. data/lib/taskmapper/cli/init.rb +77 -0
  37. data/lib/taskmapper/comment.rb +97 -0
  38. data/lib/taskmapper/common.rb +81 -0
  39. data/lib/taskmapper/dummy/comment.rb +27 -0
  40. data/lib/taskmapper/dummy/dummy.rb +28 -0
  41. data/lib/taskmapper/dummy/project.rb +42 -0
  42. data/lib/taskmapper/dummy/ticket.rb +43 -0
  43. data/lib/taskmapper/exception.rb +2 -0
  44. data/lib/taskmapper/helper.rb +72 -0
  45. data/lib/taskmapper/project.rb +145 -0
  46. data/lib/taskmapper/provider.rb +82 -0
  47. data/lib/taskmapper/tester/comment.rb +18 -0
  48. data/lib/taskmapper/tester/project.rb +19 -0
  49. data/lib/taskmapper/tester/tester.rb +28 -0
  50. data/lib/taskmapper/tester/ticket.rb +19 -0
  51. data/lib/taskmapper/ticket.rb +154 -0
  52. data/spec/project_spec.rb +84 -0
  53. data/spec/rcov.opts +1 -0
  54. data/spec/spec.opts +1 -0
  55. data/spec/spec_helper.rb +9 -0
  56. data/spec/taskmapper-cli_spec.rb +60 -0
  57. data/spec/taskmapper-exception_spec.rb +160 -0
  58. data/spec/taskmapper_spec.rb +13 -0
  59. data/spec/ticket_spec.rb +56 -0
  60. data/taskmapper.gemspec +118 -0
  61. metadata +189 -0
@@ -0,0 +1,97 @@
1
+ module TaskMapper::Provider
2
+ module Base
3
+ # The comment class
4
+ #
5
+ # This will probably one of the most troublesome parts of creating a provider for taskmapper
6
+ # since there are so many different ways comments are handled by different APIs.
7
+ # Keep in mind that if you do need to change/overwrite the methods, you will most likely
8
+ # only need to overwrite #find_by_id, #find_by_attributes, #search, and possibly #initialize
9
+ # as long as their parameters conform to what the find method expects.
10
+ #
11
+ # Here are the expected attributes:
12
+ #
13
+ # * author
14
+ # * body
15
+ # * id
16
+ # * created_at
17
+ # * updated_at
18
+ # * ticket_id
19
+ # * project_id
20
+ class Comment < Hashie::Mash
21
+ include TaskMapper::Provider::Common
22
+ include TaskMapper::Provider::Helper
23
+ extend TaskMapper::Provider::Helper
24
+ attr_accessor :system, :system_data
25
+ API = nil # Replace with your comment's API's Class
26
+
27
+ # Find comment
28
+ # You can also retrieve an array of all comments by not specifying any query.
29
+ #
30
+ # * find(<project_id>, <ticket_id>) or find(<project_id>, <ticket_id>, :all) - Returns an array of all comments
31
+ # * find(<project_id>, <ticket_id>, ##) - Returns a comment based on that id or some other primary (unique) attribute
32
+ # * find(<project_id>, <ticket_id>, [#, #, #]) - Returns many comments with these ids
33
+ # * find(<project_id>, <ticket_id>, :first, :name => 'Project name') - Returns the first comment based on the comment's attribute(s)
34
+ # * find(<project_id>, <ticket_id>, :last, :name => 'Some project') - Returns the last comment based on the comment's attribute(s)
35
+ # * find(<project_id>, <ticket_id>, :all, :name => 'Test Project') - Returns all comments based on the given attribute(s)
36
+ def self.find(project_id, ticket_id, *options)
37
+ first, attributes = options
38
+ if first.nil? or (first == :all and attributes.nil?)
39
+ self.find_by_attributes(project_id, ticket_id)
40
+ elsif first.is_a? Array
41
+ first.collect { |id| self.find_by_id(project_id, ticket_id, id) }
42
+ elsif first == :first
43
+ comments = attributes.nil? ? self.find_by_attributes(project_id, ticket_id) : self.find_by_attributes(project_id, ticket_id, attributes)
44
+ comments.first
45
+ elsif first == :last
46
+ comments = attributes.nil? ? self.find_by_attributes(project_id, ticket_id) : self.find_by_attributes(project_id, ticket_id, attributes)
47
+ comments.last
48
+ elsif first == :all
49
+ self.find_by_attributes(project_id, ticket_id, attributes)
50
+ else
51
+ self.find_by_id(project_id, ticket_id, first)
52
+ end
53
+ end
54
+
55
+ # The first of whatever comment
56
+ def self.first(project_id, ticket_id, *options)
57
+ self.find(project_id, ticket_id, :first, *options)
58
+ end
59
+
60
+ # The last of whatever comment
61
+ def self.last(project_id, ticket_id, *options)
62
+ self.find(project_id, ticket_id, :last, *options)
63
+ end
64
+
65
+ # Accepts an integer id and returns the single comment instance
66
+ # Must be defined by the provider
67
+ def self.find_by_id(project_id, ticket_id, id)
68
+ if self::API.is_a? Class
69
+ self.new self::API.find(id, :params => {:project_id => project_id, :ticket_id => ticket_id})
70
+ else
71
+ raise TaskMapper::Exception.new("#{self.name}::#{this_method} method must be implemented by the provider")
72
+ end
73
+ end
74
+
75
+ # Accepts an attributes hash and returns all comments matching those attributes in an array
76
+ # Should return all comments if the attributes hash is empty
77
+ # Must be defined by the provider
78
+ def self.find_by_attributes(project_id, ticket_id, attributes = {})
79
+ if self::API.is_a? Class
80
+ self.search(project_id, ticket_id, attributes)
81
+ else
82
+ raise TaskMapper::Exception.new("#{self.name}::#{this_method} method must be implemented by the provider")
83
+ end
84
+ end
85
+
86
+ # This is a helper method to find
87
+ def self.search(project_id, ticket_id, options = {}, limit = 1000)
88
+ if self::API.is_a? Class
89
+ comments = self::API.find(:all, :params => {:project_id => project_id, :ticket_id => ticket_id}).collect { |comment| self.new comment }
90
+ search_by_attribute(comments, options, limit)
91
+ else
92
+ raise TaskMapper::Exception.new("#{self.name}::#{this_method} method must be implemented by the provider")
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,81 @@
1
+ module TaskMapper::Provider
2
+ # Common module
3
+ # contains method definitions common to all or most of the models
4
+ module Common
5
+ module ClassMethods
6
+ # Create a something.
7
+ # Basically, a .new and .save in the same call. The default method assumes it is passed a
8
+ # single hash with attribute information
9
+ def create(*options)
10
+ if self::API.is_a? Class
11
+ something = self::API.new(*options)
12
+ something.save
13
+ self.new something
14
+ else
15
+ raise TaskMapper::Exception.new("#{self.name}::#{this_method} method must be implemented by the provider")
16
+ end
17
+ end
18
+ end
19
+
20
+ # Automatic extension of class methods on include
21
+ def self.included(base)
22
+ base.extend ClassMethods
23
+ end
24
+
25
+ # The initializer
26
+ # It tries to do a default system for ActiveResource-based api providers
27
+ def initialize(*options)
28
+ @system_data ||= {}
29
+ @cache ||= {}
30
+ first = options.shift
31
+ case first
32
+ when Hash
33
+ super(first.to_hash)
34
+ else
35
+ @system_data[:client] = first
36
+ self.prefix_options ||= @system_data[:client].prefix_options if @system_data[:client].prefix_options
37
+ super(first.attributes)
38
+ end
39
+ end
40
+
41
+ # Update the something and save
42
+ # As opposed to update which just updates the attributes
43
+ def update!(*options)
44
+ update(*options)
45
+ save
46
+ end
47
+
48
+ # Save changes to this project
49
+ # Returns true (success) or false (failure) or nil (no changes)
50
+ def save
51
+ if @system_data and (something = @system_data[:client]) and something.respond_to?(:attributes)
52
+ changes = 0
53
+ something.attributes.each do |k, v|
54
+ if self.send(k) != v
55
+ something.send(k + '=', self.send(k))
56
+ changes += 1
57
+ end
58
+ end
59
+ something.save if changes > 0
60
+ else
61
+ raise TaskMapper::Exception.new("#{self.class.name}::#{this_method} method must be implemented by the provider")
62
+ end
63
+ end
64
+
65
+ # Delete this project
66
+ # Returns true (success) or false(failure)
67
+ def destroy
68
+ if @system_data and @system_data[:client] and @system_data[:client].respond_to?(:destroy)
69
+ return @system_data[:client].destroy
70
+ else
71
+ raise TaskMapper::Exception.new("#{self.class.name}::#{this_method} method must be implemented by the provider")
72
+ end
73
+ end
74
+
75
+ def respond_to?(symbol, include_private = false)
76
+ result = super(symbol)
77
+ return true if result or @system_data.nil? or @system_data[:client].nil?
78
+ @system_data[:client].respond_to?(symbol, include_private)
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,27 @@
1
+ module TaskMapper::Provider
2
+ module Dummy
3
+ # This is the Comment class for the Dummy provider
4
+ class Comment < TaskMapper::Provider::Base::Comment
5
+
6
+
7
+ def self.find_by_id(id)
8
+ self.new({:id => id})
9
+ end
10
+
11
+ def self.find_by_attributes(*options)
12
+ [self.new(*options)]
13
+ end
14
+
15
+ # You don't need to define an initializer, this is only here to initialize dummy data
16
+ def initialize(project_id, ticket_id, *options)
17
+ data = {:id => rand(1000), :status => ['lol', 'rofl', 'lmao', 'lamo', 'haha', 'heh'][rand(6)],
18
+ :priority => rand(10), :summary => 'Tickets ticket ticket ticket', :resolution => false,
19
+ :created_at => Time.now, :updated_at => Time.now, :description => 'Ticket ticket ticket ticket laughing',
20
+ :assignee => 'lol-man'}
21
+ @system = :dummy
22
+ super(data.merge(options.first || {}))
23
+ end
24
+
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,28 @@
1
+ module TaskMapper::Provider
2
+ # This is the Dummy Provider
3
+ #
4
+ # It doesn't really do anything, it exists in order to test the basic functionality of taskmapper
5
+ # and to provide an example the basics of what is to be expected from the providers.
6
+ #
7
+ # Note that the initial provider name is a module rather than a class. TaskMapper.new
8
+ # extends on an instance-based fashion. If you would rather initialize using code that is
9
+ # closer to:
10
+ #
11
+ # TaskMapper::Provider::Dummy.new(authentication)
12
+ #
13
+ # You will have to do a little magic trick and define new on the provider as a wrapper
14
+ # around the TaskMapper.new call.
15
+ module Dummy
16
+ include TaskMapper::Provider::Base
17
+ # An example of what to do if you would like to do TaskMapper::Provider::Dummy.new(...)
18
+ # rather than TaskMapper.new(:dummy, ...)
19
+ def self.new(authentication = {})
20
+ TaskMapper.new(:dummy, authentication)
21
+ # maybe do some other stuff
22
+ end
23
+ end
24
+ end
25
+
26
+ %w| project ticket comment |.each do |f|
27
+ require File.dirname(__FILE__) + '/' + f +'.rb'
28
+ end
@@ -0,0 +1,42 @@
1
+ module TaskMapper::Provider
2
+ module Dummy
3
+ # This is the Project class for the Dummy provider
4
+ class Project < TaskMapper::Provider::Base::Project
5
+
6
+ def self.find_by_id(id)
7
+ self.new({:id => id})
8
+ end
9
+
10
+ def self.find_by_attributes(*options)
11
+ [self.new(*options)]
12
+ end
13
+
14
+ def self.create(*attributes)
15
+ self.new(*attributes)
16
+ end
17
+
18
+ # You should define @system and @system_data here.
19
+ # The data stuff is just to initialize fake data. In a real provider, you would use the API
20
+ # to grab the information and then initialize based on that info.
21
+ # @system_data would hold the API's model/instance for reference
22
+ def initialize(*options)
23
+ data = {:id => rand(1000).to_i, :name => 'Dummy', :description => 'Mock!-ing Bird',
24
+ :created_at => Time.now, :updated_at => Time.now}
25
+ @system = :dummy
26
+ super(data.merge(options.first || {}))
27
+ end
28
+
29
+ # Nothing to save so we always return true
30
+ # ...unless it's an odd numbered second on Friday the 13th. muhaha!
31
+ def save
32
+ time = Time.now
33
+ !(time.wday == 5 and time.day == 13 and time.to_i % 2 == 1)
34
+ end
35
+
36
+ # Nothing to update, so we always return true
37
+ def update!(*options)
38
+ return true
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,43 @@
1
+ module TaskMapper::Provider
2
+ module Dummy
3
+ # The Dummy Provider's Ticket class
4
+ class Ticket < TaskMapper::Provider::Base::Ticket
5
+ @system = :dummy
6
+
7
+ def self.find_by_id(project_id, ticket_id)
8
+ self.new(project_id, {:id => ticket_id})
9
+ end
10
+
11
+ def self.find_by_attributes(*ticket_attributes)
12
+ [self.new(*ticket_attributes)]
13
+ end
14
+
15
+ # You don't need to define an initializer, this is only here to initialize dummy data
16
+ def initialize(project_id, *options)
17
+ data = {:id => rand(1000), :status => ['lol', 'rofl', 'lmao', 'lamo', 'haha', 'heh'][rand(6)],
18
+ :priority => rand(10), :summary => 'Tickets ticket ticket ticket', :resolution => false,
19
+ :created_at => Time.now, :updated_at => Time.now, :description => 'Ticket ticket ticket ticket laughing',
20
+ :assignee => 'lol-man', :project_id => project_id}
21
+ @system = :dummy
22
+ super(data.merge(options.first || {}))
23
+ end
24
+
25
+ # Nothing to save so we always return true
26
+ # ...unless it's the Ides of March and the second is divisible by three. muhaha!
27
+ def save
28
+ time = Time.now
29
+ !(time.wday == 15 and time.day == 3 and time.to_i % 3 == 0)
30
+ end
31
+
32
+ # Nothing to close, so we always return true
33
+ def close
34
+ true
35
+ end
36
+
37
+ # Nothing to destroy so we always return true
38
+ def destroy
39
+ true
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,2 @@
1
+ class TaskMapper::Exception < Exception
2
+ end
@@ -0,0 +1,72 @@
1
+ module TaskMapper::Provider
2
+ # Contains a series of helper methods
3
+ module Helper
4
+
5
+ # Current method name reflection
6
+ # http://www.ruby-forum.com/topic/75258#895630
7
+ # Call when raising 'implemented by the provider' exceptions
8
+ def this_method
9
+ caller[0][/`([^']*)'/, 1]
10
+ end
11
+
12
+ # A helper method for easy finding
13
+ def easy_finder(api, symbol, options, at_index = 0)
14
+ if api.is_a? Class
15
+ return api if options.length == 0 and symbol == :first
16
+ options.insert(at_index, symbol) if options[at_index].is_a?(Hash)
17
+ api.find(*options)
18
+ else
19
+ raise TaskMapper::Exception.new("#{Helper.name}::#{this_method} method must be implemented by the provider")
20
+ end
21
+ end
22
+
23
+ # This is a search filter that all parameters are passed through
24
+ # Redefine this method in your classes if you need specific functionality
25
+ def search_filter(k)
26
+ k
27
+ end
28
+
29
+ def provider_parent(klass)
30
+ TaskMapper::Provider.const_get(klass.to_s.split('::')[-2])
31
+ end
32
+
33
+ # Goes through all the things and returns results that match the options attributes hash
34
+ def search_by_attribute(things, options = {}, limit = 1000)
35
+ things.find_all do |thing|
36
+ options.inject(true) do |memo, kv|
37
+ break unless memo
38
+ key, value = kv
39
+ begin
40
+ memo &= thing.send(key) == value
41
+ rescue NoMethodError
42
+ memo = false
43
+ end
44
+ memo
45
+ end and (limit -= 1) > 0
46
+ end
47
+ end
48
+
49
+ # Returns a filter-like string from a hash
50
+ # If array_join is given, arrays are joined rather than having their own separated key:values
51
+ #
52
+ # ex: filter_string({:name => 'some value', :tags => ['abc', 'def']}) = "name:'some value' tag:abc tag:def"
53
+ def filter_string(filter = {}, array_join = nil)
54
+ filter.inject('') do |mem, kv|
55
+ key, value = kv
56
+ if value.is_a?(Array)
57
+ if !array_join.nil?
58
+ mem += value.inject('') { |m, v|
59
+ v = "\"#{v}\"" if v.to_s.include?(' ')
60
+ m+= "#{key}:#{v}"
61
+ }
62
+ return mem
63
+ else
64
+ value = value.join(array_join)
65
+ end
66
+ end
67
+ value = "\"#{value}\"" if value.to_s.include?(' ')
68
+ mem += "#{key}:#{value} "
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,145 @@
1
+ module TaskMapper::Provider
2
+ module Base
3
+ # This is the base Project class for providers
4
+ #
5
+ # Providers should inherit this class and redefine the methods
6
+ #
7
+ # Each provider should have their own @system defined.
8
+ # For example, taskmapper-unfuddle's @system is :unfuddle and taskmapper-lighthouse's
9
+ # @system is :lighthouse.
10
+ #
11
+ # Methods that must be implemented by the provider
12
+ #
13
+ # * self.find_by_id
14
+ # * self.find_by_attributes
15
+ #
16
+ # Methods that might need to be implemented by the provider
17
+ # * tickets
18
+ # * ticket
19
+ # * initialize
20
+ # * update
21
+ # * destroy
22
+ # * self.create
23
+ #
24
+ # Methods that would probably be okay if the provider left it alone:
25
+ #
26
+ # * self.find - although you can define your own to optimize it a bit
27
+ # * update!
28
+ #
29
+ # A provider should define as many attributes as feasibly possible. The list below are
30
+ # some guidelines as to what attributes are necessary, if your provider's api does not
31
+ # implement them, point it to an attribute that is close to it. (for example, a name
32
+ # can point to title. Remember to alias it in your class!)
33
+ #
34
+ # * id
35
+ # * name
36
+ # * created_at
37
+ # * updated_at
38
+ # * description
39
+ class Project < Hashie::Mash
40
+ include TaskMapper::Provider::Common
41
+ include TaskMapper::Provider::Helper
42
+ extend TaskMapper::Provider::Helper
43
+ attr_accessor :system, :system_data
44
+ API = nil #Replace with your api digestor's class.
45
+
46
+ # Find project
47
+ # You can also retrieve an array of all projects by not specifying any query.
48
+ #
49
+ # * find() or find(:all) - Returns an array of all projects
50
+ # * find(##) - Returns a project based on that id or some other primary (unique) attribute
51
+ # * find([#, #, #]) - Returns many projects with these ids
52
+ # * find(:first, :name => 'Project name') - Returns the first project based on the project's attribute(s)
53
+ # * find(:last, :name => 'Some project') - Returns the last project based on the project's attribute(s)
54
+ # * find(:all, :name => 'Test Project') - Returns all projects based on the given attribute(s)
55
+ def self.find(*options)
56
+ first = options.shift
57
+ attributes = options.shift
58
+ if first.nil? or (first == :all and attributes.nil?)
59
+ self.find_by_attributes
60
+ elsif first.is_a? Array
61
+ first.collect { |id| self.find_by_id(id) }
62
+ elsif first == :first
63
+ projects = attributes.nil? ? self.find_by_attributes : self.find_by_attributes(attributes)
64
+ projects.first
65
+ elsif first == :last
66
+ projects = attributes.nil? ? self.find_by_attributes : self.find_by_attributes(attributes)
67
+ projects.last
68
+ elsif first == :all
69
+ self.find_by_attributes(attributes)
70
+ else
71
+ self.find_by_id(first)
72
+ end
73
+ end
74
+
75
+ # The first of whatever project
76
+ def self.first(*options)
77
+ self.find(:first, *options)
78
+ end
79
+
80
+ # The last of whatever project
81
+ def self.last(*options)
82
+ self.find(:last, *options)
83
+ end
84
+
85
+ # Accepts an integer id and returns the single project instance
86
+ # Must be defined by the provider
87
+ def self.find_by_id(id)
88
+ if self::API.is_a? Class
89
+ self.new self::API.find(id)
90
+ else
91
+ raise TaskMapper::Exception.new("#{self.name}::#{this_method} method must be implemented by the provider")
92
+ end
93
+ end
94
+
95
+ # Accepts an attributes hash and returns all projects matching those attributes in an array
96
+ # Should return all projects if the attributes hash is empty
97
+ # Must be defined by the provider
98
+ def self.find_by_attributes(attributes = {})
99
+ if self::API.is_a? Class
100
+ self.search(attributes)
101
+ else
102
+ raise TaskMapper::Exception.new("#{self.name}::#{this_method} method must be implemented by the provider")
103
+ end
104
+ end
105
+
106
+ # This is a helper method to find
107
+ def self.search(options = {}, limit = 1000)
108
+ if self::API.is_a? Class
109
+ projects = self::API.find(:all).collect { |project| self.new project }
110
+ search_by_attribute(projects, options, limit)
111
+ else
112
+ raise TaskMapper::Exception.new("#{self.name}::#{this_method} method must be implemented by the provider")
113
+ end
114
+ end
115
+
116
+ # Asks the provider's api for the tickets associated with the project,
117
+ # returns an array of Ticket objects.
118
+ def tickets(*options)
119
+ options.insert 0, id
120
+ easy_finder(provider_parent(self.class)::Ticket, :all, options, 1)
121
+ end
122
+
123
+ # Very similar to tickets, and is practically an alias of it
124
+ # however this returns the ticket class if no parameter is given
125
+ # unlike tickets which returns an array of all tickets when given no parameters
126
+ def ticket(*options)
127
+ options.insert(0, id) if options.length > 0
128
+ easy_finder(provider_parent(self.class)::Ticket, :first, options, 1)
129
+ end
130
+
131
+ # Create a ticket
132
+ def ticket!(*options)
133
+ options[0].merge!(:project_id => id) if options.first.is_a?(Hash)
134
+ provider_parent(self.class)::Ticket.create(*options)
135
+ end
136
+
137
+ # Define some provider specific initalizations
138
+ def initialize(*options)
139
+ # @system_data = {'some' => 'data}
140
+ super(*options)
141
+ end
142
+
143
+ end
144
+ end
145
+ end