ruby-features 1.0.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.
- data/LICENSE +21 -0
- data/README.md +4 -0
- data/lib/generators/ruby_features/install_generator.rb +13 -0
- data/lib/generators/ruby_features/templates/ruby-features.rb +1 -0
- data/lib/ruby-features.rb +45 -0
- data/lib/ruby-features/concern.rb +50 -0
- data/lib/ruby-features/container.rb +13 -0
- data/lib/ruby-features/lazy.rb +38 -0
- data/lib/ruby-features/mixins.rb +27 -0
- data/lib/ruby-features/single.rb +36 -0
- data/lib/ruby-features/utils.rb +51 -0
- data/lib/ruby-features/utils/const_accessor_19.rb +32 -0
- data/lib/ruby-features/utils/const_accessor_20.rb +13 -0
- data/lib/ruby-features/version.rb +3 -0
- data/spec/define_spec.rb +69 -0
- data/spec/find_and_apply_spec.rb +44 -0
- data/spec/lazy_apply_spec.rb +19 -0
- data/spec/ruby_features/nested/nested_feature.rb +9 -0
- data/spec/ruby_features/root_feature.rb +9 -0
- data/spec/spec_helper.rb +7 -0
- data/spec/support/helpers.rb +25 -0
- metadata +89 -0
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2015 Sergey Tokarenko
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
module RubyFeatures
|
2
|
+
module Generators
|
3
|
+
class InstallGenerator < Rails::Generators::Base
|
4
|
+
desc 'Copy RubyFeatures initializer'
|
5
|
+
source_root File.expand_path('../templates', __FILE__)
|
6
|
+
|
7
|
+
def copy_initializer
|
8
|
+
template 'ruby-features.rb', 'config/initializers/ruby-features.rb'
|
9
|
+
end
|
10
|
+
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
RubyFeatures.find_in_path(Rails.root.join('app/models/concerns')).apply_all
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'ruby-features/version'
|
2
|
+
|
3
|
+
module RubyFeatures
|
4
|
+
autoload :Container, 'ruby-features/container'
|
5
|
+
autoload :Single, 'ruby-features/single'
|
6
|
+
autoload :Mixins, 'ruby-features/mixins'
|
7
|
+
autoload :Concern, 'ruby-features/concern'
|
8
|
+
autoload :Utils, 'ruby-features/utils'
|
9
|
+
autoload :Lazy, 'ruby-features/lazy'
|
10
|
+
|
11
|
+
module Generators
|
12
|
+
autoload :InstallGenerator, 'generators/ruby-features/install_generator'
|
13
|
+
end
|
14
|
+
|
15
|
+
@@features = {}
|
16
|
+
|
17
|
+
class << self
|
18
|
+
def find_in_path(*folders)
|
19
|
+
old_feature_names = @@features.keys
|
20
|
+
|
21
|
+
Dir[*folders.map{|folder| File.join(folder, '**', '*_feature.rb') }].each do |file|
|
22
|
+
require file
|
23
|
+
end
|
24
|
+
|
25
|
+
Container.new(@@features.keys - old_feature_names)
|
26
|
+
end
|
27
|
+
|
28
|
+
def define(feature_name, &feature_body)
|
29
|
+
feature = Single.new(feature_name, feature_body)
|
30
|
+
feature_name = feature.name
|
31
|
+
raise NameError.new("Such feature is already registered: #{feature_name}") if @@features.has_key?(feature_name)
|
32
|
+
|
33
|
+
@@features[feature_name] = feature
|
34
|
+
end
|
35
|
+
|
36
|
+
def apply(*feature_names)
|
37
|
+
feature_names.each do |feature_name|
|
38
|
+
raise NameError.new("Such feature is not registered: #{feature_name}") unless @@features.has_key?(feature_name)
|
39
|
+
|
40
|
+
@@features[feature_name].apply
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module RubyFeatures
|
2
|
+
module Concern
|
3
|
+
|
4
|
+
def _apply(target, feature_name)
|
5
|
+
target_class = RubyFeatures::Utils.ruby_const_get(self, "::#{target}")
|
6
|
+
|
7
|
+
_apply_methods(target_class, feature_name, :class)
|
8
|
+
_apply_methods(target_class, feature_name, :instance)
|
9
|
+
_apply_applied_blocks(target_class)
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def self.extended(base)
|
15
|
+
base.instance_variable_set(:@_applied_blocks, [])
|
16
|
+
end
|
17
|
+
|
18
|
+
def applied(&block)
|
19
|
+
instance_variable_get(:@_applied_blocks) << block
|
20
|
+
end
|
21
|
+
|
22
|
+
def class_methods(&block)
|
23
|
+
RubyFeatures::Utils.prepare_module(self, 'ClassMethods').class_eval(&block)
|
24
|
+
end
|
25
|
+
|
26
|
+
def instance_methods(&block)
|
27
|
+
RubyFeatures::Utils.prepare_module(self, 'InstanceMethods').class_eval(&block)
|
28
|
+
end
|
29
|
+
|
30
|
+
def _apply_methods(target_class, feature_name, methods_type)
|
31
|
+
methods_module_name = "#{methods_type.capitalize}Methods"
|
32
|
+
|
33
|
+
if RubyFeatures::Utils.module_defined?(self, methods_module_name)
|
34
|
+
methods_module = const_get(methods_module_name)
|
35
|
+
common_methods = target_class.public_send(:"#{'instance_' if methods_type == :instance}methods") & methods_module.instance_methods
|
36
|
+
raise NameError.new("Feature #{feature_name} tried to define already existing #{methods_type} methods: #{common_methods.inspect}") unless common_methods.empty?
|
37
|
+
|
38
|
+
target_class.send((methods_type == :instance ? :include : :extend), methods_module)
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
def _apply_applied_blocks(target_class)
|
44
|
+
instance_variable_get(:@_applied_blocks).each do |applied_block|
|
45
|
+
target_class.class_eval(&applied_block)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module RubyFeatures
|
2
|
+
module Lazy
|
3
|
+
|
4
|
+
ACTIVE_SUPPORT_LAZY_TARGETS = %w(
|
5
|
+
action_view action_controller action_mailer
|
6
|
+
active_record active_job
|
7
|
+
i18n
|
8
|
+
).map(&:to_sym).freeze
|
9
|
+
|
10
|
+
@active_support_available = begin
|
11
|
+
require 'active_support'
|
12
|
+
true
|
13
|
+
rescue LoadError
|
14
|
+
false
|
15
|
+
end
|
16
|
+
|
17
|
+
class << self
|
18
|
+
def active_support_available?
|
19
|
+
@active_support_available
|
20
|
+
end
|
21
|
+
|
22
|
+
def apply(target, &block)
|
23
|
+
if active_support_available?
|
24
|
+
target_namespace = RubyFeatures::Utils.underscore(target.split('::').first).to_sym
|
25
|
+
|
26
|
+
if ACTIVE_SUPPORT_LAZY_TARGETS.include?(target_namespace)
|
27
|
+
ActiveSupport.on_load target_namespace, yield: true, &block
|
28
|
+
|
29
|
+
return
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
yield
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module RubyFeatures
|
2
|
+
module Mixins
|
3
|
+
class << self
|
4
|
+
|
5
|
+
def build_and_apply!(feature)
|
6
|
+
feature_module = RubyFeatures::Utils.prepare_module!(
|
7
|
+
self,
|
8
|
+
RubyFeatures::Utils.modulize(feature.name)
|
9
|
+
)
|
10
|
+
|
11
|
+
feature.apply_to_blocks.each do |target, blocks|
|
12
|
+
RubyFeatures::Lazy.apply(target) do
|
13
|
+
_lazy_build_and_apply!(feature, feature_module, target, blocks)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def _lazy_build_and_apply!(feature, feature_module, target, blocks)
|
19
|
+
lazy_module = RubyFeatures::Utils.prepare_module!(feature_module, target)
|
20
|
+
lazy_module.extend RubyFeatures::Concern
|
21
|
+
blocks.each { |block| lazy_module.class_eval(&block) }
|
22
|
+
lazy_module._apply(target, feature.name)
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module RubyFeatures
|
2
|
+
class Single
|
3
|
+
|
4
|
+
attr_reader :name, :applied, :apply_to_blocks
|
5
|
+
alias applied? applied
|
6
|
+
|
7
|
+
def initialize(name, feature_body)
|
8
|
+
@name = name = name.to_s
|
9
|
+
raise NameError.new("Wrong feature name: #{name}") unless name.match(/^[\/_a-z\d]+$/)
|
10
|
+
|
11
|
+
@apply_to_blocks = {}
|
12
|
+
@applied = false
|
13
|
+
|
14
|
+
instance_eval(&feature_body) if feature_body
|
15
|
+
end
|
16
|
+
|
17
|
+
def apply
|
18
|
+
unless applied?
|
19
|
+
Mixins.build_and_apply!(self)
|
20
|
+
|
21
|
+
@apply_to_blocks = nil
|
22
|
+
@applied = true
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def apply_to(target, &block)
|
29
|
+
target = target.to_s
|
30
|
+
|
31
|
+
@apply_to_blocks[target] ||= []
|
32
|
+
@apply_to_blocks[target] << block
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module RubyFeatures
|
2
|
+
module Utils
|
3
|
+
|
4
|
+
autoload :ConstAccessor19, 'ruby-features/utils/const_accessor_19'
|
5
|
+
autoload :ConstAccessor20, 'ruby-features/utils/const_accessor_20'
|
6
|
+
|
7
|
+
begin
|
8
|
+
const_defined?('Some::Const')
|
9
|
+
extend ConstAccessor20
|
10
|
+
rescue NameError
|
11
|
+
extend ConstAccessor19
|
12
|
+
end
|
13
|
+
|
14
|
+
class << self
|
15
|
+
|
16
|
+
def module_defined?(target, module_name)
|
17
|
+
ruby_const_defined?(target, module_name) && ruby_const_get(target, module_name).name.start_with?(target.name)
|
18
|
+
end
|
19
|
+
|
20
|
+
def prepare_module!(target, module_name)
|
21
|
+
module_defined?(target, module_name) ?
|
22
|
+
raise(NameError.new("Module already initiated: #{target.name}::#{module_name}")) :
|
23
|
+
prepare_module(target, module_name)
|
24
|
+
end
|
25
|
+
|
26
|
+
def prepare_module(target, modules)
|
27
|
+
modules = modules.split('::') unless modules.kind_of?(Array)
|
28
|
+
|
29
|
+
first_submodule = modules.shift
|
30
|
+
new_target = module_defined?(target, first_submodule) ?
|
31
|
+
target.const_get(first_submodule) :
|
32
|
+
target.const_set(first_submodule, Module.new)
|
33
|
+
|
34
|
+
modules.empty? ?
|
35
|
+
new_target :
|
36
|
+
prepare_module(new_target, modules)
|
37
|
+
end
|
38
|
+
|
39
|
+
def modulize(string)
|
40
|
+
string.gsub(/([a-z\d]+)/i) { $1.capitalize }.gsub(/[-_]/, '').gsub('/', '::')
|
41
|
+
end
|
42
|
+
|
43
|
+
def underscore(string)
|
44
|
+
string.gsub(/([A-Z][a-z\d]*)/){ "_#{$1.downcase}" }.
|
45
|
+
gsub(/^_/, '').
|
46
|
+
gsub('::', '/')
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module RubyFeatures
|
2
|
+
module Utils
|
3
|
+
module ConstAccessor19
|
4
|
+
def ruby_const_defined?(target, const_parts)
|
5
|
+
const_parts = const_parts.split('::') unless const_parts.kind_of?(Array)
|
6
|
+
|
7
|
+
first_const_part = const_parts.shift
|
8
|
+
first_const_defined = target.const_defined?(first_const_part)
|
9
|
+
|
10
|
+
!first_const_defined || const_parts.empty? ?
|
11
|
+
first_const_defined :
|
12
|
+
ruby_const_defined?(target.const_get(first_const_part), const_parts)
|
13
|
+
end
|
14
|
+
|
15
|
+
def ruby_const_get(target, const_parts)
|
16
|
+
const_parts = const_parts.split('::') unless const_parts.kind_of?(Array)
|
17
|
+
|
18
|
+
first_const_part = const_parts.shift
|
19
|
+
if first_const_part == ''
|
20
|
+
target = ::Object
|
21
|
+
first_const_part = const_parts.shift
|
22
|
+
end
|
23
|
+
|
24
|
+
first_const = target.const_get(first_const_part)
|
25
|
+
|
26
|
+
const_parts.empty? ?
|
27
|
+
first_const :
|
28
|
+
ruby_const_get(first_const, const_parts)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/spec/define_spec.rb
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
describe RubyFeatures::Single do
|
2
|
+
|
3
|
+
prepare_test_class 'DefineTestModule::DefineTestClass'
|
4
|
+
|
5
|
+
it 'should apply class methods' do
|
6
|
+
expect{
|
7
|
+
define_test_feature('class_methods') do
|
8
|
+
class_methods do
|
9
|
+
def test_class_method; end
|
10
|
+
end
|
11
|
+
end.apply
|
12
|
+
}.to change{test_class.respond_to?(:test_class_method)}.from(false).to(true)
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'should apply instance methods' do
|
16
|
+
expect{
|
17
|
+
define_test_feature('instance_methods') do
|
18
|
+
instance_methods do
|
19
|
+
def test_instance_method; end
|
20
|
+
end
|
21
|
+
end.apply
|
22
|
+
}.to change{test_class.new.respond_to?(:test_instance_method)}.from(false).to(true)
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'should process applied block' do
|
26
|
+
expect{
|
27
|
+
define_test_feature('applied_block') do
|
28
|
+
applied do
|
29
|
+
attr_accessor :applied_block_accessor
|
30
|
+
end
|
31
|
+
end.apply
|
32
|
+
}.to change{test_class.new.respond_to?(:applied_block_accessor)}.from(false).to(true)
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'should check that feature name is correct' do
|
36
|
+
expect{
|
37
|
+
define_test_feature('wrong feature name')
|
38
|
+
}.to raise_error(/Wrong feature name/)
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'should raise error if target already has feature class method' do
|
42
|
+
test_class.class_eval do
|
43
|
+
def self.existing_class_method; end
|
44
|
+
end
|
45
|
+
|
46
|
+
expect{
|
47
|
+
define_test_feature('existing_class_method') do
|
48
|
+
class_methods do
|
49
|
+
def existing_class_method; end
|
50
|
+
end
|
51
|
+
end.apply
|
52
|
+
}.to raise_error(/tried to define already existing class methods: \[:existing_class_method\]/)
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'should raise error if target already has feature instance method' do
|
56
|
+
test_class.class_eval do
|
57
|
+
def existing_instance_method; end
|
58
|
+
end
|
59
|
+
|
60
|
+
expect{
|
61
|
+
define_test_feature('existing_instance_method') do
|
62
|
+
instance_methods do
|
63
|
+
def existing_instance_method; end
|
64
|
+
end
|
65
|
+
end.apply
|
66
|
+
}.to raise_error(/tried to define already existing instance methods: \[:existing_instance_method\]/)
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
describe RubyFeatures do
|
2
|
+
|
3
|
+
prepare_test_class('FindAndApplyTestClass')
|
4
|
+
|
5
|
+
it 'should find features in path and apply all on demand' do
|
6
|
+
features_container = subject.find_in_path(File.expand_path('../ruby_features', __FILE__))
|
7
|
+
|
8
|
+
expect(test_class).to_not respond_to(:root_method)
|
9
|
+
expect(test_class).to_not respond_to(:nested_method)
|
10
|
+
|
11
|
+
features_container.apply_all
|
12
|
+
|
13
|
+
expect(test_class).to respond_to(:root_method)
|
14
|
+
expect(test_class).to respond_to(:nested_method)
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'should apply features by name' do
|
18
|
+
|
19
|
+
define_test_feature('manual') do
|
20
|
+
class_methods do
|
21
|
+
def manual_method; end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
expect(test_class).to_not respond_to(:manual_method)
|
26
|
+
RubyFeatures.apply('find_and_apply_test_class/manual')
|
27
|
+
expect(test_class).to respond_to(:manual_method)
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'should raise error if trying to apply not existing feature' do
|
31
|
+
expect{
|
32
|
+
RubyFeatures.apply('find_and_apply_test_class/not_existing_feature')
|
33
|
+
}.to raise_error(/Such feature is not registered/)
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'should raise error if trying to define already registered feature' do
|
37
|
+
RubyFeatures.define('find_and_apply_test_class/duplicate_feature')
|
38
|
+
|
39
|
+
expect{
|
40
|
+
RubyFeatures.define('find_and_apply_test_class/duplicate_feature')
|
41
|
+
}.to raise_error(/Such feature is already registered/)
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
if RubyFeatures::Lazy.active_support_available?
|
2
|
+
describe RubyFeatures::Lazy do
|
3
|
+
|
4
|
+
it 'should use ActiveSupport lazy load' do
|
5
|
+
RubyFeatures.define 'lazy_load/active_support' do
|
6
|
+
apply_to 'ActiveRecord::Base' do
|
7
|
+
class_methods do
|
8
|
+
def lazy_active_support; end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end.apply
|
12
|
+
expect(defined?(ActiveRecord)).to_not be true
|
13
|
+
|
14
|
+
require 'active_record'
|
15
|
+
expect(ActiveRecord::Base).to respond_to(:lazy_active_support)
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
RSpec.configure do
|
2
|
+
|
3
|
+
def prepare_test_class(class_parts)
|
4
|
+
class_parts = class_parts.split('::') unless class_parts.kind_of?(Array)
|
5
|
+
|
6
|
+
test_class = Object
|
7
|
+
|
8
|
+
until class_parts.empty?
|
9
|
+
test_class = class_parts.size > 1 ?
|
10
|
+
test_class.const_set(class_parts.shift, Module.new) :
|
11
|
+
test_class.const_set(class_parts.shift, Class.new)
|
12
|
+
end
|
13
|
+
|
14
|
+
let(:test_class){ test_class }
|
15
|
+
end
|
16
|
+
|
17
|
+
def define_test_feature(feature_name_postfix, &block)
|
18
|
+
test_class_name = test_class.name
|
19
|
+
feature_name = "#{RubyFeatures::Utils.underscore(test_class_name)}/#{feature_name_postfix}"
|
20
|
+
|
21
|
+
RubyFeatures.define feature_name do
|
22
|
+
apply_to test_class_name, &block
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
metadata
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ruby-features
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Sergey Tokarenko
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2015-05-17 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: bundler
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
description: Makes extending of Ruby classes and modules to be easy, safe and controlled.
|
31
|
+
email: private.tokarenko.sergey@gmail.com
|
32
|
+
executables: []
|
33
|
+
extensions: []
|
34
|
+
extra_rdoc_files: []
|
35
|
+
files:
|
36
|
+
- lib/generators/ruby_features/install_generator.rb
|
37
|
+
- lib/generators/ruby_features/templates/ruby-features.rb
|
38
|
+
- lib/ruby-features/concern.rb
|
39
|
+
- lib/ruby-features/container.rb
|
40
|
+
- lib/ruby-features/lazy.rb
|
41
|
+
- lib/ruby-features/mixins.rb
|
42
|
+
- lib/ruby-features/single.rb
|
43
|
+
- lib/ruby-features/utils/const_accessor_19.rb
|
44
|
+
- lib/ruby-features/utils/const_accessor_20.rb
|
45
|
+
- lib/ruby-features/utils.rb
|
46
|
+
- lib/ruby-features/version.rb
|
47
|
+
- lib/ruby-features.rb
|
48
|
+
- LICENSE
|
49
|
+
- README.md
|
50
|
+
- spec/define_spec.rb
|
51
|
+
- spec/find_and_apply_spec.rb
|
52
|
+
- spec/lazy_apply_spec.rb
|
53
|
+
- spec/ruby_features/nested/nested_feature.rb
|
54
|
+
- spec/ruby_features/root_feature.rb
|
55
|
+
- spec/spec_helper.rb
|
56
|
+
- spec/support/helpers.rb
|
57
|
+
homepage: https://github.com/stokarenko/ruby-features
|
58
|
+
licenses:
|
59
|
+
- MIT
|
60
|
+
post_install_message:
|
61
|
+
rdoc_options: []
|
62
|
+
require_paths:
|
63
|
+
- lib
|
64
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 1.9.3
|
70
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
71
|
+
none: false
|
72
|
+
requirements:
|
73
|
+
- - ! '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
requirements: []
|
77
|
+
rubyforge_project:
|
78
|
+
rubygems_version: 1.8.23.2
|
79
|
+
signing_key:
|
80
|
+
specification_version: 3
|
81
|
+
summary: Makes extending of Ruby classes and modules to be easy, safe and controlled.
|
82
|
+
test_files:
|
83
|
+
- spec/define_spec.rb
|
84
|
+
- spec/find_and_apply_spec.rb
|
85
|
+
- spec/lazy_apply_spec.rb
|
86
|
+
- spec/ruby_features/nested/nested_feature.rb
|
87
|
+
- spec/ruby_features/root_feature.rb
|
88
|
+
- spec/spec_helper.rb
|
89
|
+
- spec/support/helpers.rb
|