uber 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 58b218477b8c36e49f66c8bd47175bf605613ef1
4
+ data.tar.gz: bde8206f4395aa6469f9dfe84eb3651449acd0e3
5
+ SHA512:
6
+ metadata.gz: f08f9af53d76f9800741b6eacd147d031a356b40e309172c1323cf5a80b2f1f70271c1f383f7fbcb2681ac72eaf163b64fc364a36888523688cbbb8147185d22
7
+ data.tar.gz: 8b6a3e28f375e50b829fe60bf5a2a5ab50daafee28cd5e6334d8488e201b85a12ed915ed0e3c1c84938f5006f5ec55a8b1be07141895bdecc8472c24bfa8348e
data/CHANGES.md ADDED
@@ -0,0 +1,3 @@
1
+ # 0.0.2
2
+
3
+ * Add `::inheritable_attr`.
data/Gemfile CHANGED
@@ -2,3 +2,5 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in uber.gemspec
4
4
  gemspec
5
+
6
+ gem "activesupport"
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Uber
2
2
 
3
- TODO: Write a gem description
3
+ _Gem-authoring tools like class method inheritance in modules, dynamic options and more._
4
4
 
5
5
  ## Installation
6
6
 
@@ -8,22 +8,70 @@ Add this line to your application's Gemfile:
8
8
 
9
9
  gem 'uber'
10
10
 
11
- And then execute:
11
+ Ready?
12
12
 
13
- $ bundle
13
+ # Inheritable Class Attributes
14
14
 
15
- Or install it yourself as:
15
+ This is for you if you want class attributes to be inherited, which is a mandatory mechanism for creating DSLs.
16
16
 
17
- $ gem install uber
17
+ ```ruby
18
+ require 'uber/inheritable_attr'
18
19
 
19
- ## Usage
20
+ class Song
21
+ extend Uber::InheritableAttr
20
22
 
21
- TODO: Write usage instructions here
23
+ inheritable_attr :properties
24
+ self.properties = [:title, :track] # initialize it before using it.
25
+ end
26
+ ```
22
27
 
23
- ## Contributing
28
+ Note that you have to initialize your attribute which whatever you want - usually a hash or an array.
24
29
 
25
- 1. Fork it
26
- 2. Create your feature branch (`git checkout -b my-new-feature`)
27
- 3. Commit your changes (`git commit -am 'Added some feature'`)
28
- 4. Push to the branch (`git push origin my-new-feature`)
29
- 5. Create new Pull Request
30
+ You can now use that attribute on the class level.
31
+
32
+ ```ruby
33
+ Song.properties #=> [:title, :track]
34
+ ```
35
+
36
+ Inheriting from `Song` will result in the `properties` object being `clone`d to the sub-class.
37
+
38
+ ```ruby
39
+ class Hit < Song
40
+ end
41
+
42
+ Hit.properties #=> [:title, :track]
43
+ ```
44
+
45
+ The cool thing about the inheritance is: you can work on the inherited attribute without any restrictions, as it is a _copy_ of the original.
46
+
47
+ ```ruby
48
+ Hit.properties << :number
49
+
50
+ Hit.properties #=> [:title, :track, :number]
51
+ Song.properties #=> [:title, :track]
52
+ ```
53
+
54
+ It's similar to ActiveSupport's `class_attribute` but with a simpler implementation resulting in a less dangerous potential. Also, there is no restriction about the way you modify the attribute [as found in `class_attribute`](http://apidock.com/rails/v4.0.2/Class/class_attribute).
55
+
56
+ This module is very popular amongst numerous gems like Cells, Representable, Roar and Reform.
57
+
58
+
59
+ # Options
60
+
61
+ Implements the pattern of defining configuration options and evaluating them at run-time.
62
+
63
+ Usually DSL methods accept a number of options that can either be static values, instance method names as symbols, or blocks (lambdas/Procs).
64
+
65
+ Uber::Options.new volume: 9, track: lambda { |s| s.track }
66
+
67
+
68
+ Note that `Options` behaves *and performs* like an ordinary hash when all options are static.
69
+
70
+ only use for declarative assets, not at runtime (use a hash)
71
+
72
+
73
+ # License
74
+
75
+ Copyright (c) 2014 by Nick Sutterer <apotonick@gmail.com>
76
+
77
+ Roar is released under the [MIT License](http://www.opensource.org/licenses/MIT).
@@ -0,0 +1,22 @@
1
+ module Uber
2
+ module InheritableAttr
3
+ def inheritable_attr(name)
4
+ instance_eval %Q{
5
+ def #{name}=(v)
6
+ @#{name} = v
7
+ end
8
+
9
+ def #{name}
10
+ @#{name} ||= InheritableAttribute.inherit_for(self, :#{name})
11
+ end
12
+ }
13
+ end
14
+
15
+ def self.inherit_for(klass, name)
16
+ return unless klass.superclass.respond_to?(name) and value = klass.superclass.send(name)
17
+ value.clone # only do this once.
18
+ end
19
+ end
20
+
21
+ InheritableAttribute = InheritableAttr
22
+ end
@@ -0,0 +1,70 @@
1
+ module Uber
2
+ class Options < Hash
3
+ def initialize(options)
4
+ @static = options
5
+
6
+ options.each do |k,v|
7
+ self[k] = option = Value.new(v)
8
+ @static = nil if option.dynamic?
9
+ end
10
+ end
11
+
12
+ # 1.100000 0.060000 1.160000 ( 1.159762) original
13
+ # 0.120000 0.010000 0.130000 ( 0.135803) return self
14
+ # 0.930000 0.060000 0.990000 ( 0.997095) without v.evaluate
15
+
16
+ # Evaluates every element and returns a hash. Accepts context and arbitrary arguments.
17
+ def evaluate(context, *args)
18
+ return @static unless dynamic?
19
+
20
+ evaluate_for(context, *args)
21
+ end
22
+
23
+ # Evaluates a single value.
24
+ def eval(key, *args)
25
+ self[key].evaluate(*args)
26
+ end
27
+
28
+ private
29
+ def evaluate_for(context, *args)
30
+ {}.tap do |evaluated|
31
+ each do |k,v|
32
+ evaluated[k] = v.evaluate(context, *args)
33
+ end
34
+ end
35
+ end
36
+
37
+ def dynamic?
38
+ not @static
39
+ end
40
+
41
+
42
+ class Value # TODO: rename to Value.
43
+ def initialize(value, options={})
44
+ @value = value || true
45
+ @options = options
46
+ end
47
+
48
+ def evaluate(context, *args)
49
+ return true if @value.is_a?(TrueClass)
50
+
51
+ evaluate_for(context, *args)
52
+ end
53
+
54
+ def dynamic?
55
+ @options[:instance_method] || @value.kind_of?(Proc)
56
+ end
57
+
58
+ private
59
+ def evaluate_for(context, *args)
60
+ return proc!(context, *args) unless @value.kind_of?(Proc)
61
+ @value.call(context, *args) # TODO: change to context.instance_exec and deprecate first argument.
62
+ end
63
+
64
+ def proc!(context, *args)
65
+ return context.send(@value, *args) if @options[:instance_method]
66
+ @value
67
+ end
68
+ end
69
+ end
70
+ end
data/lib/uber/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Uber
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -0,0 +1,54 @@
1
+ require 'test_helper'
2
+ require "uber/inheritable_attr"
3
+
4
+ class InheritableAttrTest < MiniTest::Spec
5
+ describe "::inheritable_attr" do
6
+ subject {
7
+ Class.new(Object) do
8
+ extend Uber::InheritableAttribute
9
+ inheritable_attr :drinks
10
+ end
11
+ }
12
+
13
+ it "provides a reader with empty inherited attributes, already" do
14
+ assert_equal nil, subject.drinks
15
+ end
16
+
17
+ it "provides a reader with empty inherited attributes in a derived class" do
18
+ assert_equal nil, Class.new(subject).drinks
19
+ #subject.drinks = true
20
+ #Class.new(subject).drinks # TODO: crashes.
21
+ end
22
+
23
+ it "provides an attribute copy in subclasses" do
24
+ subject.drinks = []
25
+ assert subject.drinks.object_id != Class.new(subject).drinks.object_id
26
+ end
27
+
28
+ it "provides a writer" do
29
+ subject.drinks = [:cabernet]
30
+ assert_equal [:cabernet], subject.drinks
31
+ end
32
+
33
+ it "inherits attributes" do
34
+ subject.drinks = [:cabernet]
35
+
36
+ subklass_a = Class.new(subject)
37
+ subklass_a.drinks << :becks
38
+
39
+ subklass_b = Class.new(subject)
40
+
41
+ assert_equal [:cabernet], subject.drinks
42
+ assert_equal [:cabernet, :becks], subklass_a.drinks
43
+ assert_equal [:cabernet], subklass_b.drinks
44
+ end
45
+
46
+ it "does not inherit attributes if we set explicitely" do
47
+ subject.drinks = [:cabernet]
48
+ subklass = Class.new(subject)
49
+
50
+ subklass.drinks = [:merlot] # we only want merlot explicitely.
51
+ assert_equal [:merlot], subklass.drinks # no :cabernet, here
52
+ end
53
+ end
54
+ end
@@ -1,7 +1,52 @@
1
1
  require 'test_helper'
2
+ require 'uber/inheritable_included'
3
+
4
+ module InheritIncludedTo
5
+ def self.call(includer, proc)
6
+ proc.call(includer) # das will ich eigentlich machen
7
+
8
+ includer.class_eval do
9
+ @block = proc
10
+
11
+ def self.included(base) #
12
+ InheritIncludedTo.call(base, instance_variable_get(:@block))
13
+ end
14
+ end
15
+ end
16
+ end
2
17
 
3
18
  class InheritanceTest < MiniTest::Spec
4
- describe "including" do
5
-
19
+ module Feature
20
+ #extend Uber::InheritedIncluded
21
+
22
+ CODE_BLOCK = lambda { |base| base.class_eval { extend ClassMethods } } # i want that to be executed at every include
23
+
24
+
25
+ def self.included(includer) #
26
+ # CODE_BLOCK.call(base)
27
+ InheritIncludedTo.call(includer, CODE_BLOCK)
28
+ end
29
+
30
+ module ClassMethods
31
+ def feature; end
32
+ end
6
33
  end
34
+
35
+ module Extension
36
+ include Feature
37
+
38
+ # TODO: test overriding ::included
39
+ end
40
+
41
+ module Client
42
+ include Extension
43
+ end
44
+
45
+ module ExtendedClient
46
+ include Client
47
+ end
48
+
49
+ it { Extension.must_respond_to :feature }
50
+ it { Client.must_respond_to :feature }
51
+ it { ExtendedClient.must_respond_to :feature }
7
52
  end
@@ -0,0 +1,72 @@
1
+ require 'test_helper'
2
+ require 'uber/options'
3
+
4
+ class UberOptionTest < MiniTest::Spec
5
+ Value = Uber::Options::Value
6
+
7
+ describe "#dynamic?" do
8
+ it { Value.new(1).dynamic?.must_equal false }
9
+ it { Value.new(true).dynamic?.must_equal false }
10
+ it { Value.new(:loud).dynamic?.must_equal false }
11
+
12
+ it { Value.new(lambda {}).dynamic?.must_equal true }
13
+ it { Value.new(Proc.new{}).dynamic?.must_equal true }
14
+ it { Value.new(:method, :instance_method => true).dynamic?.must_equal true }
15
+ end
16
+
17
+
18
+
19
+ # it "speed" do
20
+ # require "benchmark"
21
+
22
+ # options = 1000000.times.collect do
23
+ # Uber::Options.new(expires: false)
24
+ # end
25
+
26
+ # time = Benchmark.measure do
27
+ # options.each do |opt|
28
+ # opt.evaluate(nil)
29
+ # end
30
+ # end
31
+
32
+ # puts "good results"
33
+ # puts time
34
+ # end
35
+ end
36
+
37
+ class UberOptionsTest < MiniTest::Spec
38
+ Options = Uber::Options
39
+
40
+ let (:dynamic) { Options.new(:volume =>1, :style => "Punkrock", :track => Proc.new { |i| i.to_s }) }
41
+
42
+ describe "#dynamic?" do
43
+ it { Options.new(:volume =>1, :style => "Punkrock").send(:dynamic?).must_equal false }
44
+ it { Options.new(:style => Proc.new{}, :volume =>1).send(:dynamic?).must_equal true }
45
+ end
46
+
47
+ describe "#evaluate" do
48
+
49
+ it { dynamic.evaluate(999).must_equal({:volume =>1, :style => "Punkrock", :track => "999"}) }
50
+
51
+ describe "static" do
52
+ let (:static) { Options.new(:volume =>1, :style => "Punkrock") }
53
+
54
+ it { static.evaluate(nil).must_equal({:volume =>1, :style => "Punkrock"}) }
55
+
56
+ it "doesn't evaluate internally" do
57
+ static.instance_eval do
58
+ def evaluate_for(*)
59
+ raise "i shouldn't be called!"
60
+ end
61
+ end
62
+ static.evaluate(nil).must_equal({:volume =>1, :style => "Punkrock"})
63
+ end
64
+ end
65
+ end
66
+
67
+ describe "#eval" do
68
+ it { dynamic.eval(:volume, 999).must_equal 1 }
69
+ it { dynamic.eval(:style, 999).must_equal "Punkrock" }
70
+ it { dynamic.eval(:track, 999).must_equal "999" }
71
+ end
72
+ end
data/test/test_helper.rb CHANGED
@@ -1,8 +1,5 @@
1
1
  require 'bundler'
2
2
  Bundler.setup
3
3
 
4
- gem 'minitest'
5
- require 'minitest/spec'
6
4
  require 'minitest/autorun'
7
-
8
5
  require 'uber'
data/test/zeugs.rb ADDED
@@ -0,0 +1,54 @@
1
+ module Feature
2
+ module ClassMethods
3
+ def feature
4
+ end
5
+ end
6
+
7
+ # in uber, this would look somehow like
8
+ # module Feature
9
+ # module ClassMethods ... end
10
+
11
+ # extend Uber::InheritableIncluded
12
+ # inheritable_included do |includer|
13
+ # includer.extend ClassMethods
14
+ # end
15
+ # end
16
+
17
+ InheritedIncludedCodeBlock = lambda do |includer|
18
+ includer.extend ClassMethods
19
+ end
20
+
21
+ module RecursiveIncluded
22
+ def included(includer)
23
+ #super # TODO: test me.
24
+ puts "RecursiveIncluded in #{includer}"
25
+
26
+ includer.module_eval do
27
+ InheritedIncludedCodeBlock.call(includer)
28
+ extend RecursiveIncluded
29
+ end
30
+ end
31
+ end
32
+ extend RecursiveIncluded
33
+ end
34
+
35
+ module Client
36
+ include Feature
37
+ end
38
+
39
+ module Extension
40
+ include Client
41
+ end
42
+
43
+ module Plugin
44
+ include Extension
45
+ end
46
+
47
+ module Framework
48
+ include Plugin
49
+ end
50
+
51
+ Client.feature
52
+ Extension.feature
53
+ Plugin.feature
54
+ Framework.feature
data/uber.gemspec CHANGED
@@ -5,7 +5,7 @@ Gem::Specification.new do |gem|
5
5
  gem.authors = ["Nick Sutterer"]
6
6
  gem.email = ["apotonick@gmail.com"]
7
7
  gem.description = %q{A gem-authoring framework.}
8
- gem.summary = %q{Uber is a gem-authoring framework that brings simple module inheritance and more.}
8
+ gem.summary = %q{Gem-authoring tools like class method inheritance in modules, dynamic options and more.}
9
9
  gem.homepage = ""
10
10
 
11
11
  gem.files = `git ls-files`.split($\)
@@ -14,6 +14,7 @@ Gem::Specification.new do |gem|
14
14
  gem.name = "uber"
15
15
  gem.require_paths = ["lib"]
16
16
  gem.version = Uber::VERSION
17
-
18
- gem.add_development_dependency "minitest", ">= 2.8.1"
17
+
18
+ gem.add_development_dependency "rake", ">= 0.10.1"
19
+ gem.add_development_dependency "minitest", ">= 5.0.0"
19
20
  end
metadata CHANGED
@@ -1,32 +1,43 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: uber
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
5
- prerelease:
4
+ version: 0.0.2
6
5
  platform: ruby
7
6
  authors:
8
7
  - Nick Sutterer
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2013-02-07 00:00:00.000000000 Z
11
+ date: 2014-03-06 00:00:00.000000000 Z
13
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 0.10.1
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 0.10.1
14
27
  - !ruby/object:Gem::Dependency
15
28
  name: minitest
16
29
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
30
  requirements:
19
- - - ! '>='
31
+ - - ">="
20
32
  - !ruby/object:Gem::Version
21
- version: 2.8.1
33
+ version: 5.0.0
22
34
  type: :development
23
35
  prerelease: false
24
36
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
37
  requirements:
27
- - - ! '>='
38
+ - - ">="
28
39
  - !ruby/object:Gem::Version
29
- version: 2.8.1
40
+ version: 5.0.0
30
41
  description: A gem-authoring framework.
31
42
  email:
32
43
  - apotonick@gmail.com
@@ -34,41 +45,49 @@ executables: []
34
45
  extensions: []
35
46
  extra_rdoc_files: []
36
47
  files:
37
- - .gitignore
48
+ - ".gitignore"
49
+ - CHANGES.md
38
50
  - Gemfile
39
51
  - LICENSE
40
52
  - README.md
41
53
  - Rakefile
42
54
  - lib/uber.rb
55
+ - lib/uber/inheritable_attr.rb
56
+ - lib/uber/options.rb
43
57
  - lib/uber/version.rb
58
+ - test/inheritable_attr_test.rb
44
59
  - test/inheritance_test.rb
60
+ - test/options_test.rb
45
61
  - test/test_helper.rb
62
+ - test/zeugs.rb
46
63
  - uber.gemspec
47
64
  homepage: ''
48
65
  licenses: []
66
+ metadata: {}
49
67
  post_install_message:
50
68
  rdoc_options: []
51
69
  require_paths:
52
70
  - lib
53
71
  required_ruby_version: !ruby/object:Gem::Requirement
54
- none: false
55
72
  requirements:
56
- - - ! '>='
73
+ - - ">="
57
74
  - !ruby/object:Gem::Version
58
75
  version: '0'
59
76
  required_rubygems_version: !ruby/object:Gem::Requirement
60
- none: false
61
77
  requirements:
62
- - - ! '>='
78
+ - - ">="
63
79
  - !ruby/object:Gem::Version
64
80
  version: '0'
65
81
  requirements: []
66
82
  rubyforge_project:
67
- rubygems_version: 1.8.24
83
+ rubygems_version: 2.2.1
68
84
  signing_key:
69
- specification_version: 3
70
- summary: Uber is a gem-authoring framework that brings simple module inheritance and
71
- more.
85
+ specification_version: 4
86
+ summary: Gem-authoring tools like class method inheritance in modules, dynamic options
87
+ and more.
72
88
  test_files:
89
+ - test/inheritable_attr_test.rb
73
90
  - test/inheritance_test.rb
91
+ - test/options_test.rb
74
92
  - test/test_helper.rb
93
+ - test/zeugs.rb