diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ae3fdc2
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,14 @@
+/.bundle/
+/.yardoc
+/Gemfile.lock
+/_yardoc/
+/coverage/
+/doc/
+/pkg/
+/spec/reports/
+/tmp/
+*.bundle
+*.so
+*.o
+*.a
+mkmf.log
diff --git a/Gemfile b/Gemfile
new file mode 100644
index 0000000..dd1c5f5
--- /dev/null
+++ b/Gemfile
@@ -0,0 +1,4 @@
+source 'https://rubygems.org'
+
+# Specify your gem's dependencies in live-front-rails.gemspec
+gemspec
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000..cbf8ed0
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,22 @@
+Copyright (c) 2014 Andrey Krivko
+
+MIT License
+
+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..ee0717e
--- /dev/null
+++ b/README.md
@@ -0,0 +1,104 @@
+# Live Front
+
+Useful helpers used at [Live Typing](http://ltst.ru).
+
+## List of helpers
+
+### TabHelper
+
+Navigation link helper. Returns an `li` element with an 'active' class if the supplied
+controller(s) and/or action(s) are currently active. The content of the
+element is the value passed to the block. Initially was copied from
+[GitLab project](https://gitlab.com/gitlab-org/gitlab-ce). Also it was added ability to specify
+`params` option to make link active when current path suits specified params.
+
+**options**
+
+The options hash used to determine if the element is "active" (default: {})
+
+`:controller` - One or more controller names to check (optional).
+
+`:action` - One or more action names to check (optional).
+
+`:path` - A shorthand path, such as 'dashboard#index', to check (optional).
+
+`:params` - One or more params to check. It also can be specified `:_blank` value to be able to specify either blank or non-blank param (optional).
+
+`:html_options` - Extra options to be passed to the list element (optional).
+
+**block**
+
+An optional block that will become the contents of the returned `li` element.
+
+When both `:controller` and `:action` are specified, BOTH must match in order
+to be marked as active. When only one is given, either can match.
+
+#### Examples:
+
+Assuming we're on `TreeController#show`
+
+```
+# Controller matches, but action doesn't
+nav_link(controller: [:tree, :refs], action: :edit) { "Hello" }
+# => '
Hello'
+```
+
+```
+# Controller matches
+nav_link(controller: [:tree, :refs]) { "Hello" }
+# => 'Hello'
+```
+
+```
+# Shorthand path
+nav_link(path: 'tree#show') { "Hello" }
+# => 'Hello'
+```
+
+```
+# Supplying custom options for the list element
+nav_link(controller: :tree, html_options: {class: 'home'}) { "Hello" }
+# => 'Hello'
+```
+
+```
+# Supplying params options when `params[:state] # => 'archived'`
+nav_link(path: 'tree#show', params: {state: 'archived'}) { "Hello" }
+# => 'Hello'
+```
+
+```
+# Supplying params options when `params[:state] # => nil`
+nav_link(path: 'tree#show', params: {state: 'archived'}) { "Hello" }
+# => 'Hello'
+```
+
+```
+# Supplying params options when `params[:state] # => nil`
+nav_link(path: 'tree#show', params: {state: [:_blank, :active]}) { "Hello" }
+# => 'Hello'
+```
+
+## Installation
+
+Add this line to your application's Gemfile:
+
+```ruby
+gem 'live-front-rails'
+```
+
+And then execute:
+
+ $ bundle
+
+Or install it yourself as:
+
+ $ gem install live-front-rails
+
+## Contributing
+
+1. Fork it ( https://github.com/LiveTyping/live-front-rails/fork )
+2. Create your feature branch (`git checkout -b my-new-feature`)
+3. Commit your changes (`git commit -am 'Add some feature'`)
+4. Push to the branch (`git push origin my-new-feature`)
+5. Create a new Pull Request
diff --git a/Rakefile b/Rakefile
new file mode 100644
index 0000000..721edb6
--- /dev/null
+++ b/Rakefile
@@ -0,0 +1,10 @@
+require 'rspec/core/rake_task'
+require 'bundler/gem_tasks'
+
+# Default directory to look in is `/specs`
+# Run with `rake spec`
+RSpec::Core::RakeTask.new(:spec) do |task|
+ task.rspec_opts = ['--color']
+end
+
+task default: :spec
diff --git a/lib/live_front.rb b/lib/live_front.rb
new file mode 100644
index 0000000..ecf2bb3
--- /dev/null
+++ b/lib/live_front.rb
@@ -0,0 +1,13 @@
+require 'live_front/version'
+require 'rails'
+require 'live_front/application_helper'
+require 'live_front/tab_helper'
+
+module LiveFront
+ class Engine < ::Rails::Engine
+ initializer 'live_front.view_helpers' do
+ ActionView::Base.send :include, LiveFront::ApplicationHelper
+ ActionView::Base.send :include, LiveFront::TabHelper
+ end
+ end
+end
diff --git a/lib/live_front/application_helper.rb b/lib/live_front/application_helper.rb
new file mode 100644
index 0000000..afcdfa5
--- /dev/null
+++ b/lib/live_front/application_helper.rb
@@ -0,0 +1,31 @@
+module LiveFront
+ module ApplicationHelper
+ # Check if a particular controller is the current one
+ #
+ # args - One or more controller names to check
+ #
+ # Examples
+ #
+ # # On TreeController
+ # current_controller?(:tree) # => true
+ # current_controller?(:commits) # => false
+ # current_controller?(:commits, :tree) # => true
+ def current_controller?(*args)
+ args.any? { |v| v.to_s.downcase == controller.controller_name }
+ end
+
+ # Check if a particular action is the current one
+ #
+ # args - One or more action names to check
+ #
+ # Examples
+ #
+ # # On Projects#new
+ # current_action?(:new) # => true
+ # current_action?(:create) # => false
+ # current_action?(:new, :create) # => true
+ def current_action?(*args)
+ args.any? { |v| v.to_s.downcase == action_name }
+ end
+ end
+end
diff --git a/lib/live_front/tab_helper.rb b/lib/live_front/tab_helper.rb
new file mode 100644
index 0000000..d96eeef
--- /dev/null
+++ b/lib/live_front/tab_helper.rb
@@ -0,0 +1,102 @@
+module LiveFront
+ # Copied from GitLab project
+ # https://gitlab.com/gitlab-org/gitlab-ce
+ module TabHelper
+ # Navigation link helper
+ #
+ # Returns an `li` element with an 'active' class if the supplied
+ # controller(s) and/or action(s) are currently active. The content of the
+ # element is the value passed to the block.
+ #
+ # options - The options hash used to determine if the element is "active" (default: {})
+ # :controller - One or more controller names to check (optional).
+ # :action - One or more action names to check (optional).
+ # :path - A shorthand path, such as 'dashboard#index', to check (optional).
+ # :html_options - Extra options to be passed to the list element (optional).
+ # block - An optional block that will become the contents of the returned
+ # `li` element.
+ #
+ # When both :controller and :action are specified, BOTH must match in order
+ # to be marked as active. When only one is given, either can match.
+ #
+ # Examples
+ #
+ # # Assuming we're on TreeController#show
+ #
+ # # Controller matches, but action doesn't
+ # nav_link(controller: [:tree, :refs], action: :edit) { "Hello" }
+ # # => 'Hello'
+ #
+ # # Controller matches
+ # nav_link(controller: [:tree, :refs]) { "Hello" }
+ # # => 'Hello'
+ #
+ # # Shorthand path
+ # nav_link(path: 'tree#show') { "Hello" }
+ # # => 'Hello'
+ #
+ # # Supplying custom options for the list element
+ # nav_link(controller: :tree, html_options: {class: 'home'}) { "Hello" }
+ # # => 'Hello'
+ #
+ # Returns a list item element String
+ def nav_link(options = {}, &block)
+ c, a = fetch_controller_and_action(options)
+ p = options.delete(:params) || {}
+
+ klass = page_active?(c, a, p) ? 'active' : ''
+
+
+ # Add our custom class into the html_options, which may or may not exist
+ # and which may or may not already have a :class key
+ o = options.delete(:html_options) || {}
+ o[:class] = "#{o[:class]} #{klass}".strip
+
+ if block_given?
+ content_tag(:li, capture(&block), o)
+ else
+ content_tag(:li, nil, o)
+ end
+ end
+
+ private
+
+ def fetch_controller_and_action(options)
+ path = options.delete(:path)
+
+ if path
+ if path.respond_to?(:each)
+ c = path.map { |p| p.split('#').first }
+ a = path.map { |p| p.split('#').last }
+ else
+ c, a, _ = path.split('#')
+ end
+ else
+ c = options.delete(:controller)
+ a = options.delete(:action)
+ end
+ [c, a]
+ end
+
+ def page_active?(controller, action, parameters = {})
+ match_action_controller = if controller && action
+ # When given both options, make sure BOTH are active
+ current_controller?(*controller) && current_action?(*action)
+ else
+ # Otherwise check EITHER option
+ current_controller?(*controller) || current_action?(*action)
+ end
+
+ match_action_controller && match_params?(parameters)
+ end
+
+ def match_params?(parameters)
+ return true if parameters.empty?
+
+ parameters.all? do |key, value|
+ value = *value
+ value.any? { |v| v == :_blank ? params[key].to_s.blank? : v.to_s == params[key].to_s }
+ end
+ end
+ end
+end
diff --git a/lib/live_front/version.rb b/lib/live_front/version.rb
new file mode 100644
index 0000000..a7b1f62
--- /dev/null
+++ b/lib/live_front/version.rb
@@ -0,0 +1,3 @@
+module LiveFront
+ VERSION = '0.0.1'.freeze
+end
diff --git a/live-front-rails.gemspec b/live-front-rails.gemspec
new file mode 100644
index 0000000..034afa0
--- /dev/null
+++ b/live-front-rails.gemspec
@@ -0,0 +1,27 @@
+# coding: utf-8
+lib = File.expand_path('../lib', __FILE__)
+$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
+require 'live_front/version'
+
+Gem::Specification.new do |spec|
+ spec.name = 'live-front-rails'
+ spec.version = LiveFront::VERSION
+ spec.authors = ['Andrey Krivko']
+ spec.email = ['jastkand@gmail.com']
+ spec.summary = %q{Useful helpers used at Live Typing}
+ spec.description = %q{Useful helpers used at Live Typing}
+ spec.homepage = ''
+ spec.license = 'MIT'
+
+ spec.files = `git ls-files -z`.split("\x0")
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
+ spec.require_paths = ['lib']
+
+ spec.add_dependency 'rails', '~> 4.1'
+
+ spec.add_development_dependency 'bundler', '~> 1.7'
+ spec.add_development_dependency 'rake', '~> 10.0'
+ spec.add_development_dependency 'rspec', '~> 3.1.0'
+ spec.add_development_dependency 'rspec-mocks', '~> 3.1.0'
+end
diff --git a/spec/helpers/tab_helper_spec.rb b/spec/helpers/tab_helper_spec.rb
new file mode 100644
index 0000000..5faf73f
--- /dev/null
+++ b/spec/helpers/tab_helper_spec.rb
@@ -0,0 +1,49 @@
+require 'spec_helper'
+
+describe LiveFront::TabHelper do
+ describe '#nav_link' do
+ before do
+ allow(self).to receive(:action_name).and_return('foo')
+ end
+
+ it 'captures block output' do
+ expect(nav_link { 'Testing Blocks' }).to match(/Testing Blocks/)
+ end
+
+ it 'performs checks on the current controller' do
+ expect(nav_link(controller: :foo)).to match(//)
+ expect(nav_link(controller: :bar)).not_to match(/active/)
+ expect(nav_link(controller: [:foo, :bar])).to match(/active/)
+ end
+
+ it 'performs checks on the current action' do
+ expect(nav_link(action: :foo)).to match(//)
+ expect(nav_link(action: :bar)).not_to match(/active/)
+ expect(nav_link(action: [:foo, :bar])).to match(/active/)
+ end
+
+ it 'performs checks on both controller and action when both are present' do
+ expect(nav_link(controller: :bar, action: :foo)).not_to match(/active/)
+ expect(nav_link(controller: :foo, action: :bar)).not_to match(/active/)
+ expect(nav_link(controller: :foo, action: :foo)).to match(/active/)
+ end
+
+ it 'accepts a path shorthand' do
+ expect(nav_link(path: 'foo#bar')).not_to match(/active/)
+ expect(nav_link(path: 'foo#foo')).to match(/active/)
+ end
+
+ it 'accepts a params value and checks if they are correct' do
+ expect(nav_link(path: 'foo#foo', params: { p1: :s1 })).to match(/active/)
+ expect(nav_link(path: 'foo#foo', params: { p2: [:_blank, :s2] })).to match(/active/)
+ expect(nav_link(path: 'foo#foo', params: { p2: :s1 })).not_to match(/active/)
+ end
+
+ it 'passes extra html options to the list element' do
+ expect(
+ nav_link(action: :foo, html_options: { class: 'home' })
+ ).to match(//)
+ expect(nav_link(html_options: { class: 'active' })).to match(//)
+ end
+ end
+end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
new file mode 100644
index 0000000..b80bf17
--- /dev/null
+++ b/spec/spec_helper.rb
@@ -0,0 +1,37 @@
+ENV['RAILS_ENV'] ||= 'test'
+
+require 'active_support'
+require 'action_view'
+require 'action_controller'
+
+require 'live_front'
+
+include ActionView::Context
+include ActionView::Helpers::FormTagHelper
+include LiveFront::ApplicationHelper
+include LiveFront::TabHelper
+
+class FooController < ActionController::Base
+ def foo
+ end
+end
+
+def controller
+ FooController.new
+end
+
+def params
+ ActionController::Parameters.new p1: 's1'
+end
+
+require 'rspec/core'
+require 'rspec/mocks'
+
+# RSpec.configure do |config|
+# config.before :all do
+# include ActionView::Helpers::FormTagHelper
+# include LiveFront::ApplicationHelper
+# include LiveFront::TabHelper
+# include ActionView::Context
+# end
+# end