rtasklib 0.1.0 → 0.1.1.pre.alpha

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3c60a1881ed6632325dec8f7b90b7945bd04e1d9
4
- data.tar.gz: 0f9414694814029b834ab9031828bc0b6b56f1b2
3
+ metadata.gz: cbef69d8085f3de726027c8c93d277d1b5e95e6b
4
+ data.tar.gz: efc01dfeb85579ba8539ea68df1ccf216684ea10
5
5
  SHA512:
6
- metadata.gz: b78ea752b977227baced57676296360a5349c300ca84d7136d0b25dcfc8039fa1bea2ff8e10fbb6d56bc5afd82c95ba1820bb79fa090850e151a9ce6b71b69fe
7
- data.tar.gz: 26217f53ae87e4cda5cead70454adec4a07f10900435acc8cb9188f4050d6670b08d33fe646558c8a81bda3958193c7f534a5930ebd34acc4681f26042ca3c86
6
+ metadata.gz: ca2de133e12b01df7a4403948b946b9e018447921ea384714fc3c1a6e824e33e2175179b324bb9210bef56817500ca05e0378dd3ba5d093c9986de310477b760
7
+ data.tar.gz: 5e487152aa6a0a98b6d615e5bca94d10123d3eee416da29f35e98eaa55e7f567d528a5c600ae958aa83d306c79195fe8fe36e6aaa8466c05fa44d4bf753e67be
data/.coveralls.yml ADDED
@@ -0,0 +1 @@
1
+ service_name: travis-ci
data/.gitignore CHANGED
@@ -3,7 +3,10 @@
3
3
  /Gemfile.lock
4
4
  /_yardoc/
5
5
  /coverage/
6
- /doc/
7
6
  /pkg/
7
+ /doc/
8
8
  /spec/reports/
9
9
  /tmp/
10
+ *.py
11
+ lib/rtasklib/marshallable.rb
12
+ spec/marshallable_spec.rb
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ ruby-2.1.3
data/.travis.yml CHANGED
@@ -1,3 +1,32 @@
1
1
  language: ruby
2
+
2
3
  rvm:
3
- - 2.0.0
4
+ - 2.1.3
5
+
6
+ env:
7
+ - TASKWARRIOR=v2.4.1
8
+
9
+ before_install:
10
+ - gem install bundler
11
+ - sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y
12
+ - sudo apt-get update -qq
13
+ - sudo apt-get install -qq build-essential cmake uuid-dev g++-4.8
14
+ - sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.8 50
15
+ - git clone https://git.tasktools.org/scm/tm/task.git
16
+ - cd task
17
+ - git checkout $TASK_VERSION
18
+ - git clean -dfx
19
+ - cmake .
20
+ - make
21
+ - sudo make install
22
+ - pwd
23
+ - cd ../
24
+ - ls /home/travis/build/dropofwill/rtasklib/spec/data/.task
25
+ - export TASKRC=/home/travis/build/dropofwill/rtasklib/spec/data/.taskrc
26
+ - export TASKDATA=/home/travis/build/dropofwill/rtasklib/spec/data/.task
27
+ - task _version
28
+
29
+ script: 'bundle exec rake'
30
+
31
+ notifications:
32
+ email: false
data/Guardfile ADDED
@@ -0,0 +1,11 @@
1
+ guard 'rspec', cmd: "bundle exec rspec --color --format=doc --format=Nc" do
2
+ # watch /lib/ files
3
+ watch(%r{^lib/(.+).rb$}) do |m|
4
+ "spec/#{m[1]}_spec.rb"
5
+ end
6
+
7
+ # watch /spec/ files
8
+ watch(%r{^spec/(.+).rb$}) do |m|
9
+ "spec/#{m[1]}.rb"
10
+ end
11
+ end
data/PLAN.md ADDED
@@ -0,0 +1,204 @@
1
+ # rtasklib
2
+
3
+ ## Public API
4
+
5
+
6
+ rtasklib::
7
+
8
+ For now require `task --version` > 2.4.0, we can work on backwards
9
+ compatibility later.
10
+
11
+ ### TaskWarrior Direct
12
+
13
+ * #filter(filter_string)
14
+ just send random shit to task and handle the probable errors properly
15
+ `task #{filter_string} export`
16
+
17
+ * add(
18
+
19
+ ### ActiveRecord API
20
+
21
+ #### Required
22
+
23
+ * #where()
24
+ takes string or hash arguments
25
+ strings get past directly to `task <filter> export`
26
+ returns a chainable relationship, instead of the raw objects of #find
27
+
28
+ * #find(uuid | id)
29
+ #find([uuid | id, ...])
30
+
31
+ * #take(num=1)
32
+
33
+ * #first(num=1)
34
+ #last(num=1)
35
+ Num is how many to return
36
+ Should this be by urgency or id or uuid?
37
+
38
+ * #find_by(key: value)
39
+ e.g. #find_by(project: "LinuxDev") which is the same as
40
+ #where(project: "LinuxDev").take or .first
41
+
42
+ #find_by!(key: value) is the same, but throws an error
43
+
44
+ * #all()
45
+
46
+ * #order()
47
+ either sql style string or AR style hash:
48
+ "created at DESC" or created_at: :desc
49
+ Chainable on relations from #where
50
+
51
+ * #select() alias #project()
52
+ same as #order()
53
+ Only returns the listed columns
54
+
55
+ * #distinct()
56
+ same as #take() but for selection/projections
57
+
58
+ * #limit(num)
59
+ maximum number of rows to return
60
+
61
+ * #offset(num)
62
+ change the starting point of a query
63
+
64
+ * #readonly()
65
+
66
+ * #find_or_create_by(), #find_or_create_by!()
67
+
68
+ * #count alias #size, #length
69
+
70
+ * #pluck()
71
+
72
+ * #exists?()
73
+
74
+ * #explain() show underlying command structure
75
+
76
+
77
+ #### Probs should, but nah
78
+
79
+ * Optimistic or Pessimistic locking to prevent race conditions
80
+
81
+ * Eager loading
82
+
83
+ * Scopes
84
+
85
+
86
+ #### Maybe
87
+
88
+ * #average(), #minimum(), #maximum(), #sum()
89
+
90
+ * #none(), returns an empty relation, useful in chains perhaps?
91
+
92
+ * #find_each(), #find_in_batches()
93
+ load all tasks in in batches, unnecessary for the task interface?
94
+
95
+ * #joins()? Maybe for dependencies?
96
+
97
+ * #group()? Out of scope probably? Needs a motivating use case.
98
+
99
+ * #having()? Require group to be working first
100
+
101
+ * #unscope, #only, #reorder, #reverse_order, #rewhere
102
+ These exist for performance reasons in SQL, probably not necessary for us.
103
+
104
+
105
+
106
+ ## [TaskWarrior 3rd-Party Guidelines]()
107
+
108
+ Taskwarrior can be extended by means of a third-party application. There are script examples of import and export add-ons that support many different formats (clone the repository, look in task.git/scripts/add-ons). Then there are more sophisticated applications such as Vit that provide a complete replacement UI.
109
+
110
+ All of these provide interesting new features and improve ease of use for different kinds of users. We encourage you to create such add-ons, but in doing so, there are some rules that must be followed, which will not only protect the users data from mistreatment, but also your application from being sensitive to changes in Taskwarrior.
111
+
112
+ ### Rules
113
+
114
+ * Produce, consume and handle UTF8 text properly. UTF8 is the only text encoding supported by Taskwarrior.
115
+
116
+ * Don't attempt to parse the pending.data file. Here's why: the .data file format is currently on its fourth version. The very first version was never released, so if you want to read Taskwarrior data properly, you will need to parse the three supported formats. Those formats are not documented. Additionally, you will need to handle the GC operations, implement the task "unwait" feature, observe user defined attribute handling restrictions, and implement recurring task synthesis all of which require .taskrc and default value access. You would essentially be rewriting the data access and configuration portion of Taskwarrior, which is a major undertaking. To support filters you would also need to evaluate the supported clauses, provide DOM access and implement aliases. Then there is also the fifth data format, which is planned...
117
+
118
+ * Use the export command to query data from Taskwarrior. The export command implements filters which you can use, or you can omit a filter, get all the data, and implement your own filtering. JSON parsing is very well supported in all relevant programming languages, which means you should be using Taskwarrior itself to query the data, with a commodity JSON parser in conjunction. While the JSON format will be tweaked over time, the general form will not.
119
+
120
+ * Use the command line interface to put data into Taskwarrior. Composing a valid command line is a simple way to put data in to Taskwarrior, and the ONLY way to modify data in Taskwarrior.
121
+
122
+ * Verify feature support by running task --version. This command returns the version number, which will help you determine whether or not a particular feature is supported. Note that this command does not scan for a configuration file, and is therefore safe to run if Taskwarrior is not yet set up.
123
+
124
+ * UDAs (User Defined Attributes) must be preserved in the data. When reading the JSON for a task, there may be attributes that you have never encountered before. If this is the case, you must not modify them in any way. This not only makes your application future-proof, but allows it to tolerate UDAs from other data sources. It also prevents the Taskserver from stripping out your data.
125
+ Guidelines
126
+
127
+ * If you need to store additional data, consider putting your own data file in the ~/.task directory. Just don't use the file names pending.data, completed.data, backlog.data, undo.data or synch.key.
128
+
129
+ * There are many helper commands designed to assist add-on scripts such as shell completion scripts. These commands all begin with an underscore, see them with this command: `task help | grep ' _'`.
130
+
131
+ * Familiarize yourself with the means of forcing color on or off, disabling word wrapping, disabling bulk operation limitations, disabling confirmation, disabling gc, modifying verbosity and so on. There are ways around almost all the restrictions, and while these don't make sense for regular users, they can be critical for add-on authors.
132
+
133
+ ## Classes
134
+
135
+ ```
136
+ TaskWarriorException => optional?, TaskException
137
+
138
+
139
+ ReadOnlyDictView => external, possibly IceNine
140
+
141
+ Deepcopies data to enforce immutability
142
+
143
+
144
+ SerializingObject => internal, Serializer
145
+
146
+ TaskResource, TaskFilter < SerialObject
147
+
148
+ This is the key user-input -> data step
149
+
150
+ Serializing method should hold the following contract:
151
+ - any empty value (meaning removal of the attribute) is deserialized into a empty string
152
+
153
+ - None denotes a empty value for any attribute
154
+
155
+ Deserializing method should hold the following contract:
156
+ - None denotes an empty value for any attribute (however, this is here as a safeguard, TaskWarrior currently does not export empty-valued attributes) if the attribute is not iterable (e.g. list or set), in which case a empty iterable should be used.
157
+
158
+ Normalizing methods should hold the following contract:
159
+ - They are used to validate and normalize the user input. Any attribute value that comes from the user (during Task initialization, assignign values to Task attributes, or filtering by user-provided values of attributes) is first validated and normalized using the normalize_{key} method.
160
+
161
+ - If validation or normalization fails, normalizer is expected
162
+ to raise ValueError.
163
+
164
+ Normalize/Serialize/Deserialize the following data inputs:
165
+ - timestamps, should be localized, so to UTC before string: '%Y%m%dT%H%M%SZ'
166
+
167
+ - datetimes/dates, should be localized, default time=midnight
168
+
169
+ - annotations, should be an array of hashes
170
+
171
+ - tags, 'blah, blah'.split(',')
172
+
173
+ - depends, uuid
174
+
175
+ - possibly an array of data structure shoehorned into string since UDA doesn't
176
+
177
+ - Possible cool use case for meta-programming here.
178
+
179
+ TaskResource => internal, TaskResource
180
+
181
+ inherits from Serializer
182
+
183
+ TaskFilter => internal, TaskFilter
184
+
185
+ inherits from Serializer
186
+
187
+ TaskAnnotation => internal, TaskAnnotation
188
+
189
+ inherits from TaskResource
190
+
191
+ Task => internal, Task
192
+
193
+ inherits from TaskResource
194
+
195
+ TaskQuerySet => internal, TaskLazyLook | TaskLookup
196
+
197
+ Lazy lookup for a task object
198
+
199
+ TaskWarrior => internal, TaskWarrior
200
+
201
+ The main shebang
202
+
203
+
204
+ ```
data/README.md CHANGED
@@ -1,8 +1,12 @@
1
1
  # Rtasklib
2
2
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/rtasklib`. To experiment with that code, run `bin/console` for an interactive prompt.
3
+ [![Coverage Status](https://travis-ci.org/dropofwill/rtasklib.svg?branch=master)](https://travis-ci.org/dropofwill/rtasklib) [![Coverage Status](https://coveralls.io/repos/dropofwill/rtasklib/badge.svg?branch=master)](https://coveralls.io/r/dropofwill/rtasklib?branch=master) [![yard docs](http://b.repl.ca/v1/yard-docs-blue.png)](http://will-paul.com/rtasklib)
4
+
5
+
6
+ ## Description
7
+
8
+ A Ruby wrapper around the TaskWarrior CLI, based on the Python tasklib. Requires a working TaskWarrior install.
4
9
 
5
- TODO: Delete this and the text above, and describe your gem
6
10
 
7
11
  ## Installation
8
12
 
@@ -20,16 +24,33 @@ Or install it yourself as:
20
24
 
21
25
  $ gem install rtasklib
22
26
 
27
+
28
+ ## Dependencies
29
+
30
+ * Taskwarrior > 2.4 (require custom UDAs, recurrences, and duration data types)
31
+
32
+ * Ruby > 2 (currently untested on older versions)
33
+
34
+ * See `./rtasklib.gemspec` for the latest Ruby dependencies
35
+
36
+
23
37
  ## Usage
24
38
 
25
39
  TODO: Write usage instructions here
26
40
 
41
+
27
42
  ## Development
28
43
 
29
44
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/console` for an interactive prompt that will allow you to experiment.
30
45
 
31
46
  To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release` to create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
32
47
 
48
+
49
+ ## License
50
+
51
+ Release under the MIT License (MIT) Copyright (&copy;) 2015 Will Paul
52
+
53
+
33
54
  ## Contributing
34
55
 
35
56
  1. Fork it ( https://github.com/[my-github-username]/rtasklib/fork )
data/Rakefile CHANGED
@@ -1,10 +1,9 @@
1
1
  require "bundler/gem_tasks"
2
2
  require "rspec/core/rake_task"
3
3
 
4
-
5
4
  # run tests with `rake spec`
6
5
  RSpec::Core::RakeTask.new :spec do |task|
7
- task.rspec_opts = ["--color", "--format", "nested"]
6
+ task.rspec_opts = ["--color", "--format=doc", "--format=Nc"]
8
7
  end
9
8
 
10
9
  task default: :spec
data/bin/console CHANGED
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require "bundler/setup"
4
- require "rtasklib"
5
4
 
6
5
  # You can add fixtures and/or initialization code here to make experimenting
7
6
  # with your gem easier. You can also use a different console, if you like.
8
7
 
9
8
  # (If you use this, don't forget to add pry to your Gemfile!)
10
- # require "pry"
11
- # Pry.start
9
+ require_relative "../lib/rtasklib"
10
+ require "pry"
12
11
 
13
- require "irb"
14
- IRB.start
12
+ tw = Rtasklib::TaskWarrior.new("./spec/data/.task")
13
+
14
+ binding.pry
data/bin/env ADDED
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+
5
+ # You can add fixtures and/or initialization code here to make experimenting
6
+ # with your gem easier. You can also use a different console, if you like.
7
+
8
+ # (If you use this, don't forget to add pry to your Gemfile!)
9
+ require_relative "../lib/rtasklib"
10
+ require "pry"
11
+
12
+ Pry.start
data/bin/setup CHANGED
File without changes
@@ -0,0 +1,50 @@
1
+ require "multi_json"
2
+ require "oj"
3
+
4
+ module Rtasklib
5
+
6
+ module Controller
7
+ extend self
8
+
9
+ def create
10
+ end
11
+
12
+ def update
13
+ end
14
+
15
+ def get
16
+ end
17
+
18
+ def all
19
+ all = []
20
+ Execute.task_popen3(*@override_a, "export") do |i, o, e, t|
21
+ all = MultiJson.load(o.read).map do |x|
22
+ Rtasklib::Models::TaskModel.new(x)
23
+ end
24
+ end
25
+ return all
26
+ end
27
+
28
+ def get_rc
29
+ res = []
30
+ Execute.task_popen3(*@override_a, "_show") do |i, o, e, t|
31
+ o.read.each_line { |l| res.push(l.chomp) }
32
+ end
33
+ Taskrc.new(res, :array)
34
+ end
35
+
36
+ def get_version
37
+ version = nil
38
+ Execute.task_popen3(*@override_a, "_version") do |i, o, e, t|
39
+ version = to_gem_version(o.read.chomp)
40
+ end
41
+ return version
42
+ end
43
+
44
+ # Convert "1.6.2 (adf342jsd)" to Gem::Version object
45
+ def to_gem_version raw
46
+ std_ver = raw.chomp.gsub(' ','.').delete('(').delete(')')
47
+ Gem::Version.new std_ver
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,89 @@
1
+ require "open3"
2
+ require "pty"
3
+ require "expect"
4
+ require "ruby_expect"
5
+ require "stringio"
6
+
7
+ module Rtasklib
8
+
9
+ # How to execute shell commands and capture output
10
+ module Execute
11
+ # so that the methods are available within the modules lookup path
12
+ extend self
13
+
14
+ @@exp_regex = {
15
+ create_rc: %r{Would \s you \s like \s a \s sample \s *.+ \s created, \s
16
+ so \s taskwarrior \s can \s proceed\? \s
17
+ \(yes/no\)}x }
18
+
19
+ # popen versions
20
+ #
21
+ def popen3 program='task', *opts, &block
22
+ execute = opts.unshift(program)
23
+ execute = execute.join(" ")
24
+ p execute
25
+
26
+ Open3.popen3(execute) do |i, o, e, t|
27
+ handle_response(e, t)
28
+ yield(i, o, e, t) if block_given?
29
+ end
30
+ end
31
+
32
+ def task_popen3 *opts, &block
33
+ popen3('task', opts, &block)
34
+ end
35
+
36
+ def each_popen3 program='task', *opts, &block
37
+ popen3(program, *opts) do |i, o, e, t|
38
+ o.each_line do |l|
39
+ yield(l, i, o, e, t)
40
+ end
41
+ end
42
+ end
43
+
44
+ def task_each_popen3 *opts, &block
45
+ popen3(program, *opts) do |i, o, e, t|
46
+ yield(i, o, e, t)
47
+ end
48
+ end
49
+
50
+ def handle_response stderr, thread
51
+ unless thread.value.success?
52
+ puts stderr.read
53
+ exit(-1)
54
+ end
55
+ end
56
+
57
+ # Non-greedy json object detection
58
+ # if /\{.*\}/ =~ l
59
+ # p l.chomp
60
+ # res.push(l.chomp)
61
+ # end
62
+ # def task create_new, *opts, &block
63
+ # exp_regex = @@exp_regex
64
+ # retval = 0
65
+ # res = nil
66
+ # buff = ""
67
+ #
68
+ # run("task", *opts) do |exp, procedure|
69
+ # res = procedure.any do
70
+ # puts exp
71
+ # expect exp_regex[:create_rc] do
72
+ # if create_new
73
+ # send "yes"
74
+ # else
75
+ # send "no"
76
+ # end
77
+ # end
78
+ # block.call if block_given?
79
+ # end
80
+
81
+ # Filters should be a list of values
82
+ # Ranges interpreted as ids
83
+ # 1...5 : "1-5"
84
+ # 1..5 : "1-4"
85
+ # 1 : "1"
86
+ # and joined with ","
87
+ # [1...5, 8, 9] : "1-5,8,9"
88
+ end
89
+ end
@@ -0,0 +1,86 @@
1
+ require "virtus"
2
+ require "active_model"
3
+
4
+ module Rtasklib::Models
5
+ ValidationError = Class.new RuntimeError
6
+
7
+ class UUID < Virtus::Attribute
8
+ def coerce(value)
9
+ value.to_s
10
+ end
11
+ end
12
+
13
+ RcBooleans = Virtus.model do |mod|
14
+ mod.coerce = true
15
+ mod.coercer.config.string.boolean_map = {
16
+ 'no' => false,
17
+ 'yes' => true,
18
+ 'on' => true,
19
+ 'off' => false }
20
+ end
21
+
22
+ class TaskrcModel
23
+ # A base Virtus model whose attributes are created dynamically based on the
24
+ # given attributes are read from a .taskrc or Hash
25
+ #
26
+ # Dynamically add convert Boolean Strings to Ruby's Boolean values
27
+ include RcBooleans
28
+ end
29
+
30
+ class TaskModel
31
+ include Virtus.model
32
+ # perhaps use Veto
33
+ include ActiveModel::Validations
34
+
35
+ # Default attributes from TW
36
+ # Should match: http://taskwarrior.org/docs/design/task.html
37
+ #
38
+ # Required for every task
39
+ attribute :description, String
40
+ # But on creation these should be set by `task`
41
+ attribute :status, String
42
+ attribute :uuid, String
43
+ attribute :entry, Date
44
+
45
+ # Optional for every task
46
+ attribute :start, Date
47
+ attribute :until, Date
48
+ attribute :scheduled, Date
49
+ attribute :annotation, Array[String]
50
+ attribute :tags, Array[String]
51
+ attribute :project, String
52
+ attribute :depends, String
53
+ attribute :urgency, Float
54
+ # is calculated, so maybe private?
55
+ attribute :priority, String
56
+
57
+ # Required only for tasks that are Deleted or Completed
58
+ attribute :end, Date
59
+
60
+ # Required only for tasks that are Waiting
61
+ attribute :wait, Date
62
+
63
+ # Required only for tasks that are Recurring or have Recurring Parent
64
+ attribute :recur, Date
65
+
66
+ # Optional except for tasks with Recurring Parents
67
+ attribute :due, Date
68
+
69
+ # Required only for tasks that have Recurring Child
70
+ attribute :parent, UUID
71
+
72
+ # Internal attributes should be read-only
73
+ attribute :mask, String
74
+ attribute :imask, String
75
+ attribute :modified, Date
76
+
77
+ # TODO: handle arbitrary UDA's
78
+
79
+ # Refactoring idea, need to understand Virtus internals a bit better
80
+ # [:mask, :imask, :modified, :status, :uuid, :entry].each do |ro_attr|
81
+ # define_method("set_#{ro_attr.to_s}") do |value|
82
+ # self.class.find_by(ro_attr).send(".=", value)
83
+ # end
84
+ # end
85
+ end
86
+ end
@@ -0,0 +1,10 @@
1
+ require "active_model"
2
+ require "active_model/serializer"
3
+
4
+ module Rtasklib
5
+
6
+ module Models
7
+ class TaskSerializer < ActiveModel::Serializer
8
+ end
9
+ end
10
+ end