mani 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/.rubocop.yml +8 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +80 -0
- data/LICENSE +26 -0
- data/Makefile +12 -0
- data/README.md +318 -0
- data/bin/mani +48 -0
- data/examples/chording.rb +17 -0
- data/examples/hello.rb +5 -0
- data/examples/workspace_initializer.rb +46 -0
- data/lib/mani/tokenizer.rb +88 -0
- data/lib/mani/version.rb +7 -0
- data/lib/mani/window.rb +95 -0
- data/lib/mani/x.rb +48 -0
- data/lib/mani.rb +65 -0
- data/mani.gemspec +19 -0
- data/spec/lib/tokenizer_spec.rb +237 -0
- data/spec.rb +3 -0
- metadata +64 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 1b269a1ad0554dc57959f8142fe75d121b9bf472
|
4
|
+
data.tar.gz: 76ead7572bb2b1b3ce1d05def8a96b54acad42e9
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 18e6bbc6919d0d1744b3894b9ea948dbdfc652befde15b8561c450404978d34741d69c53fceb4aca2e5391f8da597ce8c4b9fead94d05fe99ac35a50930bb6a4
|
7
|
+
data.tar.gz: e3b81f2cbc77d58f183414f867ed51389db569ef6809c9f189ec52aa21c7f5bda1711f860d4143e2ca10a91250f380cd77d99fb584a20fbecb28153113e48747
|
data/.gitignore
ADDED
data/.rubocop.yml
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
activesupport (4.1.6)
|
5
|
+
i18n (~> 0.6, >= 0.6.9)
|
6
|
+
json (~> 1.7, >= 1.7.7)
|
7
|
+
minitest (~> 5.1)
|
8
|
+
thread_safe (~> 0.1)
|
9
|
+
tzinfo (~> 1.1)
|
10
|
+
addressable (2.3.6)
|
11
|
+
builder (3.2.2)
|
12
|
+
descendants_tracker (0.0.4)
|
13
|
+
thread_safe (~> 0.3, >= 0.3.1)
|
14
|
+
docile (1.1.5)
|
15
|
+
faraday (0.9.0)
|
16
|
+
multipart-post (>= 1.2, < 3)
|
17
|
+
git (1.2.8)
|
18
|
+
github_api (0.12.1)
|
19
|
+
addressable (~> 2.3)
|
20
|
+
descendants_tracker (~> 0.0.4)
|
21
|
+
faraday (~> 0.8, < 0.10)
|
22
|
+
hashie (>= 3.2)
|
23
|
+
multi_json (>= 1.7.5, < 2.0)
|
24
|
+
nokogiri (~> 1.6.3)
|
25
|
+
oauth2
|
26
|
+
hashie (3.3.1)
|
27
|
+
highline (1.6.21)
|
28
|
+
i18n (0.6.11)
|
29
|
+
jeweler (2.0.1)
|
30
|
+
builder
|
31
|
+
bundler (>= 1.0)
|
32
|
+
git (>= 1.2.5)
|
33
|
+
github_api
|
34
|
+
highline (>= 1.6.15)
|
35
|
+
nokogiri (>= 1.5.10)
|
36
|
+
rake
|
37
|
+
rdoc
|
38
|
+
json (1.8.1)
|
39
|
+
jwt (1.0.0)
|
40
|
+
mini_portile (0.6.0)
|
41
|
+
minitest (5.4.2)
|
42
|
+
multi_json (1.10.1)
|
43
|
+
multi_xml (0.5.5)
|
44
|
+
multipart-post (2.0.0)
|
45
|
+
nokogiri (1.6.3.1)
|
46
|
+
mini_portile (= 0.6.0)
|
47
|
+
oauth2 (1.0.0)
|
48
|
+
faraday (>= 0.8, < 0.10)
|
49
|
+
jwt (~> 1.0)
|
50
|
+
multi_json (~> 1.3)
|
51
|
+
multi_xml (~> 0.5)
|
52
|
+
rack (~> 1.2)
|
53
|
+
rack (1.5.2)
|
54
|
+
rake (10.3.2)
|
55
|
+
rdoc (3.12.2)
|
56
|
+
json (~> 1.4)
|
57
|
+
shoulda (3.5.0)
|
58
|
+
shoulda-context (~> 1.0, >= 1.0.1)
|
59
|
+
shoulda-matchers (>= 1.4.1, < 3.0)
|
60
|
+
shoulda-context (1.2.1)
|
61
|
+
shoulda-matchers (2.7.0)
|
62
|
+
activesupport (>= 3.0.0)
|
63
|
+
simplecov (0.9.1)
|
64
|
+
docile (~> 1.1.0)
|
65
|
+
multi_json (~> 1.0)
|
66
|
+
simplecov-html (~> 0.8.0)
|
67
|
+
simplecov-html (0.8.0)
|
68
|
+
thread_safe (0.3.4)
|
69
|
+
tzinfo (1.2.2)
|
70
|
+
thread_safe (~> 0.1)
|
71
|
+
|
72
|
+
PLATFORMS
|
73
|
+
ruby
|
74
|
+
|
75
|
+
DEPENDENCIES
|
76
|
+
bundler (~> 1.0)
|
77
|
+
jeweler (~> 2.0.1)
|
78
|
+
rdoc (~> 3.12)
|
79
|
+
shoulda
|
80
|
+
simplecov
|
data/LICENSE
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
Copyright (c) 2014, Nick Sinopoli <nsinopoli@gmail.com> All rights reserved.
|
2
|
+
|
3
|
+
Redistribution and use in source and binary forms, with or without
|
4
|
+
modification, are permitted provided that the following conditions are met:
|
5
|
+
|
6
|
+
* Redistributions of source code must retain the above copyright notice, this
|
7
|
+
list of conditions and the following disclaimer.
|
8
|
+
|
9
|
+
* Redistributions in binary form must reproduce the above copyright notice,
|
10
|
+
this list of conditions and the following disclaimer in the documentation
|
11
|
+
and/or other materials provided with the distribution.
|
12
|
+
|
13
|
+
* Neither the name of Nick Sinopoli nor the names of his contributors may be
|
14
|
+
used to endorse or promote products derived from this software without
|
15
|
+
specific prior written permission.
|
16
|
+
|
17
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
18
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
19
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
20
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
21
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
22
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
23
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
24
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
25
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
26
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/Makefile
ADDED
data/README.md
ADDED
@@ -0,0 +1,318 @@
|
|
1
|
+
# Mani
|
2
|
+
|
3
|
+
**Mani** is a window automation tool. Common tasks (such as launching programs,
|
4
|
+
visiting websites, and typing text) can be scripted using a simple Ruby DSL.
|
5
|
+
|
6
|
+
While [xmonad](http://xmonad.org/) is currently the only supported window
|
7
|
+
manager, Mani can support any window manager for the X Window System with just
|
8
|
+
a few lines of code. Please open an
|
9
|
+
[issue](https://github.com/NSinopoli/mani/issues) to add support for your
|
10
|
+
favorite window manager.
|
11
|
+
|
12
|
+
## Installation
|
13
|
+
|
14
|
+
```bash
|
15
|
+
gem install mani
|
16
|
+
```
|
17
|
+
|
18
|
+
### xmonad
|
19
|
+
|
20
|
+
You'll have to do a few things to get Mani to work as expected under xmonad.
|
21
|
+
|
22
|
+
1. Install [xdotool](http://www.semicomplete.com/projects/xdotool/).
|
23
|
+
2. Add [XMonad.Hooks.EwmhDesktops](http://xmonad.org/xmonad-docs/xmonad-contrib/XMonad-Hooks-EwmhDesktops.html)
|
24
|
+
to your xmonad configuration, and then restart xmonad.
|
25
|
+
|
26
|
+
```haskell
|
27
|
+
import XMonad.Hooks.EwmhDesktops
|
28
|
+
|
29
|
+
main = xmonad $ ewmh defaultConfig{ handleEventHook =
|
30
|
+
handleEventHook defaultConfig <+> fullscreenEventHook }
|
31
|
+
```
|
32
|
+
|
33
|
+
## Getting Started
|
34
|
+
|
35
|
+
Here's a simple example:
|
36
|
+
|
37
|
+
```ruby
|
38
|
+
# hello.rb
|
39
|
+
Mani.new(window_manager: :xmonad) do
|
40
|
+
window :hello, launch: 'urxvt' do
|
41
|
+
run 'echo "Hello, world."'
|
42
|
+
end
|
43
|
+
end
|
44
|
+
```
|
45
|
+
|
46
|
+
Run it:
|
47
|
+
|
48
|
+
```bash
|
49
|
+
$ mani hello.rb
|
50
|
+
```
|
51
|
+
|
52
|
+
Provided you have
|
53
|
+
[rxvt-unicode](http://software.schmorp.de/pkg/rxvt-unicode.html) installed, a
|
54
|
+
new terminal will open and the command `echo "Hello, world."` will run inside
|
55
|
+
it.
|
56
|
+
|
57
|
+
## Examples
|
58
|
+
|
59
|
+
Check out the [examples](examples) directory for more examples.
|
60
|
+
|
61
|
+
## DSL Overview
|
62
|
+
|
63
|
+
`Mani.new` should be at the beginning of every script. It takes an options
|
64
|
+
hash, which can take the following keys:
|
65
|
+
|
66
|
+
* `:window_manager` - the window manager (currently only `:xmonad` is supported)
|
67
|
+
* `:switch_to_workspace` - an optional proc which, when called, returns a
|
68
|
+
sequence which, when interpreted, will switch to the specified workspace
|
69
|
+
(defaults to the default sequence used by the `:window_manager` to change
|
70
|
+
workspaces)
|
71
|
+
|
72
|
+
```ruby
|
73
|
+
switcher = ->(space) { "super+#{space}" }
|
74
|
+
Mani.new(window_manager: :xmonad, switch_to_workspace: switcher) do
|
75
|
+
# Switch to workspace 1
|
76
|
+
workspace 1
|
77
|
+
|
78
|
+
# Switch to workspace 2
|
79
|
+
workspace 2
|
80
|
+
end
|
81
|
+
```
|
82
|
+
|
83
|
+
`workspace` switches to the specified workspace. If a block is supplied, it
|
84
|
+
will be called.
|
85
|
+
|
86
|
+
```ruby
|
87
|
+
Mani.new(window_manager: :xmonad) do
|
88
|
+
# Switch to workspace 1, launch a terminal and run 'echo "Hello, world."'
|
89
|
+
workspace 1 do
|
90
|
+
window :hello, launch: 'urxvt' do
|
91
|
+
run 'echo "Hello, world."'
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# Switch to workspace 2
|
96
|
+
workspace 2
|
97
|
+
end
|
98
|
+
```
|
99
|
+
|
100
|
+
`window` serves as the container for window commands. Its first argument is the
|
101
|
+
window name. Its second (optional) argument is an options hash, which can take
|
102
|
+
the following keys:
|
103
|
+
|
104
|
+
* `:launch` - the program to be launched
|
105
|
+
* `:delay` - the amount of time, in seconds, to delay after launching the
|
106
|
+
program (defaults to 0.5)
|
107
|
+
|
108
|
+
If a block is supplied, it will be called.
|
109
|
+
|
110
|
+
```ruby
|
111
|
+
Mani.new(window_manager: :xmonad) do
|
112
|
+
# Create a new terminal window, wait one second, and then run 'ls'
|
113
|
+
window :ls do
|
114
|
+
launch 'urxvt', delay: 1 do
|
115
|
+
run 'ls'
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
# The same process can be accomplished using this condensed syntax:
|
120
|
+
window :ls, launch: 'urxvt', delay: 1 do
|
121
|
+
run 'ls'
|
122
|
+
end
|
123
|
+
|
124
|
+
# Just launch a program
|
125
|
+
window :thunderbird, launch: 'thunderbird'
|
126
|
+
|
127
|
+
# Return to the :ls window, and then run 'date'
|
128
|
+
window :ls do
|
129
|
+
run 'date'
|
130
|
+
end
|
131
|
+
end
|
132
|
+
```
|
133
|
+
|
134
|
+
`launch` launches a program. It takes an optional options hash, which can take
|
135
|
+
the following keys:
|
136
|
+
|
137
|
+
* `:delay` - the amount of time, in seconds, to delay after launching the
|
138
|
+
program (defaults to 0.5)
|
139
|
+
|
140
|
+
```ruby
|
141
|
+
Mani.new(window_manager: :xmonad) do
|
142
|
+
# Create a new terminal window, wait one second, and then run 'ls'
|
143
|
+
window :ls do
|
144
|
+
launch 'urxvt', delay: 1 do
|
145
|
+
run 'ls'
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
```
|
150
|
+
|
151
|
+
`run` executes a [combination](#combination-syntax) within a window. It takes
|
152
|
+
an optional options hash, which can take the following keys:
|
153
|
+
|
154
|
+
* `:delay` - the amount of time, in seconds, to delay after running the
|
155
|
+
command (defaults to 0.5)
|
156
|
+
|
157
|
+
```ruby
|
158
|
+
Mani.new(window_manager: :xmonad) do
|
159
|
+
# Run 'ls'
|
160
|
+
window :ls, launch: 'urxvt', delay: 1 do
|
161
|
+
run 'ls'
|
162
|
+
end
|
163
|
+
end
|
164
|
+
```
|
165
|
+
|
166
|
+
`type` types a [combination](#combination-syntax) within a window. It takes an
|
167
|
+
optional options hash, which can take the following keys:
|
168
|
+
|
169
|
+
* `:delay` - the amount of time, in seconds, to delay after typing the text
|
170
|
+
(defaults to 0.5)
|
171
|
+
|
172
|
+
```ruby
|
173
|
+
Mani.new(window_manager: :xmonad) do
|
174
|
+
# Launch gvim
|
175
|
+
window :gvim, launch: 'gvim -f', delay: 2 do
|
176
|
+
# Wait two seconds, then move to the end of the buffer, start a new line,
|
177
|
+
# and type "Hello, world."
|
178
|
+
type 'GoHello, world.'
|
179
|
+
end
|
180
|
+
end
|
181
|
+
```
|
182
|
+
|
183
|
+
`visit` is used to visit a website. It takes an optional options hash, which
|
184
|
+
can take the following keys:
|
185
|
+
|
186
|
+
* `:delay` - the amount of time, in seconds, to delay after entering the url
|
187
|
+
(defaults to 0.5)
|
188
|
+
|
189
|
+
Note that depending on the browser, you may need to insert an "F6"
|
190
|
+
[keystroke](#combination-syntax) at the beginning of the url.
|
191
|
+
|
192
|
+
```ruby
|
193
|
+
Mani.new(window_manager: :xmonad) do
|
194
|
+
window :chromium, launch: 'chromium', delay: 1.5 do
|
195
|
+
visit 'localhost:8080'
|
196
|
+
end
|
197
|
+
|
198
|
+
window :firefox, launch: 'firefox', delay: 1.5 do
|
199
|
+
# Type "F6" first so that the cursor is in the address bar before typing
|
200
|
+
# the url.
|
201
|
+
visit '{{F6}}gmail.com'
|
202
|
+
end
|
203
|
+
end
|
204
|
+
```
|
205
|
+
|
206
|
+
`browser_tab` is used to either create a new browser tab or switch to a
|
207
|
+
specific tab. Its first argument, when `:new`, will create a new tab. If it is
|
208
|
+
an integer, the browser will instead switch to that tab. Its second argument
|
209
|
+
is an optional options hash, which can take the following keys:
|
210
|
+
|
211
|
+
* `:delay` - the amount of time, in seconds, to delay after handling the tab
|
212
|
+
(defaults to 0.5)
|
213
|
+
|
214
|
+
If a block is supplied, it will be called.
|
215
|
+
|
216
|
+
```ruby
|
217
|
+
Mani.new(window_manager: :xmonad) do
|
218
|
+
window :chromium, launch: 'chromium', delay: 1.5 do
|
219
|
+
# Visit 'localhost:8080' within the initial tab
|
220
|
+
visit 'localhost:8080'
|
221
|
+
|
222
|
+
# Create a new tab and visit 'news.ycombinator.com'
|
223
|
+
browser_tab :new do
|
224
|
+
visit 'news.ycombinator.com'
|
225
|
+
end
|
226
|
+
|
227
|
+
# Switch back to the first tab (localhost:8080)
|
228
|
+
browser_tab 1
|
229
|
+
end
|
230
|
+
end
|
231
|
+
```
|
232
|
+
|
233
|
+
### Combination Syntax
|
234
|
+
|
235
|
+
Simulating keystrokes for function keys and modifiers requires the use of a
|
236
|
+
special syntax. The `{{` and `}}` tags are used to indicate a sequence.
|
237
|
+
Chording is achieved by separating keys with a `+` sign.
|
238
|
+
|
239
|
+
```ruby
|
240
|
+
Mani.new(window_manager: :xmonad) do
|
241
|
+
window :firefox, launch: 'firefox', delay: 1.5 do
|
242
|
+
# Type "F6" first so that the cursor is in the address bar before typing
|
243
|
+
# the url.
|
244
|
+
visit '{{F6}}gmail.com'
|
245
|
+
|
246
|
+
# Paste the contents of the clipboard into the url.
|
247
|
+
visit 'localhost:8080/reports/{{ctrl+v}}/preview'
|
248
|
+
end
|
249
|
+
end
|
250
|
+
```
|
251
|
+
|
252
|
+
In the event a literal `{{` or `}}` is desired, a `%` sign can be added as a
|
253
|
+
prefix to escape the sequence.
|
254
|
+
|
255
|
+
```ruby
|
256
|
+
Mani.new(window_manager: :xmonad) do
|
257
|
+
window :hello, launch: 'urxvt' do
|
258
|
+
run 'echo "%{{literal opening brackets"'
|
259
|
+
run 'echo "literal closing brackets%}}"'
|
260
|
+
run 'echo "%{{literal opening and closing brackets%}}"'
|
261
|
+
end
|
262
|
+
end
|
263
|
+
```
|
264
|
+
|
265
|
+
## Contributing
|
266
|
+
|
267
|
+
Please follow the guidelines below.
|
268
|
+
|
269
|
+
### Issue Reporting
|
270
|
+
|
271
|
+
* Use the [issue tracker](https://github.com/NSinopoli/mani/issues) to report
|
272
|
+
any issues or ideas for improvements.
|
273
|
+
* Check that the issue has not already been reported.
|
274
|
+
* Check that the issue has not already been fixed in the latest code (i.e., on
|
275
|
+
`master`).
|
276
|
+
* Be clear, concise, and precise in your description of the problem.
|
277
|
+
* Open an issue with a descriptive title and a summary in grammatically
|
278
|
+
correct, complete sentences.
|
279
|
+
* Include any relevant code to the issue summary.
|
280
|
+
|
281
|
+
### Pull Requests
|
282
|
+
|
283
|
+
* Read [how to properly contribute to open source projects on Github](http://gun.io/blog/how-to-github-fork-branch-and-pull-request).
|
284
|
+
* Fork the project.
|
285
|
+
* Use a topic/feature branch to easily amend a pull request later, if
|
286
|
+
necessary.
|
287
|
+
* Write [good commit messages](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html).
|
288
|
+
* Use the same [coding conventions](#code-conventions) as the rest of the
|
289
|
+
project.
|
290
|
+
* Commit and push until you are happy with your contribution.
|
291
|
+
* Make sure to add [tests](#tests) for it. This is important so I don't break
|
292
|
+
it in a future version unintentionally.
|
293
|
+
* Please try not to mess with the version or history. If you want to have your
|
294
|
+
own version, please isolate the change to its own commit so I can cherry-pick
|
295
|
+
around it.
|
296
|
+
* [Squash related commits together](http://gitready.com/advanced/2009/02/10/squashing-commits-with-rebase.html).
|
297
|
+
* Open a [pull request](https://help.github.com/articles/using-pull-requests)
|
298
|
+
that relates to *only* one subject with a clear title and description in
|
299
|
+
grammatically correct, complete sentences.
|
300
|
+
|
301
|
+
### Code Conventions
|
302
|
+
|
303
|
+
**Mani** follows the conventions laid out in the
|
304
|
+
[Ruby Style Guide](https://github.com/bbatsov/ruby-style-guide). These
|
305
|
+
conventions are enforced by [RuboCop](https://github.com/bbatsov/rubocop).
|
306
|
+
|
307
|
+
```
|
308
|
+
make install
|
309
|
+
make style
|
310
|
+
```
|
311
|
+
|
312
|
+
Please ensure that any contributed code does not produce any RuboCop offenses.
|
313
|
+
|
314
|
+
### Tests
|
315
|
+
|
316
|
+
```
|
317
|
+
make test
|
318
|
+
```
|
data/bin/mani
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift(File.dirname(File.realpath(__FILE__)) + '/../lib')
|
4
|
+
|
5
|
+
require 'mani'
|
6
|
+
require 'mani/version'
|
7
|
+
|
8
|
+
require 'optparse'
|
9
|
+
|
10
|
+
class Mani
|
11
|
+
class CLI
|
12
|
+
# Parses command line options.
|
13
|
+
#
|
14
|
+
# @param [Array] args The command line arguments
|
15
|
+
def self.parse_options(args)
|
16
|
+
option_parser = OptionParser.new do |opts|
|
17
|
+
opts.banner = "Usage: mani FILE"
|
18
|
+
|
19
|
+
opts.separator ""
|
20
|
+
|
21
|
+
opts.separator "Example:"
|
22
|
+
opts.separator " $ mani workspace_initializer.rb"
|
23
|
+
|
24
|
+
opts.separator ""
|
25
|
+
|
26
|
+
opts.separator "Options:"
|
27
|
+
|
28
|
+
opts.on("-v", "--version", "Print the version") do |v|
|
29
|
+
puts "Mani v#{Mani::Version::VERSION}"
|
30
|
+
exit
|
31
|
+
end
|
32
|
+
|
33
|
+
opts.on_tail("-h", "--help", "Show this message") do
|
34
|
+
puts opts
|
35
|
+
exit
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
option_parser.parse!(args)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
ARGV << '-h' if ARGV.empty? && $stdin.tty?
|
45
|
+
|
46
|
+
Mani::CLI.parse_options(ARGV) if $stdin.tty?
|
47
|
+
|
48
|
+
load ARGF.file
|
@@ -0,0 +1,17 @@
|
|
1
|
+
Mani.new do
|
2
|
+
window :urxvt, launch: 'urxvt' do
|
3
|
+
# Copy the most recent report id
|
4
|
+
run %q(
|
5
|
+
ssh 127.0.0.1 -p 49154
|
6
|
+
psql -h localhost -U web_dev web_development <<<
|
7
|
+
'SELECT id FROM reports order by id desc limit 1;'
|
8
|
+
| grep -m 1 \[0-9\] | xclip -selection c
|
9
|
+
).strip.gsub(/\s+/, ' ')
|
10
|
+
run 'exit'
|
11
|
+
end
|
12
|
+
|
13
|
+
window :chrome, launch: 'google-chrome-stable', delay: 1 do
|
14
|
+
# Paste the report id within the url
|
15
|
+
visit 'localhost:8080/reports/{{ctrl+v}}/preview'
|
16
|
+
end
|
17
|
+
end
|
data/examples/hello.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
Mani.new(window_manager: :xmonad) do
|
2
|
+
workspace 1 do
|
3
|
+
window :top, launch: 'urxvt' do
|
4
|
+
run 'top'
|
5
|
+
end
|
6
|
+
|
7
|
+
window :date, launch: 'urxvt' do
|
8
|
+
run 'date'
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
workspace 2 do
|
13
|
+
window :firefox, launch: 'firefox', delay: 1.5 do
|
14
|
+
# Type "F6" first so that the cursor is in the address bar before typing
|
15
|
+
# the url.
|
16
|
+
visit '{{F6}}gmail.com'
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
workspace 3 do
|
21
|
+
# Use "-f" to prevent vim from forking and detaching from the original
|
22
|
+
# process.
|
23
|
+
window :gvim, launch: 'gvim -f', delay: 2 do
|
24
|
+
type 'GoHello, world.'
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
workspace 4 do
|
29
|
+
window :thunderbird, launch: 'thunderbird'
|
30
|
+
|
31
|
+
window :hipchat, launch: 'hipchat', delay: 3
|
32
|
+
end
|
33
|
+
|
34
|
+
workspace 8 do
|
35
|
+
window :chromium, launch: 'chromium', delay: 1.5 do
|
36
|
+
visit 'localhost:8080'
|
37
|
+
|
38
|
+
browser_tab :new do
|
39
|
+
visit 'news.ycombinator.com'
|
40
|
+
end
|
41
|
+
|
42
|
+
# Switch back to the first tab (localhost)
|
43
|
+
browser_tab 1
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'strscan'
|
2
|
+
|
3
|
+
class Mani
|
4
|
+
# This class contains methods to handle the tokenization of strings.
|
5
|
+
class Tokenizer
|
6
|
+
# The escape character
|
7
|
+
ESCAPE_CHARACTER = '%'
|
8
|
+
|
9
|
+
# The delimiter signifying the start of a sequence
|
10
|
+
SEQUENCE_OPEN_DELIMITER = '{{'
|
11
|
+
|
12
|
+
# The delimiter signifying the end of a sequence
|
13
|
+
SEQUENCE_CLOSE_DELIMITER = '}}'
|
14
|
+
|
15
|
+
# The delimiter signifying an "open sequence" escape sequence
|
16
|
+
LITERAL_OPEN_DELIMITER = ESCAPE_CHARACTER + SEQUENCE_OPEN_DELIMITER
|
17
|
+
|
18
|
+
# The delimiter signifying a "close sequence" escape sequence
|
19
|
+
LITERAL_CLOSE_DELIMITER = ESCAPE_CHARACTER + SEQUENCE_CLOSE_DELIMITER
|
20
|
+
|
21
|
+
# The pattern to match the start of a sequence
|
22
|
+
SEQUENCE_OPEN = /
|
23
|
+
# find opening delimiter at beginning of string...
|
24
|
+
^#{SEQUENCE_OPEN_DELIMITER}
|
25
|
+
# ...or elsewhere in the string, provided it's not preceded by
|
26
|
+
# ESCAPE_CHARACTER
|
27
|
+
|[^#{ESCAPE_CHARACTER}]#{SEQUENCE_OPEN_DELIMITER}
|
28
|
+
/x
|
29
|
+
|
30
|
+
# The pattern to match the end of a sequence
|
31
|
+
SEQUENCE_CLOSE = /
|
32
|
+
# find closing delimiter at beginning of string...
|
33
|
+
^#{SEQUENCE_CLOSE_DELIMITER}
|
34
|
+
# ...or elsewhere in the string, provided it's not preceded by
|
35
|
+
# ESCAPE_CHARACTER
|
36
|
+
|[^#{ESCAPE_CHARACTER}]#{SEQUENCE_CLOSE_DELIMITER}
|
37
|
+
/x
|
38
|
+
|
39
|
+
# Retrieves the tokens comprising the supplied text.
|
40
|
+
#
|
41
|
+
# @param [String] text The text
|
42
|
+
# @return [Array]
|
43
|
+
def self.get_tokens(text)
|
44
|
+
tokenize StringScanner.new(text), []
|
45
|
+
end
|
46
|
+
|
47
|
+
# Strips the comment delimiters from the supplied text.
|
48
|
+
#
|
49
|
+
# @param [String] text The text
|
50
|
+
# @return [String]
|
51
|
+
def self.strip_comment_delimiters(text)
|
52
|
+
text
|
53
|
+
.gsub(LITERAL_OPEN_DELIMITER, SEQUENCE_OPEN_DELIMITER)
|
54
|
+
.gsub(LITERAL_CLOSE_DELIMITER, SEQUENCE_CLOSE_DELIMITER)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Recursively scans the string within the supplied scanner to produce a
|
58
|
+
# list of tokens.
|
59
|
+
#
|
60
|
+
# @param [StringScanner] scanner The string scanner
|
61
|
+
# @param [Array] tokens The tokens
|
62
|
+
# @return [Array]
|
63
|
+
def self.tokenize(scanner, tokens)
|
64
|
+
match = scanner.scan_until SEQUENCE_OPEN
|
65
|
+
unless match
|
66
|
+
static = strip_comment_delimiters scanner.rest
|
67
|
+
tokens.concat [[:static, static]] unless static.empty?
|
68
|
+
return tokens
|
69
|
+
end
|
70
|
+
|
71
|
+
if !scanner.check_until SEQUENCE_CLOSE
|
72
|
+
static = strip_comment_delimiters(match + scanner.rest)
|
73
|
+
tokens.concat [[:static, static]]
|
74
|
+
else
|
75
|
+
static = strip_comment_delimiters match.chomp(SEQUENCE_OPEN_DELIMITER)
|
76
|
+
tokens.concat [[:static, static]] unless static.empty?
|
77
|
+
|
78
|
+
match = scanner.scan_until SEQUENCE_CLOSE
|
79
|
+
match.chomp! SEQUENCE_CLOSE_DELIMITER
|
80
|
+
|
81
|
+
sequence = strip_comment_delimiters match
|
82
|
+
tokens.concat [[:sequence, sequence]] unless sequence.empty?
|
83
|
+
|
84
|
+
tokenize scanner, tokens
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
data/lib/mani/version.rb
ADDED
data/lib/mani/window.rb
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
class Mani
|
2
|
+
# This class contains methods to handle operations run within windows.
|
3
|
+
class Window
|
4
|
+
# Creates a new tab, or switches to the supplied tab.
|
5
|
+
#
|
6
|
+
# @param [Symbol, Integer] tab The tab. If :new is specified, a new tab
|
7
|
+
# will be created and the window will switch to it. If an integer is
|
8
|
+
# specified, the window will switch to that (previously existing) tab.
|
9
|
+
# @param [Hash] options The options
|
10
|
+
# * :delay (Numeric) _Optional_ The amount of time, in seconds, to delay
|
11
|
+
# after handling the tab (defaults to 0.5)
|
12
|
+
# @param [Proc] block The code to execute after handling the tab
|
13
|
+
def browser_tab(tab, options = {}, &block)
|
14
|
+
@windowing_system.focus_window @pid
|
15
|
+
|
16
|
+
if tab == :new
|
17
|
+
@windowing_system.type_keysequence 'ctrl+t'
|
18
|
+
@windowing_system.type_keysequence 'alt+9'
|
19
|
+
elsif tab >= 1 && tab <= 8
|
20
|
+
@windowing_system.type_keysequence "alt+#{tab}"
|
21
|
+
elsif tab > 8
|
22
|
+
@windowing_system.type_keysequence 'alt+8'
|
23
|
+
(tab - 8).times { @windowing_system.type_keysequence 'ctrl+Tab' }
|
24
|
+
else
|
25
|
+
return
|
26
|
+
end
|
27
|
+
|
28
|
+
sleep options[:delay] || 0.5
|
29
|
+
|
30
|
+
instance_eval(&block) if block
|
31
|
+
end
|
32
|
+
|
33
|
+
# Initializes the window.
|
34
|
+
#
|
35
|
+
# @param [Object] windowing_system The windowing system
|
36
|
+
def initialize(windowing_system)
|
37
|
+
@windowing_system = windowing_system
|
38
|
+
end
|
39
|
+
|
40
|
+
# Launches the supplied program.
|
41
|
+
#
|
42
|
+
# @param [String] program The program
|
43
|
+
# @param [Hash] options The options
|
44
|
+
# * :delay (Numeric) _Optional_ The amount of time, in seconds, to delay
|
45
|
+
# after launching the program (defaults to 0.5)
|
46
|
+
# @param [Proc] block The code to execute after launching the program
|
47
|
+
def launch(program, options = {}, &block)
|
48
|
+
return if @pid
|
49
|
+
|
50
|
+
@pid = Process.spawn program
|
51
|
+
Process.detach @pid
|
52
|
+
|
53
|
+
sleep options[:delay] || 0.5
|
54
|
+
|
55
|
+
instance_eval(&block) if block
|
56
|
+
end
|
57
|
+
|
58
|
+
# Runs the supplied command within the current window.
|
59
|
+
#
|
60
|
+
# @param [String] command The command
|
61
|
+
# @param [Hash] options The options
|
62
|
+
# * :delay (Numeric) _Optional_ The amount of time, in seconds, to delay
|
63
|
+
# after running the command (defaults to 0.5)
|
64
|
+
def run(command, options = {})
|
65
|
+
@windowing_system.focus_window @pid
|
66
|
+
@windowing_system.type_combination command
|
67
|
+
@windowing_system.type_keysequence 'Return'
|
68
|
+
|
69
|
+
sleep options[:delay] || 0.5
|
70
|
+
end
|
71
|
+
|
72
|
+
# Types the supplied text into the current window.
|
73
|
+
#
|
74
|
+
# @param [String] text The text
|
75
|
+
# @param [Hash] options The options
|
76
|
+
# * :delay (Numeric) _Optional_ The amount of time, in seconds, to delay
|
77
|
+
# after typing the text (defaults to 0.5)
|
78
|
+
def type(text, options = {})
|
79
|
+
@windowing_system.focus_window @pid
|
80
|
+
@windowing_system.type_combination text
|
81
|
+
|
82
|
+
sleep options[:delay] || 0.5
|
83
|
+
end
|
84
|
+
|
85
|
+
# Enters the supplied url into the current window.
|
86
|
+
#
|
87
|
+
# @param [String] url The url
|
88
|
+
# @param [Hash] options The options
|
89
|
+
# * :delay (Numeric) _Optional_ The amount of time, in seconds, to delay
|
90
|
+
# after entering the url (defaults to 0.5)
|
91
|
+
def visit(url, options = {})
|
92
|
+
run url, options
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
data/lib/mani/x.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'x_do'
|
2
|
+
|
3
|
+
class Mani
|
4
|
+
# This class contains methods to interface with the X Window System.
|
5
|
+
class X
|
6
|
+
# Initializes XDo to handle the heavy lifting of interfacing with X.
|
7
|
+
def initialize
|
8
|
+
@xdo = XDo.new
|
9
|
+
end
|
10
|
+
|
11
|
+
# Finds the visible window with the supplied pid and focuses it.
|
12
|
+
#
|
13
|
+
# @param [Fixnum] pid The pid
|
14
|
+
def focus_window(pid)
|
15
|
+
@xdo.find_windows(pid: pid, visible: true).first.focus
|
16
|
+
end
|
17
|
+
|
18
|
+
# Types the supplied combination. Note that any text between '{{' and '}}'
|
19
|
+
# delimiters is treated as a sequence. All other text is treated literally.
|
20
|
+
#
|
21
|
+
# @param [String] combination The combination
|
22
|
+
def type_combination(combination)
|
23
|
+
tokens = Mani::Tokenizer.get_tokens combination
|
24
|
+
tokens.each do |token|
|
25
|
+
case token.first
|
26
|
+
when :static
|
27
|
+
type_string token.last
|
28
|
+
when :sequence
|
29
|
+
type_keysequence token.last
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Types the supplied sequence (e.g., 'ctrl+v', 'F6', 'alt+2').
|
35
|
+
#
|
36
|
+
# @param [String] sequence The sequence
|
37
|
+
def type_keysequence(sequence)
|
38
|
+
@xdo.keyboard.type_keysequence sequence
|
39
|
+
end
|
40
|
+
|
41
|
+
# Types the supplied string. Note that the string is treated literally.
|
42
|
+
#
|
43
|
+
# @param [String] string The string
|
44
|
+
def type_string(string)
|
45
|
+
@xdo.keyboard.type_string string
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/mani.rb
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'mani/window'
|
2
|
+
require 'mani/tokenizer'
|
3
|
+
|
4
|
+
# The base class.
|
5
|
+
class Mani
|
6
|
+
# Initializes the windowing system, as well as the window manager options.
|
7
|
+
#
|
8
|
+
# @param [Hash] options The options
|
9
|
+
# * :window_manager (Symbol) The window manager (currently only handles
|
10
|
+
# :xmonad)
|
11
|
+
# * :switch_to_workspace (Proc) _Optional_ The proc which, when called,
|
12
|
+
# returns a string which, when interpreted, will switch to the specified
|
13
|
+
# workspace
|
14
|
+
# @param [Proc] block The code to execute after initialization
|
15
|
+
def initialize(options = {}, &block)
|
16
|
+
window_manager = options.delete :window_manager
|
17
|
+
case window_manager
|
18
|
+
when :xmonad
|
19
|
+
require 'mani/x'
|
20
|
+
|
21
|
+
@windowing_system = Mani::X.new
|
22
|
+
@window_manager_options = {
|
23
|
+
switch_to_workspace: ->(space) { "super+#{space}" }
|
24
|
+
}.merge options
|
25
|
+
else
|
26
|
+
fail 'Unrecognized :window_manager.'
|
27
|
+
end
|
28
|
+
|
29
|
+
@windows = {}
|
30
|
+
|
31
|
+
instance_eval(&block)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Creates a new window object, or defers execution of the supplied block to
|
35
|
+
# an existing window object.
|
36
|
+
#
|
37
|
+
# @param [Symbol] name The window name
|
38
|
+
# @param [Hash] options The options
|
39
|
+
# * :launch (String) _Optional_ The program to launch. If specified, any
|
40
|
+
# remaining options, as well as the supplied block, will be passed to
|
41
|
+
# Mani::Window#launch. If not specified, the supplied block will be
|
42
|
+
# executed (within the context of the window).
|
43
|
+
# @param [Proc] block The code to execute
|
44
|
+
def window(name, options = {}, &block)
|
45
|
+
@windows[name] = Window.new @windowing_system unless @windows[name]
|
46
|
+
|
47
|
+
program = options.delete :launch
|
48
|
+
if program
|
49
|
+
@windows[name].launch program, options, &block
|
50
|
+
else
|
51
|
+
@windows[name].instance_eval(&block)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Switches to the specified workspace.
|
56
|
+
#
|
57
|
+
# @param [Integer] space The space number
|
58
|
+
# @param [Proc] block The code to execute after switching to the workspace
|
59
|
+
def workspace(space, &block)
|
60
|
+
sequence = @window_manager_options[:switch_to_workspace].call space
|
61
|
+
@windowing_system.type_keysequence sequence
|
62
|
+
|
63
|
+
instance_eval(&block) if block
|
64
|
+
end
|
65
|
+
end
|
data/mani.gemspec
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
$LOAD_PATH.unshift File.expand_path('../lib', __FILE__)
|
2
|
+
require 'mani/version'
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.name = 'mani'
|
6
|
+
gem.homepage = 'https://github.com/NSinopoli/mani'
|
7
|
+
gem.license = 'BSD (3-Clause)'
|
8
|
+
gem.summary = 'A window automation tool'
|
9
|
+
gem.description = 'A window automation tool'
|
10
|
+
gem.email = 'NSinopoli@gmail.com'
|
11
|
+
gem.authors = ['Nick Sinopoli']
|
12
|
+
|
13
|
+
gem.version = Mani::Version::VERSION
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($/)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(spec)/})
|
18
|
+
gem.require_paths = ['lib']
|
19
|
+
end
|
@@ -0,0 +1,237 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require_relative '../../lib/mani/tokenizer'
|
3
|
+
|
4
|
+
describe Mani::Tokenizer do
|
5
|
+
describe '.get_tokens' do
|
6
|
+
describe 'when there are no tags' do
|
7
|
+
it 'returns the correct tokens' do
|
8
|
+
source = 'static'
|
9
|
+
actual = Mani::Tokenizer.get_tokens source
|
10
|
+
expected = [[:static, 'static']]
|
11
|
+
actual.must_equal expected
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe 'when there is an opening tag and no closing tag' do
|
16
|
+
it 'returns the correct tokens' do
|
17
|
+
source = 'static {{ static'
|
18
|
+
actual = Mani::Tokenizer.get_tokens source
|
19
|
+
expected = [
|
20
|
+
[:static, 'static {{ static'],
|
21
|
+
]
|
22
|
+
actual.must_equal expected
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe 'when there is a closing tag and no opening tag' do
|
27
|
+
it 'returns the correct tokens' do
|
28
|
+
source = 'static }} static'
|
29
|
+
actual = Mani::Tokenizer.get_tokens source
|
30
|
+
expected = [
|
31
|
+
[:static, 'static }} static']
|
32
|
+
]
|
33
|
+
actual.must_equal expected
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe 'when there is a commented opening tag and no closing tag' do
|
38
|
+
it 'returns the correct tokens' do
|
39
|
+
source = 'static %{{ static'
|
40
|
+
actual = Mani::Tokenizer.get_tokens source
|
41
|
+
expected = [
|
42
|
+
[:static, 'static {{ static']
|
43
|
+
]
|
44
|
+
actual.must_equal expected
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe 'when there is a commented closing tag and no opening tag' do
|
49
|
+
it 'returns the correct tokens' do
|
50
|
+
source = 'static %}} static'
|
51
|
+
actual = Mani::Tokenizer.get_tokens source
|
52
|
+
expected = [
|
53
|
+
[:static, 'static }} static']
|
54
|
+
]
|
55
|
+
actual.must_equal expected
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe 'when there are commented opening and closing tags' do
|
60
|
+
it 'returns the correct tokens' do
|
61
|
+
source = 'static %{{seq%}} static'
|
62
|
+
actual = Mani::Tokenizer.get_tokens source
|
63
|
+
expected = [
|
64
|
+
[:static, 'static {{seq}} static'],
|
65
|
+
]
|
66
|
+
actual.must_equal expected
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe 'when there is a commented opening tag and a closing tag' do
|
71
|
+
it 'returns the correct tokens' do
|
72
|
+
source = 'static %{{seq}} static'
|
73
|
+
actual = Mani::Tokenizer.get_tokens source
|
74
|
+
expected = [
|
75
|
+
[:static, 'static {{seq}} static'],
|
76
|
+
]
|
77
|
+
actual.must_equal expected
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
describe 'when there is an opening tag and a commented closing tag' do
|
82
|
+
it 'returns the correct tokens' do
|
83
|
+
source = 'static {{seq%}} static'
|
84
|
+
actual = Mani::Tokenizer.get_tokens source
|
85
|
+
expected = [
|
86
|
+
[:static, 'static {{seq}} static'],
|
87
|
+
]
|
88
|
+
actual.must_equal expected
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
describe 'when there is an empty sequence' do
|
93
|
+
it 'returns the correct tokens' do
|
94
|
+
source = '{{}}'
|
95
|
+
actual = Mani::Tokenizer.get_tokens source
|
96
|
+
expected = []
|
97
|
+
actual.must_equal expected
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
describe 'when there is one, unnested sequence' do
|
102
|
+
it 'returns the correct tokens' do
|
103
|
+
source = 'static {{seq}} static'
|
104
|
+
actual = Mani::Tokenizer.get_tokens source
|
105
|
+
expected = [
|
106
|
+
[:static, 'static '],
|
107
|
+
[:sequence, 'seq'],
|
108
|
+
[:static, ' static']
|
109
|
+
]
|
110
|
+
actual.must_equal expected
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
describe 'when there is a sequence with a commented opening tag within' do
|
115
|
+
it 'returns the correct tokens' do
|
116
|
+
source = 'static {{seq %{{ }} static'
|
117
|
+
actual = Mani::Tokenizer.get_tokens source
|
118
|
+
expected = [
|
119
|
+
[:static, 'static '],
|
120
|
+
[:sequence, 'seq {{ '],
|
121
|
+
[:static, ' static']
|
122
|
+
]
|
123
|
+
actual.must_equal expected
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
describe 'when there is a sequence with a commented closing tag within' do
|
128
|
+
it 'returns the correct tokens' do
|
129
|
+
source = 'static {{seq %}} }} static'
|
130
|
+
actual = Mani::Tokenizer.get_tokens source
|
131
|
+
expected = [
|
132
|
+
[:static, 'static '],
|
133
|
+
[:sequence, 'seq }} '],
|
134
|
+
[:static, ' static']
|
135
|
+
]
|
136
|
+
actual.must_equal expected
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
describe 'when there are commented tags within a sequence' do
|
141
|
+
it 'returns the correct tokens' do
|
142
|
+
source = 'static {{seq %{{ nested %}} }} static'
|
143
|
+
actual = Mani::Tokenizer.get_tokens source
|
144
|
+
expected = [
|
145
|
+
[:static, 'static '],
|
146
|
+
[:sequence, 'seq {{ nested }} '],
|
147
|
+
[:static, ' static']
|
148
|
+
]
|
149
|
+
actual.must_equal expected
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
describe 'when there is a sequence within commented tags' do
|
154
|
+
it 'returns the correct tokens' do
|
155
|
+
source = 'static %{{comment {{seq}} %}} static'
|
156
|
+
actual = Mani::Tokenizer.get_tokens source
|
157
|
+
expected = [
|
158
|
+
[:static, 'static {{comment '],
|
159
|
+
[:sequence, 'seq'],
|
160
|
+
[:static, ' }} static']
|
161
|
+
]
|
162
|
+
actual.must_equal expected
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
describe 'when there is a sequence within a sequence' do
|
167
|
+
it 'returns the correct tokens' do
|
168
|
+
source = 'static {{nested {{seq}} }} static'
|
169
|
+
actual = Mani::Tokenizer.get_tokens source
|
170
|
+
expected = [
|
171
|
+
[:static, 'static '],
|
172
|
+
[:sequence, 'nested {{seq'],
|
173
|
+
[:static, ' }} static']
|
174
|
+
]
|
175
|
+
actual.must_equal expected
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
describe 'when there are multiple, unnested sequence' do
|
180
|
+
it 'returns the correct tokens' do
|
181
|
+
source = '{{seq}} static {{second seq}} more static'
|
182
|
+
actual = Mani::Tokenizer.get_tokens source
|
183
|
+
expected = [
|
184
|
+
[:sequence, 'seq'],
|
185
|
+
[:static, ' static '],
|
186
|
+
[:sequence, 'second seq'],
|
187
|
+
[:static, ' more static']
|
188
|
+
]
|
189
|
+
actual.must_equal expected
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
describe 'when there are multiple empty sequence' do
|
194
|
+
it 'returns the correct tokens' do
|
195
|
+
source = '{{}} static {{}}'
|
196
|
+
actual = Mani::Tokenizer.get_tokens source
|
197
|
+
expected = [
|
198
|
+
[:static, ' static '],
|
199
|
+
]
|
200
|
+
actual.must_equal expected
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
describe 'when the tags are out of order' do
|
205
|
+
it 'returns the correct tokens' do
|
206
|
+
source = '}} static {{'
|
207
|
+
actual = Mani::Tokenizer.get_tokens source
|
208
|
+
expected = [
|
209
|
+
[:static, '}} static {{'],
|
210
|
+
]
|
211
|
+
actual.must_equal expected
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
describe 'when the commenting tags are out of order' do
|
216
|
+
it 'returns the correct tokens' do
|
217
|
+
source = '%}} static %{{'
|
218
|
+
actual = Mani::Tokenizer.get_tokens source
|
219
|
+
expected = [
|
220
|
+
[:static, '}} static {{'],
|
221
|
+
]
|
222
|
+
actual.must_equal expected
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
describe 'when literal commenting tags are used' do
|
227
|
+
it 'returns the correct tokens' do
|
228
|
+
source = '%%{{ static %%}} more static'
|
229
|
+
actual = Mani::Tokenizer.get_tokens source
|
230
|
+
expected = [
|
231
|
+
[:static, '%{{ static %}} more static'],
|
232
|
+
]
|
233
|
+
actual.must_equal expected
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
data/spec.rb
ADDED
metadata
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mani
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Nick Sinopoli
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-10-14 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: A window automation tool
|
14
|
+
email: NSinopoli@gmail.com
|
15
|
+
executables:
|
16
|
+
- mani
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- .gitignore
|
21
|
+
- .rubocop.yml
|
22
|
+
- Gemfile
|
23
|
+
- Gemfile.lock
|
24
|
+
- LICENSE
|
25
|
+
- Makefile
|
26
|
+
- README.md
|
27
|
+
- bin/mani
|
28
|
+
- examples/chording.rb
|
29
|
+
- examples/hello.rb
|
30
|
+
- examples/workspace_initializer.rb
|
31
|
+
- lib/mani.rb
|
32
|
+
- lib/mani/tokenizer.rb
|
33
|
+
- lib/mani/version.rb
|
34
|
+
- lib/mani/window.rb
|
35
|
+
- lib/mani/x.rb
|
36
|
+
- mani.gemspec
|
37
|
+
- spec.rb
|
38
|
+
- spec/lib/tokenizer_spec.rb
|
39
|
+
homepage: https://github.com/NSinopoli/mani
|
40
|
+
licenses:
|
41
|
+
- BSD (3-Clause)
|
42
|
+
metadata: {}
|
43
|
+
post_install_message:
|
44
|
+
rdoc_options: []
|
45
|
+
require_paths:
|
46
|
+
- lib
|
47
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
48
|
+
requirements:
|
49
|
+
- - '>='
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: '0'
|
52
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
53
|
+
requirements:
|
54
|
+
- - '>='
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: '0'
|
57
|
+
requirements: []
|
58
|
+
rubyforge_project:
|
59
|
+
rubygems_version: 2.1.4
|
60
|
+
signing_key:
|
61
|
+
specification_version: 4
|
62
|
+
summary: A window automation tool
|
63
|
+
test_files:
|
64
|
+
- spec/lib/tokenizer_spec.rb
|