contexts 0.1.0

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 769797619302adbd6183a7498199810826cb6a37
4
+ data.tar.gz: 015c8f23bf692fab0904b72f4e6c9530bf4ac6b7
5
+ SHA512:
6
+ metadata.gz: fd913b15066d17f2e1f7e1253daa729f543b41716f3d91c63c05c38b1114f3080efc3c05d2d59f94420fdfa865e11b6e1b5089cf6de4ad1afa9ffc3a9d88d33f
7
+ data.tar.gz: dc46451e7a778b23fed0589ef360dd1ede8461f41ccbf7ee9ad880c86df98548d316b78b3b3e86854c970c280bae67a24b7968043f98da4ddbf4a4e34c214f3e
data/.editorconfig ADDED
@@ -0,0 +1,10 @@
1
+ root = true
2
+
3
+ [*]
4
+ indent_style = space
5
+ indent_size = 2
6
+ end_of_line = lf
7
+ charset = utf-8
8
+
9
+ trim_trailing_whitespace = true
10
+ insert_final_newline = true
data/.gitignore ADDED
@@ -0,0 +1,7 @@
1
+ *.gem
2
+ /.bundle
3
+ /Gemfile.lock
4
+ /pkg
5
+ /tmp
6
+ /log
7
+ /coverage
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format=doc
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/README.md ADDED
@@ -0,0 +1,28 @@
1
+ # Contexts
2
+
3
+ This gem provides simple way to reuse routes logic in rails apps.
4
+
5
+ ## License
6
+
7
+ Copyright (c) 2014 Kolesnikov Danil
8
+
9
+ MIT License
10
+
11
+ Permission is hereby granted, free of charge, to any person obtaining
12
+ a copy of this software and associated documentation files (the
13
+ "Software"), to deal in the Software without restriction, including
14
+ without limitation the rights to use, copy, modify, merge, publish,
15
+ distribute, sublicense, and/or sell copies of the Software, and to
16
+ permit persons to whom the Software is furnished to do so, subject to
17
+ the following conditions:
18
+
19
+ The above copyright notice and this permission notice shall be
20
+ included in all copies or substantial portions of the Software.
21
+
22
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task default: :spec
data/contexts.gemspec ADDED
@@ -0,0 +1,27 @@
1
+ lib = File.expand_path('../lib', __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+
4
+ require 'contexts/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'contexts'
8
+ spec.version = Contexts::VERSION
9
+ spec.authors = ['Kolesnikov Danil']
10
+ spec.email = ['kolesnikovde@gmail.com']
11
+ spec.summary = 'Simple way to reuse routes logic in rails apps'
12
+ spec.description = 'Simple way to reuse routes logic in rails apps'
13
+ spec.homepage = 'https://github.com/kolesnikovde/contexts'
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^spec/})
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_dependency 'rails', '>= 4.0.0'
22
+ spec.add_development_dependency 'bundler', '~> 1'
23
+ spec.add_development_dependency 'rake', '~> 10'
24
+ spec.add_development_dependency 'rspec', '~> 3'
25
+ spec.add_development_dependency 'rspec-rails'
26
+ spec.add_development_dependency 'codeclimate-test-reporter'
27
+ end
data/lib/contexts.rb ADDED
@@ -0,0 +1,56 @@
1
+ require 'contexts/version'
2
+ require 'contexts/base'
3
+ require 'mapper'
4
+
5
+ module Contexts
6
+ def self.included(base)
7
+ base.class_eval do
8
+ before_action :apply_contexts
9
+
10
+ helper_method :contexts,
11
+ :current_context,
12
+ :locked_context,
13
+ :context_locked?
14
+ end
15
+ end
16
+
17
+ def contexts
18
+ @contexts ||= (request.env['contexts'] || {})
19
+ end
20
+
21
+ def current_context(key = nil)
22
+ if key
23
+ (ctx = contexts[key]) && ctx.current
24
+ else
25
+ Hash[contexts.map{ |name, ctx| [ name, ctx.current ] }]
26
+ end
27
+ end
28
+
29
+ def locked_context(key = nil)
30
+ ctx = (session[:locked_context] ||= {})
31
+
32
+ key ? ctx[key.to_s] : ctx
33
+ end
34
+
35
+ def context_locked?(key = nil)
36
+ locked_context(key).present?
37
+ end
38
+
39
+ def lock_context(data)
40
+ ctx = locked_context
41
+ ctx.merge!(data.stringify_keys).reject!{ |k, v| v.blank? }
42
+ ctx
43
+ end
44
+
45
+ protected
46
+
47
+ def apply_contexts
48
+ contexts.each{ |name, ctx| ctx.apply(self, locked_context(ctx.key)) }
49
+ end
50
+
51
+ def default_url_options
52
+ options = super
53
+ contexts.each{ |name, ctx| options[ctx.key] = ctx.url_option }
54
+ options
55
+ end
56
+ end
@@ -0,0 +1,38 @@
1
+ module Contexts
2
+ class Base
3
+ def key
4
+ end
5
+
6
+ def required?
7
+ true
8
+ end
9
+
10
+ def current
11
+ end
12
+
13
+ def default
14
+ end
15
+
16
+ def constraints
17
+ end
18
+
19
+ def url_option
20
+ current
21
+ end
22
+
23
+ def apply(controller, value)
24
+ end
25
+
26
+ def matches_constraints?(request)
27
+ if (con = constraints).nil?
28
+ true
29
+ elsif con.respond_to?(:matches?)
30
+ con.matches?(request)
31
+ elsif con.is_a?(Proc)
32
+ con.call(request)
33
+ else
34
+ con === request.params[key]
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,3 @@
1
+ module Contexts
2
+ VERSION = '0.1.0'
3
+ end
data/lib/mapper.rb ADDED
@@ -0,0 +1,27 @@
1
+ module ActionDispatch::Routing
2
+ class Mapper
3
+ def contexts(*names, &blk)
4
+ names.reverse.each do |name|
5
+ blk = lambda do |blk|
6
+ lambda { context(name, &blk) }
7
+ end.call(blk)
8
+ end
9
+
10
+ blk.call
11
+ end
12
+
13
+ def context(name, &blk)
14
+ name = "#{name}_context".classify.constantize unless name.is_a?(Class)
15
+ ctx = name.new
16
+ path = ":#{ctx.key}"
17
+ path = "(#{path})" if ctx.required?
18
+
19
+ constraints = lambda do |req|
20
+ (req.env['contexts'] ||= {})[ctx.key] = ctx
21
+ ctx.matches_constraints?(req)
22
+ end
23
+
24
+ scope(path, constraints: constraints, &blk)
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,4 @@
1
+ require 'spec_helper'
2
+
3
+ describe Contexts do
4
+ end
@@ -0,0 +1,117 @@
1
+ require 'spec_helper'
2
+ require 'dummy/app/controllers/application_controller'
3
+ require 'dummy/app/contexts/language_context'
4
+
5
+ shared_examples 'context controller' do
6
+ before(:all) do
7
+ Rails.application.routes.draw do
8
+ contexts :language do
9
+ get '/', to: 'application#index', as: :root
10
+ get '/hello', to: 'application#index', as: :hello_page
11
+ end
12
+ end
13
+ end
14
+
15
+ after(:all) do
16
+ Rails.application.reload_routes!
17
+ end
18
+
19
+ context 'when context has constraints' do
20
+ class RequestBasedConstraint
21
+ def self.matches?(req)
22
+ req.params[:lang] =~ /ru|en/
23
+ end
24
+ end
25
+
26
+ constraints = {
27
+ regexp: /ru|en/,
28
+ proc: ->(req) { req.params[:lang] =~ /ru|en/ },
29
+ request_based: RequestBasedConstraint
30
+ }
31
+
32
+ constraints.each do |name, con|
33
+ it "checks value via #{name}" do
34
+ allow_any_instance_of(LanguageContext).to receive(:constraints).and_return(con)
35
+
36
+ expect(get: '/en').to route_to('application#index', lang: 'en')
37
+ expect(get: '/ru/hello').to route_to('application#index', lang: 'ru')
38
+ expect(get: '/de').not_to be_routable
39
+ end
40
+ end
41
+ end
42
+
43
+ context 'when context has no constraints' do
44
+ it 'allows any value' do
45
+ allow_any_instance_of(LanguageContext).to receive(:constraints).and_return(nil)
46
+
47
+ expect(get: '/').to route_to('application#index')
48
+ expect(get: '/de/').to route_to('application#index', lang: 'de')
49
+ end
50
+ end
51
+
52
+ context 'when context is required' do
53
+ it 'requires value' do
54
+ expect(get: '/').not_to be_routable
55
+ expect(get: '/en/').to route_to('application#index', lang: 'en')
56
+ end
57
+ end
58
+
59
+ describe '#contexts' do
60
+ it 'returns contexts hash' do
61
+ get '/en'
62
+
63
+ expect(controller.contexts[:lang]).to be_a(LanguageContext)
64
+ end
65
+ end
66
+
67
+ describe '#current_context' do
68
+ it 'returns current context value' do
69
+ get '/ru/hello'
70
+
71
+ expect(controller.current_context(:lang)).to eq(:ru)
72
+ expect(controller.current_context).to eq(lang: :ru)
73
+ end
74
+ end
75
+
76
+ context 'url helpers' do
77
+ it 'includes context' do
78
+ get '/ru'
79
+
80
+ expect(controller.hello_page_path).to eq('/ru/hello')
81
+ end
82
+
83
+ it 'uses default value' do
84
+ get '/en'
85
+
86
+ expect(controller.hello_page_path).to eq('/en/hello')
87
+ end
88
+
89
+ it 'allows to customize url option' do
90
+ get '/en'
91
+
92
+ ctx = controller.contexts[:lang]
93
+ allow(ctx).to receive(:url_option) do
94
+ ctx.current if ctx.current != ctx.default
95
+ end
96
+
97
+ expect(controller.hello_page_path).to eq('/hello')
98
+ end
99
+ end
100
+
101
+ context 'context locking' do
102
+ it 'overrides current context' do
103
+ get '/ru/hello'
104
+ expect(controller).not_to be_context_locked(:lang)
105
+
106
+ controller.lock_context(lang: :en)
107
+
108
+ expect(controller).to be_context_locked(:lang)
109
+ expect(controller.locked_context(:lang)).to eq(:en)
110
+ expect(controller.locked_context).to eq('lang' => :en)
111
+ end
112
+ end
113
+ end
114
+
115
+ describe ApplicationController, type: [:controller, :request] do
116
+ it_behaves_like 'context controller'
117
+ end
@@ -0,0 +1,21 @@
1
+ class LanguageContext < Contexts::Base
2
+ def key
3
+ :lang
4
+ end
5
+
6
+ def constraints
7
+ /ru|en/
8
+ end
9
+
10
+ def default
11
+ I18n.default_locale
12
+ end
13
+
14
+ def current
15
+ I18n.locale
16
+ end
17
+
18
+ def apply(ctrl, value)
19
+ I18n.locale = value || ctrl.params[key].presence || default
20
+ end
21
+ end
@@ -0,0 +1,9 @@
1
+ class ApplicationController < ActionController::Base
2
+ include Contexts
3
+
4
+ protect_from_forgery with: :exception
5
+
6
+ def index
7
+ render nothing: true
8
+ end
9
+ end
@@ -0,0 +1,21 @@
1
+ require File.expand_path('../boot', __FILE__)
2
+ require 'action_controller/railtie'
3
+ Bundler.require(*Rails.groups)
4
+ require 'contexts'
5
+
6
+ module Dummy
7
+ class Application < Rails::Application
8
+ config.secret_key_base = 'dummy_key'
9
+ config.session_store :cookie_store, key: '_dummy_session'
10
+ config.cache_classes = true
11
+ config.eager_load = false
12
+ config.consider_all_requests_local = true
13
+ config.action_dispatch.show_exceptions = false
14
+ config.action_controller.perform_caching = false
15
+ config.action_controller.allow_forgery_protection = false
16
+ config.active_support.deprecation = :stderr
17
+
18
+ config.i18n.available_locales = [ :en, :ru ]
19
+ config.i18n.default_locale = :en
20
+ end
21
+ end
@@ -0,0 +1,5 @@
1
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../../../Gemfile', __FILE__)
2
+
3
+ require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
4
+
5
+ $LOAD_PATH.unshift File.expand_path('../../../../lib', __FILE__)
@@ -0,0 +1,3 @@
1
+ require File.expand_path('../application', __FILE__)
2
+
3
+ Rails.application.initialize!
@@ -0,0 +1,11 @@
1
+ require 'codeclimate-test-reporter'
2
+ CodeClimate::TestReporter.start
3
+
4
+ ENV['RAILS_ENV'] = 'test'
5
+
6
+ require File.expand_path('../dummy/config/environment.rb', __FILE__)
7
+ require 'rspec/rails'
8
+
9
+ RSpec.configure do |config|
10
+ config.order = 'random'
11
+ end
metadata ADDED
@@ -0,0 +1,155 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: contexts
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Kolesnikov Danil
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-10-21 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 4.0.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 4.0.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec-rails
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: codeclimate-test-reporter
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: Simple way to reuse routes logic in rails apps
98
+ email:
99
+ - kolesnikovde@gmail.com
100
+ executables: []
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - ".editorconfig"
105
+ - ".gitignore"
106
+ - ".rspec"
107
+ - Gemfile
108
+ - README.md
109
+ - Rakefile
110
+ - contexts.gemspec
111
+ - lib/contexts.rb
112
+ - lib/contexts/base.rb
113
+ - lib/contexts/version.rb
114
+ - lib/mapper.rb
115
+ - spec/contexts_spec.rb
116
+ - spec/controllers/application_controller_spec.rb
117
+ - spec/dummy/app/contexts/language_context.rb
118
+ - spec/dummy/app/controllers/application_controller.rb
119
+ - spec/dummy/config/application.rb
120
+ - spec/dummy/config/boot.rb
121
+ - spec/dummy/config/environment.rb
122
+ - spec/spec_helper.rb
123
+ homepage: https://github.com/kolesnikovde/contexts
124
+ licenses:
125
+ - MIT
126
+ metadata: {}
127
+ post_install_message:
128
+ rdoc_options: []
129
+ require_paths:
130
+ - lib
131
+ required_ruby_version: !ruby/object:Gem::Requirement
132
+ requirements:
133
+ - - ">="
134
+ - !ruby/object:Gem::Version
135
+ version: '0'
136
+ required_rubygems_version: !ruby/object:Gem::Requirement
137
+ requirements:
138
+ - - ">="
139
+ - !ruby/object:Gem::Version
140
+ version: '0'
141
+ requirements: []
142
+ rubyforge_project:
143
+ rubygems_version: 2.2.2
144
+ signing_key:
145
+ specification_version: 4
146
+ summary: Simple way to reuse routes logic in rails apps
147
+ test_files:
148
+ - spec/contexts_spec.rb
149
+ - spec/controllers/application_controller_spec.rb
150
+ - spec/dummy/app/contexts/language_context.rb
151
+ - spec/dummy/app/controllers/application_controller.rb
152
+ - spec/dummy/config/application.rb
153
+ - spec/dummy/config/boot.rb
154
+ - spec/dummy/config/environment.rb
155
+ - spec/spec_helper.rb