inherited_class_var 0.2.2 → 1.0.0.beta1
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 +4 -4
- data/.gitignore +1 -0
- data/README.md +35 -63
- data/inherited_class_var.gemspec +3 -3
- data/lib/inherited_class_var/hash.rb +15 -0
- data/lib/inherited_class_var/variable.rb +68 -0
- data/lib/inherited_class_var/version.rb +1 -1
- data/lib/inherited_class_var.rb +12 -57
- metadata +10 -9
- data/lib/inherited_class_var/cache.rb +0 -54
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c26fdce40dde8a1b5da981e01f10ac4c8a7c6fa3
|
4
|
+
data.tar.gz: 84f8404281c828b41232cf61e5f4bc8b60de8bd1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 097aedaabecfb5b04c235f89267167fda5a9ec45aca6c209befbce890cf8fcbeb824f46be58b8861e6625c29b689358fdf188678b13ee18cecac7f324aaeae38
|
7
|
+
data.tar.gz: 6a53d905f6f9cb5675a203775d68d56edc433d2283575881eb4bd8a4a96c3a8e1c8bebd57150e20153072848fde10c256f2d6991ae3087cadcc62e1d648bde94
|
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -1,17 +1,27 @@
|
|
1
|
-
# InheritedClassVar
|
1
|
+
# InheritedClassVar [](https://travis-ci.org/FinalCAD/inherited_class_var)[](https://codeclimate.com/github/FinalCAD/inherited_class_var)[](https://gemnasium.com/FinalCAD/inherited_class_var)[](https://coveralls.io/github/FinalCAD/inherited_class_var?branch=master)[](http://badge.fury.io/rb/inherited_class_var)
|
2
2
|
|
3
|
-
|
3
|
+
Implement class variables that inherit from their ancestors. Such as a `Hash`:
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
[](https://gemnasium.com/FinalCAD/inherited_class_var)
|
8
|
-
|
9
|
-
[](https://travis-ci.org/FinalCAD/inherited_class_var) (Travis CI)
|
5
|
+
```ruby
|
6
|
+
require 'inherited_class_var'
|
10
7
|
|
11
|
-
|
8
|
+
class Bird
|
9
|
+
include InheritedClassVar
|
10
|
+
inherited_class_hash :attributes #, shallow: false, reverse: false (default aoptions)
|
11
|
+
|
12
|
+
def self.attribute(attribute_name, options={})
|
13
|
+
attributes_object.merge(attribute_name.to_sym => options)
|
14
|
+
end
|
15
|
+
attribute :name, upcase: true
|
16
|
+
end
|
12
17
|
|
13
|
-
|
18
|
+
class Duck < Bird
|
19
|
+
attribute :flying, default: false
|
20
|
+
end
|
14
21
|
|
22
|
+
Bird.attributes # => { name: upcase: true }
|
23
|
+
Duck.attributes # => { name: upcase: true, flying: false }
|
24
|
+
```
|
15
25
|
|
16
26
|
## Installation
|
17
27
|
|
@@ -31,70 +41,32 @@ Or install it yourself as:
|
|
31
41
|
|
32
42
|
## Usage
|
33
43
|
|
34
|
-
You can
|
35
|
-
|
36
|
-
Create a Model module with `inherited_class_var`
|
37
|
-
|
38
|
-
```
|
39
|
-
require 'inherited_class_var'
|
40
|
-
|
41
|
-
module Model
|
42
|
-
extend ActiveSupport::Concern
|
43
|
-
|
44
|
-
included do
|
45
|
-
include InheritedClassVar
|
46
|
-
inherited_class_hash :columns
|
47
|
-
end
|
44
|
+
You can also define your own variable types. This is the source for `Hash`:
|
48
45
|
|
49
|
-
|
46
|
+
```ruby
|
47
|
+
module InheritedClassVar
|
48
|
+
class Hash < Variable
|
49
|
+
alias_method :merge, :change
|
50
50
|
|
51
|
-
|
51
|
+
def default_value
|
52
|
+
{}
|
53
|
+
end
|
52
54
|
|
53
|
-
def
|
54
|
-
|
55
|
+
def _change(hash1, hash2)
|
56
|
+
method = options[:shallow] ? :merge! : :deep_merge!
|
57
|
+
block = options[:reverse] ? Proc.new {|key,left,right| left } : Proc.new {|key,left,right| right }
|
58
|
+
hash1.public_send(method, hash2, &block)
|
55
59
|
end
|
56
60
|
end
|
57
61
|
end
|
58
|
-
```
|
59
62
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
include Model
|
65
|
-
|
66
|
-
column :id, type: Integer
|
67
|
-
end
|
68
|
-
```
|
69
|
-
|
70
|
-
Gives
|
71
|
-
```
|
72
|
-
ModelBase.columns # => {:id=>{:type=>Integer}}
|
73
|
-
```
|
74
|
-
|
75
|
-
```
|
76
|
-
class UserModel < ModelBase
|
77
|
-
|
78
|
-
column :name, type: String
|
63
|
+
module InheritedClassVar
|
64
|
+
def inherited_class_hash(variable_name, options={})
|
65
|
+
inherited_class_var variable_name, InheritedClassVar::Hash, options
|
66
|
+
end
|
79
67
|
end
|
80
68
|
```
|
81
69
|
|
82
|
-
Gives
|
83
|
-
```
|
84
|
-
UserModel.columns # => {:id=>{:type=>Integer}, :name=>{:type=>String}}
|
85
|
-
```
|
86
|
-
|
87
|
-
## Development
|
88
|
-
|
89
|
-
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
90
|
-
|
91
|
-
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
92
|
-
|
93
|
-
## Contributing
|
94
|
-
|
95
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/inherited_class_var. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](contributor-covenant.org) code of conduct.
|
96
|
-
|
97
|
-
|
98
70
|
## License
|
99
71
|
|
100
72
|
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
data/inherited_class_var.gemspec
CHANGED
@@ -6,8 +6,8 @@ Gem::Specification.new do |spec|
|
|
6
6
|
spec.authors = ['Steve Chung', 'Joel AZEMAR']
|
7
7
|
spec.email = ['hello@stevenchung.ca','joel.azemar@gmail.com']
|
8
8
|
|
9
|
-
spec.summary = %q{
|
10
|
-
spec.description = %q{
|
9
|
+
spec.summary = %q{Implement class variables that inherit from their ancestors.}
|
10
|
+
spec.description = %q{Implement class variables that inherit from their ancestors.}
|
11
11
|
spec.homepage = 'https://github.com/FinalCAD/inherited_class_var'
|
12
12
|
spec.license = 'MIT'
|
13
13
|
|
@@ -16,5 +16,5 @@ Gem::Specification.new do |spec|
|
|
16
16
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
17
17
|
spec.require_paths = ['lib']
|
18
18
|
|
19
|
-
spec.add_dependency 'activesupport',
|
19
|
+
spec.add_dependency 'activesupport', '>= 4.2'
|
20
20
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module InheritedClassVar
|
2
|
+
class Hash < Variable
|
3
|
+
alias_method :merge, :change
|
4
|
+
|
5
|
+
def default_value
|
6
|
+
{}
|
7
|
+
end
|
8
|
+
|
9
|
+
def _change(hash1, hash2)
|
10
|
+
method = options[:shallow] ? :merge! : :deep_merge!
|
11
|
+
block = options[:reverse] ? Proc.new {|key,left,right| left } : Proc.new {|key,left,right| right }
|
12
|
+
hash1.public_send(method, hash2, &block)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module InheritedClassVar
|
2
|
+
class Variable
|
3
|
+
attr_reader :name, :klass, :options
|
4
|
+
|
5
|
+
def initialize(name, klass, options={})
|
6
|
+
@name = name
|
7
|
+
@klass = klass
|
8
|
+
@options = options
|
9
|
+
end
|
10
|
+
|
11
|
+
def object_method_name
|
12
|
+
self.class.object_method_name(name)
|
13
|
+
end
|
14
|
+
|
15
|
+
def default_value
|
16
|
+
raise NotImplementedError
|
17
|
+
end
|
18
|
+
|
19
|
+
def raw_value
|
20
|
+
@raw_value ||= default_value
|
21
|
+
end
|
22
|
+
|
23
|
+
def value
|
24
|
+
@value ||= klass.inherited_ancestors
|
25
|
+
.map { |ancestor| ancestor.try(object_method_name).try(:raw_value) }
|
26
|
+
.compact
|
27
|
+
.reverse
|
28
|
+
.reduce(default_value, &method(:reduce))
|
29
|
+
end
|
30
|
+
|
31
|
+
def change(other_value)
|
32
|
+
notify_change
|
33
|
+
_change(raw_value, other_value)
|
34
|
+
end
|
35
|
+
|
36
|
+
def _change(value1, value2)
|
37
|
+
raise NotImplementedError
|
38
|
+
end
|
39
|
+
def reduce(*args); _change(*args) end
|
40
|
+
|
41
|
+
def notify_change
|
42
|
+
([klass] + klass.descendants).each do |descendant|
|
43
|
+
descendant.try(object_method_name).try(:clear_memoized_value)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def clear_memoized_value
|
48
|
+
@value = nil if @value
|
49
|
+
end
|
50
|
+
|
51
|
+
class << self
|
52
|
+
def object_method_name(name)
|
53
|
+
:"#{name}_object"
|
54
|
+
end
|
55
|
+
|
56
|
+
def define_methods(name, klass, options={})
|
57
|
+
variable_class = self
|
58
|
+
object_method_name = object_method_name(name)
|
59
|
+
|
60
|
+
klass.send :define_singleton_method, object_method_name do
|
61
|
+
instance_variable_name = :"@#{object_method_name}"
|
62
|
+
instance_variable_get(instance_variable_name) || instance_variable_set(instance_variable_name, variable_class.new(name, self, options))
|
63
|
+
end
|
64
|
+
klass.send(:define_singleton_method, name) { public_send(object_method_name).value }
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
data/lib/inherited_class_var.rb
CHANGED
@@ -1,39 +1,26 @@
|
|
1
1
|
require 'active_support/all'
|
2
2
|
|
3
3
|
require 'inherited_class_var/version'
|
4
|
-
require 'inherited_class_var/
|
4
|
+
require 'inherited_class_var/variable'
|
5
|
+
require 'inherited_class_var/hash'
|
5
6
|
|
6
7
|
module InheritedClassVar
|
7
8
|
extend ActiveSupport::Concern
|
8
9
|
|
9
|
-
include Cache
|
10
|
-
|
11
10
|
class_methods do
|
12
11
|
protected
|
13
12
|
|
14
|
-
#
|
15
|
-
# Easy Open API
|
16
|
-
#
|
17
|
-
|
18
13
|
# @param variable_name [Symbol] class variable name
|
19
|
-
# @
|
20
|
-
def inherited_class_hash(variable_name)
|
21
|
-
|
22
|
-
|
23
|
-
define_singleton_method variable_name do
|
24
|
-
inherited_class_var(hidden_variable_name, {}) do |hash, to_merge|
|
25
|
-
hash.deep_merge!(to_merge) {|key,left,right| left } # a reverse_deep_merge! implementation, this will keep the parent's key order
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
define_singleton_method :"raw_#{variable_name}" do
|
30
|
-
class_var(hidden_variable_name, {})
|
31
|
-
end
|
14
|
+
# @param options [Hash] see InheritedClassVar::Hash
|
15
|
+
def inherited_class_hash(variable_name, options={})
|
16
|
+
inherited_class_var variable_name, InheritedClassVar::Hash, options
|
17
|
+
end
|
32
18
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
19
|
+
# @param variable_name [Symbol] class variable name
|
20
|
+
# @param variable_class [Class] a InheritedClassVar::Variable class
|
21
|
+
# @param options [Hash] see the variable_class
|
22
|
+
def inherited_class_var(variable_name, variable_class, options={})
|
23
|
+
variable_class.define_methods(variable_name, self, options)
|
37
24
|
end
|
38
25
|
|
39
26
|
# @param accessor_method_name [Symbol] method to access the inherited_custom_class
|
@@ -54,43 +41,11 @@ module InheritedClassVar
|
|
54
41
|
parent_class ||= base_parent_class
|
55
42
|
|
56
43
|
klass = Class.new(parent_class)
|
57
|
-
# how else can i get the current scopes name...
|
58
44
|
klass.send(:define_singleton_method, :name, &eval("-> { \"#{name}#{base_parent_class.name.demodulize}\" }"))
|
59
45
|
klass
|
60
46
|
end
|
61
47
|
|
62
|
-
|
63
|
-
# Helpers to make different types of inherited class variables
|
64
|
-
#
|
65
|
-
|
66
|
-
# @param variable_name [Symbol] class variable name based on
|
67
|
-
# @return [Symbol] the hidden variable name for class variable
|
68
|
-
def hidden_variable_name(variable_name)
|
69
|
-
:"@_#{variable_name}"
|
70
|
-
end
|
71
|
-
|
72
|
-
# @param variable_name [Symbol] class variable name
|
73
|
-
# @param default_value [Object] default value of the class variable
|
74
|
-
# @return [Object] a class variable of the specific class without taking into account inheritance
|
75
|
-
def class_var(variable_name, default_value)
|
76
|
-
instance_variable_get(variable_name) || instance_variable_set(variable_name, default_value)
|
77
|
-
end
|
78
|
-
|
79
|
-
# @param variable_name [Symbol] class variable name (recommend :@_variable_name)
|
80
|
-
# @return [Object] a class variable merged across ancestors until inherited_class_module
|
81
|
-
def inherited_class_var(variable_name, *reduce_args, &block)
|
82
|
-
class_cache(variable_name) do
|
83
|
-
inherited_ancestors.map { |ancestor| ancestor.instance_variable_get(variable_name) }
|
84
|
-
.compact
|
85
|
-
.reverse
|
86
|
-
.reduce(*reduce_args, &block)
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
#
|
91
|
-
# More Helpers
|
92
|
-
#
|
93
|
-
|
48
|
+
public
|
94
49
|
# @param included_module [Module] module to search for
|
95
50
|
# @return [Array<Module>] inherited_ancestors of included_module (including self)
|
96
51
|
def inherited_ancestors(included_module=InheritedClassVar)
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: inherited_class_var
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0.beta1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Steve Chung
|
@@ -9,23 +9,23 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date: 2016-
|
12
|
+
date: 2016-07-27 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activesupport
|
16
16
|
requirement: !ruby/object:Gem::Requirement
|
17
17
|
requirements:
|
18
|
-
- - "
|
18
|
+
- - ">="
|
19
19
|
- !ruby/object:Gem::Version
|
20
20
|
version: '4.2'
|
21
21
|
type: :runtime
|
22
22
|
prerelease: false
|
23
23
|
version_requirements: !ruby/object:Gem::Requirement
|
24
24
|
requirements:
|
25
|
-
- - "
|
25
|
+
- - ">="
|
26
26
|
- !ruby/object:Gem::Version
|
27
27
|
version: '4.2'
|
28
|
-
description:
|
28
|
+
description: Implement class variables that inherit from their ancestors.
|
29
29
|
email:
|
30
30
|
- hello@stevenchung.ca
|
31
31
|
- joel.azemar@gmail.com
|
@@ -46,7 +46,8 @@ files:
|
|
46
46
|
- bin/setup
|
47
47
|
- inherited_class_var.gemspec
|
48
48
|
- lib/inherited_class_var.rb
|
49
|
-
- lib/inherited_class_var/
|
49
|
+
- lib/inherited_class_var/hash.rb
|
50
|
+
- lib/inherited_class_var/variable.rb
|
50
51
|
- lib/inherited_class_var/version.rb
|
51
52
|
homepage: https://github.com/FinalCAD/inherited_class_var
|
52
53
|
licenses:
|
@@ -63,13 +64,13 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
63
64
|
version: '0'
|
64
65
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
65
66
|
requirements:
|
66
|
-
- - "
|
67
|
+
- - ">"
|
67
68
|
- !ruby/object:Gem::Version
|
68
|
-
version:
|
69
|
+
version: 1.3.1
|
69
70
|
requirements: []
|
70
71
|
rubyforge_project:
|
71
72
|
rubygems_version: 2.5.1
|
72
73
|
signing_key:
|
73
74
|
specification_version: 4
|
74
|
-
summary:
|
75
|
+
summary: Implement class variables that inherit from their ancestors.
|
75
76
|
test_files: []
|
@@ -1,54 +0,0 @@
|
|
1
|
-
#
|
2
|
-
# Methods to help cache the inherited variable
|
3
|
-
#
|
4
|
-
# Given:
|
5
|
-
# Parent -> Child
|
6
|
-
#
|
7
|
-
# and Parent.send(:inherited_class_hash, :some_var)
|
8
|
-
#
|
9
|
-
# Parent.merge_some_var(a: 1)
|
10
|
-
# Child.some_var # => { a: 1 }, this requires going up the ancestors (Parent, Grandparent, etc.) and merging, so we cache the result
|
11
|
-
# Child.some_var # => { a: 1 }, cached, no calculation
|
12
|
-
#
|
13
|
-
# Parent.merge_some_var(b: 2) # => this should clear the cache for all descendents
|
14
|
-
# Child.some_var # => { a: 1, b: 2 } # => cache was cleared, so recalculate via merging up ancestors
|
15
|
-
#
|
16
|
-
module InheritedClassVar
|
17
|
-
module Cache
|
18
|
-
extend ActiveSupport::Concern
|
19
|
-
|
20
|
-
class_methods do
|
21
|
-
|
22
|
-
# Clears the cache for a variable (must be public)
|
23
|
-
# @param variable_name [Symbol] variable_name to cache against
|
24
|
-
def clear_class_cache(variable_name)
|
25
|
-
instance_variable_set inherited_class_variable_name(variable_name), nil
|
26
|
-
end
|
27
|
-
|
28
|
-
protected
|
29
|
-
# Memozies a inherited_class_variable_name
|
30
|
-
# @param variable_name [Symbol] variable_name to cache against
|
31
|
-
def class_cache(variable_name)
|
32
|
-
#
|
33
|
-
# equal to: (has @)inherited_class_variable_name ||= yield
|
34
|
-
#
|
35
|
-
cache_variable_name = inherited_class_variable_name(variable_name)
|
36
|
-
instance_variable_get(cache_variable_name) || instance_variable_set(cache_variable_name, yield)
|
37
|
-
end
|
38
|
-
|
39
|
-
# Clears the cache for a variable and the same variable for all it's dependant descendants
|
40
|
-
# @param variable_name [Symbol] variable_name to cache against
|
41
|
-
def deep_clear_class_cache(variable_name)
|
42
|
-
([self] + descendants).each do |descendant|
|
43
|
-
descendant.try(:clear_class_cache, variable_name)
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
# @param variable_name [Symbol] variable_name to cache against
|
48
|
-
# @return [String] the cache variable name for the cache
|
49
|
-
def inherited_class_variable_name(variable_name)
|
50
|
-
:"#{variable_name}_inherited_class_cache"
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|