diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..9106b2a
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,8 @@
+/.bundle/
+/.yardoc
+/_yardoc/
+/coverage/
+/doc/
+/pkg/
+/spec/reports/
+/tmp/
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..0e40fe8
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,3 @@
+
+# Default ignored files
+/workspace.xml
\ No newline at end of file
diff --git a/.idea/.rakeTasks b/.idea/.rakeTasks
new file mode 100644
index 0000000..26cf061
--- /dev/null
+++ b/.idea/.rakeTasks
@@ -0,0 +1,7 @@
+
+
diff --git a/.idea/kinchan.iml b/.idea/kinchan.iml
new file mode 100644
index 0000000..30b1925
--- /dev/null
+++ b/.idea/kinchan.iml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..8b5ddd5
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..9662f9b
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.rakeTasks b/.rakeTasks
new file mode 100644
index 0000000..26cf061
--- /dev/null
+++ b/.rakeTasks
@@ -0,0 +1,7 @@
+
+
diff --git a/Gemfile b/Gemfile
new file mode 100644
index 0000000..6c69e1c
--- /dev/null
+++ b/Gemfile
@@ -0,0 +1,6 @@
+source "https://rubygems.org"
+
+# Specify your gem's dependencies in kinchan.gemspec
+gemspec
+
+gem "rake", "~> 12.0"
diff --git a/Gemfile.lock b/Gemfile.lock
new file mode 100644
index 0000000..4229a8b
--- /dev/null
+++ b/Gemfile.lock
@@ -0,0 +1,27 @@
+PATH
+ remote: .
+ specs:
+ kinchan (0.1.0)
+ require_all (~> 3.0.0)
+ selenium-webdriver (~> 3.142.6)
+
+GEM
+ remote: https://rubygems.org/
+ specs:
+ childprocess (3.0.0)
+ rake (12.3.2)
+ require_all (3.0.0)
+ rubyzip (2.0.0)
+ selenium-webdriver (3.142.6)
+ childprocess (>= 0.5, < 4.0)
+ rubyzip (>= 1.2.2)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ kinchan!
+ rake (~> 12.0)
+
+BUNDLED WITH
+ 2.1.2
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000..5d70c39
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2019 Kyle McGough
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..295b2cc
--- /dev/null
+++ b/README.md
@@ -0,0 +1,130 @@
+# [Kinchan](https://itazuranakiss.fandom.com/wiki/Kinnosuke_Ikezawa)
+## Composable browser automation for Ruby.
+
+### Requirements
+
+* Ruby
+* Bundler
+
+### Getting Started
+
+Create a new directory for your Kinchan project
+
+`mkdir kinchan_project && cd kinchan_project`
+
+Create the tasks directory where you'll store your Kinchan tasks
+
+`mkdir tasks`
+
+Initialize bundler
+
+`bundle init`
+
+Add the Kinchan gem to your bundle
+
+`bundle add kinchan`
+
+Now that we've got the structure the rest of the readme will teach you how to create, compose, and run Kinchan tasks 👽
+
+### Creating a Task
+
+In your tasks directory create a Ruby file with any name
+
+e.g. `touch ruby_reddit.rb`
+
+In your task's Ruby file you'll need to `require 'kinchan'`
+
+and then define your task as a class that inherits from `Kinchan::Task`
+
+so far your file should look a little something like this
+```ruby
+require 'kinchan'
+
+class VisitRubysReddit < Kinchan::Task
+end
+```
+
+All Kinchan tasks require an execute method that takes a single parameter (the selenium browser object) like so
+
+```ruby
+require 'kinchan'
+
+class VisitRubyReddit < Kinchan::Task
+ def execute(browser)
+ browser.navigate.to 'https://old.reddit.com/r/ruby'
+ end
+end
+```
+
+That's all it takes to create a basic task! If we run it we'll see a browser process start and navigate to the Ruby subreddit. For a full description of the
+browser API check out the [wiki page](https://github.com/SeleniumHQ/selenium/wiki/Ruby-Bindings) for Ruby Selenium (they call it a "driver").
+
+### Running a Task
+
+Create a Ruby file in the root level of your Kinchan project and require your task
+
+Create a new instance of the task and call `run`
+
+e.g.
+
+```ruby
+require_relative 'tasks/ruby_reddit'
+
+VisitRubyReddit.new.run
+```
+
+That's all it takes to run a task. Kinchan handles the rest.
+
+### Passing Data to a Task
+
+A task's initialize function can accept options, just don't forget to call super
+
+e.g.
+
+```ruby
+class Search < Kinchan::Task
+ def initialize(**options)
+ super
+ @query = options[:query]
+ end
+
+ def execute(browser)
+ browser.navigate.to 'https://www.google.com/search?q=#{CGI.escape(@query)}'
+ end
+end
+```
+
+### Composing Tasks
+
+Tasks can call any number of other tasks either before or after they execute, and their dependencies
+will have their dependencies ran and so on
+
+This is done by specifying dependencies with a task's `@before_tasks` or `@after_tasks` in their initialize method
+
+e.g.
+
+```ruby
+class PrintFirstResult < Kinchan::Task
+ def initialize(**options)
+ super
+ @before_tasks << { task: :search, options: options }
+ # specify that the search task should run, with the same options, before running this task
+ end
+
+ def execute(browser)
+ puts browser.execute_script "return document.querySelector('.srg a').innerText"
+ end
+end
+```
+
+Task's do not need to be in the same scope, as long as the task exists Kinchan will find and run it when appropriate
+
+### Setting Selenium Browser Options
+
+before running your task you can modify the selenium browser options like so
+
+```ruby
+Kinchan::Task.browser = :chrome
+Kinchan::Task.browser_options = Selenium::WebDriver::Chrome::Options.new
+Kinchan::Task.browser_options.add_argument('--headless')
+```
diff --git a/Rakefile b/Rakefile
new file mode 100644
index 0000000..43022f7
--- /dev/null
+++ b/Rakefile
@@ -0,0 +1,2 @@
+require "bundler/gem_tasks"
+task :default => :spec
diff --git a/bin/console b/bin/console
new file mode 100755
index 0000000..41a68d0
--- /dev/null
+++ b/bin/console
@@ -0,0 +1,14 @@
+#!/usr/bin/env ruby
+
+require "bundler/setup"
+require "kinchan"
+
+# You can add fixtures and/or initialization code here to make experimenting
+# with your gem easier. You can also use a different console, if you like.
+
+# (If you use this, don't forget to add pry to your Gemfile!)
+# require "pry"
+# Pry.start
+
+require "irb"
+IRB.start(__FILE__)
diff --git a/bin/setup b/bin/setup
new file mode 100755
index 0000000..dce67d8
--- /dev/null
+++ b/bin/setup
@@ -0,0 +1,8 @@
+#!/usr/bin/env bash
+set -euo pipefail
+IFS=$'\n\t'
+set -vx
+
+bundle install
+
+# Do any other automated setup that you need to do here
diff --git a/kinchan.gemspec b/kinchan.gemspec
new file mode 100644
index 0000000..a7e6b9a
--- /dev/null
+++ b/kinchan.gemspec
@@ -0,0 +1,30 @@
+require_relative 'lib/kinchan/version'
+
+Gem::Specification.new do |spec|
+ spec.name = "kinchan"
+ spec.version = Kinchan::VERSION
+ spec.authors = ["Kyle McGough"]
+ spec.email = ["contact@squared.technology"]
+
+ spec.summary = %q{Composable browser automation with Ruby.}
+ spec.description = %q{Composable browser automation with Ruby. Create, compose, and run tasks that automate the browser with Selenium.}
+ spec.homepage = "https://github.com/sosodev/kinchan"
+ spec.license = "MIT"
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
+
+ spec.metadata["homepage_uri"] = spec.homepage
+ spec.metadata["source_code_uri"] = spec.homepage
+ spec.metadata["changelog_uri"] = spec.homepage
+
+ # Specify which files should be added to the gem when it is released.
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
+ end
+ spec.bindir = "exe"
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
+ spec.require_paths = ["lib"]
+
+ spec.add_runtime_dependency 'selenium-webdriver', ['~> 3.142.6']
+ spec.add_runtime_dependency 'require_all', ['~> 3.0.0']
+end
diff --git a/lib/kinchan.rb b/lib/kinchan.rb
new file mode 100644
index 0000000..e427ae5
--- /dev/null
+++ b/lib/kinchan.rb
@@ -0,0 +1,66 @@
+# frozen_string_literal: true
+
+require 'kinchan/version'
+require 'selenium-webdriver'
+require 'require_all'
+
+module Kinchan
+ class Error < StandardError; end
+
+ class Task
+ singleton_class.send(:attr_accessor, :browser)
+ singleton_class.send(:attr_accessor, :browser_options)
+ singleton_class.send(:attr_reader, :descendants)
+ @descendants = []
+ @browser = :chrome
+ @browser_options = nil
+ @@browser_webdriver = nil
+
+ def initialize(**options)
+ @before_tasks = []
+ @after_tasks = []
+ @options = options
+ end
+
+ def self.inherited(subclass)
+ Task.descendants << subclass
+ end
+
+ def self.find_task(task_symbol)
+ Task.descendants.select { |task| task.name.split('::').last.downcase == task_symbol.to_s.downcase }[0]
+ end
+
+ def self.restart_browser
+ unless @@browser_webdriver.nil?
+ @@browser_webdriver.close
+ @@browser_webdriver = Selenium::WebDriver.for Task.browser
+ end
+ end
+
+ def execute(browser); end
+
+ def run
+ if @@browser_webdriver.nil?
+ if Task.browser_options.nil?
+ @@browser_webdriver = Selenium::WebDriver.for Task.browser
+ else
+ @@browser_webdriver = Selenium::WebDriver.for(Task.browser, options: Task.browser_options)
+ end
+ end
+
+ @before_tasks.each do |task_hash|
+ task = Task.find_task(task_hash[:task])
+ task.new(**task_hash[:options]).public_send('run') unless task.nil?
+ end
+
+ execute(@@browser_webdriver)
+
+ @after_tasks.each do |task_hash|
+ task = Task.find_task(task_hash[:task])
+ task.new(**task_hash[:options]).public_send('run') unless task.nil?
+ end
+ end
+ end
+end
+
+require_all 'tasks'
diff --git a/lib/kinchan/version.rb b/lib/kinchan/version.rb
new file mode 100644
index 0000000..4908d19
--- /dev/null
+++ b/lib/kinchan/version.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+module Kinchan
+ VERSION = '0.1.0'
+end