drobot 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,12 @@
1
+ # Drobots
2
+ [![Build Status](https://travis-ci.org/yakshed/drobots.svg?branch=master)](https://travis-ci.org/yakshed/drobots)
3
+
4
+ Helpful little Drobots.
5
+
6
+ ## Contributing
7
+
8
+ 1. Fork it ( https://github.com/yakshed/drobots/fork )
9
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
10
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
11
+ 4. Push to the branch (`git push origin my-new-feature`)
12
+ 5. Create a new Pull Request
@@ -0,0 +1,5 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+ task :default => :spec
@@ -0,0 +1,31 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require './lib/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "drobot"
8
+ spec.version = Drobot::VERSION
9
+ spec.authors = ["Sebastian Schulze", "Lucas Dohmen"]
10
+ spec.email = ["rubygems.org@bascht.com", "lucas@moonbeamlabs.com"]
11
+ spec.summary = "Clever little Robot that downloads your Documents"
12
+ spec.description = "Clever little Robot that downloads your Documents and can't be bothered to write a description."
13
+ spec.homepage = "http://www.yakshed.org"
14
+ spec.license = "GPL-3.0"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "poltergeist", "~> 1.9"
22
+ spec.add_dependency "json-schema", "~> 2.5.0"
23
+ spec.add_dependency "erubis", "~> 2.7.0"
24
+ spec.add_dependency "json", "~> 1.8.3"
25
+ spec.add_development_dependency "bundler", "~> 1.7"
26
+ spec.add_development_dependency "rake", "~> 10.0"
27
+ spec.add_development_dependency "rspec", "~> 3.0"
28
+ spec.add_development_dependency "awesome_print", "~> 1.6"
29
+ spec.add_development_dependency "sinatra", "~> 1.4"
30
+ spec.add_development_dependency "xdg", "~> 2.1"
31
+ end
@@ -0,0 +1,35 @@
1
+ require 'mkmf'
2
+
3
+ module Credentials
4
+ class PasswordstoreProvider
5
+
6
+ def initialize(pass_command: nil, pass_name:)
7
+ default_command = '/usr/bin/pass'
8
+ @pass_command = pass_command || default_command
9
+ @pass_name = pass_name
10
+ end
11
+
12
+ def username
13
+ credentials[:username]
14
+ end
15
+
16
+ def password
17
+ credentials[:password]
18
+ end
19
+
20
+ private
21
+ ##
22
+ # Parse pass output formed like
23
+ #
24
+ # soopa_secret
25
+ # Username: my_user
26
+ def credentials
27
+ return @credentials if @credentials
28
+ output = %x"#{@pass_command} #{@pass_name}".lines
29
+
30
+ password = output.shift
31
+ username = output.find { |line| line.start_with? 'Username:' }.split(":").pop.strip
32
+ @credentials = {:username => username, :password => password}
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,40 @@
1
+ require 'open-uri'
2
+ require 'capybara'
3
+ require 'version'
4
+ require 'drobots'
5
+
6
+ class Drobot
7
+ include Capybara::DSL
8
+
9
+ # Optimized for Programmer Happyness
10
+ BASEDIR = Pathname.new(__dir__).parent
11
+
12
+ def initialize(credential_provider, target = "/tmp/foo")
13
+ @credential_provider = credential_provider
14
+ @target = target
15
+ end
16
+
17
+ def download(url)
18
+ open("#{@target}/#{title}.pdf", 'wb') do |file|
19
+ # puts "Downloading #{url}"
20
+ file << open(url).read
21
+ end
22
+ end
23
+
24
+ def title
25
+ drobot_name = self.class.name.split("::").pop
26
+ prefix + drobot_name.downcase
27
+ end
28
+
29
+ def prefix
30
+ Date.today.strftime("%Y-%m-")
31
+ end
32
+
33
+ def username
34
+ @credential_provider.username
35
+ end
36
+
37
+ def password
38
+ @credential_provider.password
39
+ end
40
+ end
@@ -0,0 +1,2 @@
1
+ module Drobots
2
+ end
@@ -0,0 +1,34 @@
1
+ require 'yaml'
2
+ require 'json-schema'
3
+ require 'credentials/passwordstore_provider'
4
+
5
+ class Runner
6
+ SCHEMA = YAML.load_file(Drobot::BASEDIR.join('lib/runner_config_schema.yaml'))
7
+
8
+ def initialize(config_file: nil)
9
+ default_file = File.join(Dir.home, '.drobots.yaml')
10
+ @config_file = config_file || default_file
11
+ @config = YAML.load_file(@config_file)
12
+
13
+ JSON::Validator.validate!(SCHEMA, @config)
14
+ end
15
+
16
+ def drobots
17
+ @drobots ||= @config['drobots'].map do |name, config|
18
+ credential_provider = Credentials::PasswordstoreProvider.new(pass_name: config['passwordstore']['name'])
19
+ determine_drobot(name).new(credential_provider)
20
+ end
21
+ end
22
+
23
+ def run
24
+ drobots.each(&:run)
25
+ end
26
+
27
+ private
28
+
29
+ def determine_drobot(name)
30
+ drobot = Object.const_get("Drobots::#{name}")
31
+ rescue NameError
32
+ raise "unknown Drobot #{name}"
33
+ end
34
+ end
@@ -0,0 +1,16 @@
1
+ ---
2
+ type: object
3
+ required:
4
+ - drobots
5
+ properties:
6
+ drobots:
7
+ type: object
8
+ patternProperties:
9
+ ".+":
10
+ type: object
11
+ properties:
12
+ passwordstore:
13
+ type: object
14
+ properties:
15
+ name:
16
+ type: string
@@ -0,0 +1,3 @@
1
+ class Drobot
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,72 @@
1
+ require 'fileutils'
2
+ require 'pp'
3
+ require 'drobot'
4
+ require 'runner'
5
+ require 'credentials/passwordstore_provider'
6
+
7
+ require_relative 'fixtures/simple_app'
8
+ require_relative 'fixtures/sample_robot'
9
+ require_relative 'fixtures/drobots/firstsample'
10
+ require_relative 'fixtures/drobots/secondsample'
11
+
12
+ describe "Drobot", :type => :app do
13
+ let(:fixture_dir) { Drobot::BASEDIR.join("spec/fixtures") }
14
+ let(:output_dir) { Drobot::BASEDIR.join("tmp") }
15
+
16
+ describe "runner" do
17
+ context "with valid configuration" do
18
+ subject { Runner.new(config_file: File.join(fixture_dir, 'test_config.yaml')) }
19
+
20
+ it "iterates over multiple drobots" do
21
+ drobots = subject.drobots
22
+ expect(drobots.count).to eq 2
23
+ expect(drobots.first).to be_a(Drobots::Firstsample)
24
+ expect(drobots.last).to be_a(Drobots::Secondsample)
25
+ end
26
+
27
+ it "runs Drobots" do
28
+ subject.run
29
+
30
+ expect(subject.drobots.first.ran?).to be true
31
+ expect(subject.drobots.last.ran?).to be true
32
+ end
33
+ end
34
+ context "with broken configurations" do
35
+ it "gives a proper warning" do
36
+ expect {
37
+ Runner.new(config_file: File.join(fixture_dir, 'broken_config.yaml'))
38
+ }.to raise_exception(JSON::Schema::ValidationError)
39
+ end
40
+
41
+ it "detects non-existing Drobots" do
42
+ expect {
43
+ Runner.new(config_file: File.join(fixture_dir, 'missing_config.yaml')).run
44
+ }.to raise_exception(RuntimeError, /unknown Drobot NonExistingDrobot/)
45
+ end
46
+ end
47
+ end
48
+
49
+ describe "automated download", :type => :feature do
50
+
51
+ let(:runner) { Runner.new }
52
+ let(:credential_provider) { Credentials::PasswordstoreProvider.new(pass_command: "#{fixture_dir}/mock_pass", pass_name: 'sample/app') }
53
+ subject { Drobots::Sample.new(credential_provider, output_dir) }
54
+
55
+ before do
56
+ Capybara.app = SimpleApp.new
57
+ FileUtils.mkdir_p(output_dir)
58
+ end
59
+
60
+ it "sorts files into the correct folder." do
61
+ files = proc { Dir["#{output_dir}/**"] }
62
+ FileUtils.rm(files.call)
63
+
64
+ expect(files.call).to eq []
65
+
66
+ subject.run
67
+
68
+ filename = Date.today.strftime("%Y-%m-sample.pdf")
69
+ expect(files.call).to eq [ File.join(output_dir, filename) ]
70
+ end
71
+ end
72
+ end
@@ -0,0 +1 @@
1
+ output/*
@@ -0,0 +1,6 @@
1
+ ---
2
+ drobots:
3
+ Firstsample:
4
+ Secondsample:
5
+ passwordstore:
6
+ name: 'second/sample'
@@ -0,0 +1,3 @@
1
+ require_relative 'simple_app'
2
+
3
+ run SimpleApp.new
@@ -0,0 +1,12 @@
1
+ require 'drobot'
2
+
3
+ module Drobots
4
+ class Firstsample < Drobot
5
+ def run
6
+ @ran = true
7
+ end
8
+ def ran?
9
+ @ran
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ require 'drobot'
2
+
3
+ module Drobots
4
+ class Secondsample < Drobot
5
+ def run
6
+ @ran = true
7
+ end
8
+ def ran?
9
+ @ran
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,8 @@
1
+ ---
2
+ drobots:
3
+ NonExistingDrobot:
4
+ passwordstore:
5
+ name: 'nonexisting/sample'
6
+ Secondsample:
7
+ passwordstore:
8
+ name: 'second/sample'
@@ -0,0 +1,3 @@
1
+ #!/bin/sh
2
+ echo "soopa_secret"
3
+ echo "Username: my_user"
@@ -0,0 +1,15 @@
1
+ class Drobots::Sample < Drobot
2
+ def run
3
+ visit "/"
4
+ click_link 'login'
5
+
6
+ within('#myform') do
7
+ fill_in 'username', :with => username
8
+ fill_in 'password', :with => password
9
+ click_button 'Yo'
10
+ end
11
+
12
+ l = find_link 'Mega Secure Download'
13
+ download(l[:href])
14
+ end
15
+ end
@@ -0,0 +1,22 @@
1
+ require 'sinatra'
2
+ require 'tilt/erubis'
3
+
4
+ class SimpleApp < Sinatra::Base
5
+ get '/' do
6
+ erb :simple_index
7
+ end
8
+
9
+ get '/login' do
10
+ erb :simple_login
11
+ end
12
+
13
+ post '/login' do
14
+ @authenticated = (@params[:username] == 'my_user' && @params[:password] == 'soopa_secret')
15
+ erb :simple_login
16
+ end
17
+
18
+ get '/other' do
19
+ erb :simple_other
20
+ end
21
+ end
22
+
@@ -0,0 +1,8 @@
1
+ ---
2
+ drobots:
3
+ Firstsample:
4
+ passwordstore:
5
+ name: 'first/sample'
6
+ Secondsample:
7
+ passwordstore:
8
+ name: 'second/sample'
@@ -0,0 +1,5 @@
1
+ <h1>Welcome!</h1>
2
+ <a href="/other">other</a>
3
+ <a href="/login">login</a>
4
+ <a href="/download.pdf">download</a>
5
+
@@ -0,0 +1,11 @@
1
+ <% if @authenticated -%>
2
+ <a href="/sample.pdf">Mega Secure Download</a>
3
+ <% else %>
4
+ <h1>Go away</h1>
5
+ <% end %>
6
+ <hr/>
7
+ <form id="myform" action="/login" method="post">
8
+ <input name="username" type="text" value=""/>
9
+ <input name="password" type="text" value=""/>
10
+ <input name="submit" type="submit" value="Yo" />
11
+ </form>
@@ -0,0 +1 @@
1
+ <h1>Hurrah!</h1>
@@ -0,0 +1,101 @@
1
+ require 'capybara/rspec'
2
+ require 'capybara/poltergeist'
3
+
4
+ # This file was generated by the `rspec --init` command. Conventionally, all
5
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
6
+ # The generated `.rspec` file contains `--require spec_helper` which will cause
7
+ # this file to always be loaded, without a need to explicitly require it in any
8
+ # files.
9
+ #
10
+ # Given that it is always loaded, you are encouraged to keep this file as
11
+ # light-weight as possible. Requiring heavyweight dependencies from this file
12
+ # will add to the boot time of your test suite on EVERY test run, even for an
13
+ # individual file that may not need all of that loaded. Instead, consider making
14
+ # a separate helper file that requires the additional dependencies and performs
15
+ # the additional setup, and require it from the spec files that actually need
16
+ # it.
17
+ #
18
+ # The `.rspec` file also contains a few flags that are not defaults but that
19
+ # users commonly want.
20
+ #
21
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
22
+ RSpec.configure do |config|
23
+ # rspec-expectations config goes here. You can use an alternate
24
+ # assertion/expectation library such as wrong or the stdlib/minitest
25
+ # assertions if you prefer.
26
+ config.expect_with :rspec do |expectations|
27
+ # This option will default to `true` in RSpec 4. It makes the `description`
28
+ # and `failure_message` of custom matchers include text for helper methods
29
+ # defined using `chain`, e.g.:
30
+ # be_bigger_than(2).and_smaller_than(4).description
31
+ # # => "be bigger than 2 and smaller than 4"
32
+ # ...rather than:
33
+ # # => "be bigger than 2"
34
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
35
+ end
36
+
37
+ # rspec-mocks config goes here. You can use an alternate test double
38
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
39
+ config.mock_with :rspec do |mocks|
40
+ # Prevents you from mocking or stubbing a method that does not exist on
41
+ # a real object. This is generally recommended, and will default to
42
+ # `true` in RSpec 4.
43
+ mocks.verify_partial_doubles = true
44
+ end
45
+
46
+ # The settings below are suggested to provide a good initial experience
47
+ # with RSpec, but feel free to customize to your heart's content.
48
+ =begin
49
+ # These two settings work together to allow you to limit a spec run
50
+ # to individual examples or groups you care about by tagging them with
51
+ # `:focus` metadata. When nothing is tagged with `:focus`, all examples
52
+ # get run.
53
+ config.filter_run :focus
54
+ config.run_all_when_everything_filtered = true
55
+
56
+ # Allows RSpec to persist some state between runs in order to support
57
+ # the `--only-failures` and `--next-failure` CLI options. We recommend
58
+ # you configure your source control system to ignore this file.
59
+ config.example_status_persistence_file_path = "spec/examples.txt"
60
+
61
+ # Limits the available syntax to the non-monkey patched syntax that is
62
+ # recommended. For more details, see:
63
+ # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/
64
+ # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
65
+ # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode
66
+ config.disable_monkey_patching!
67
+
68
+ # This setting enables warnings. It's recommended, but in some cases may
69
+ # be too noisy due to issues in dependencies.
70
+ config.warnings = true
71
+
72
+ # Many RSpec users commonly either run the entire suite or an individual
73
+ # file, and it's useful to allow more verbose output when running an
74
+ # individual spec file.
75
+ if config.files_to_run.one?
76
+ # Use the documentation formatter for detailed output,
77
+ # unless a formatter has already been configured
78
+ # (e.g. via a command-line flag).
79
+ config.default_formatter = 'doc'
80
+ end
81
+
82
+ # Print the 10 slowest examples and example groups at the
83
+ # end of the spec run, to help surface which specs are running
84
+ # particularly slow.
85
+ config.profile_examples = 10
86
+
87
+ # Run specs in random order to surface order dependencies. If you find an
88
+ # order dependency and want to debug it, you can fix the order by providing
89
+ # the seed, which is printed after each run.
90
+ # --seed 1234
91
+ config.order = :random
92
+
93
+ # Seed global randomization in this process using the `--seed` CLI option.
94
+ # Setting this allows you to use `--seed` to deterministically reproduce
95
+ # test failures related to randomization by passing the same `--seed` value
96
+ # as the one that triggered the failure.
97
+ Kernel.srand config.seed
98
+ =end
99
+ end
100
+
101
+ Capybara.default_driver = :poltergeist