liner 0.1.2 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1031b1dc95208b1a8692204114af88b6e46ee907
4
- data.tar.gz: f4a3cb0860e01d58d748bf0b000ef1e38b8a6290
3
+ metadata.gz: 53f068a96a64299ca295a0c1821a5232a4ec2618
4
+ data.tar.gz: 459fd2b8e24e184a5437744fa1ae3701c384d609
5
5
  SHA512:
6
- metadata.gz: 0cd932e7fddb88187281890814b89af03ef3c01fc0bcc635fa3f6f2d3614c624776a74f2ef7308c9cee013d004b7df82214f4483931921871af53ba571e9257f
7
- data.tar.gz: 7722ba0e233b9ad646ec73be8fe5192c154d3442be4ff2f01138912f1648f96cdbd3004caec1851cc19ba7edabcd0bbe5d52bdbd3e4551528e0c45c8627832c5
6
+ metadata.gz: 98f7de6a0c3580a6dd7c8774aa7b427b7819c30a28ad1dce9fdfd362fd3c6a9742df59f2c9a0b64508e943a802a3fa422c3f442c6529827d7acf5dab9e943bd1
7
+ data.tar.gz: 7d8e902c6662b26f751bee45f120855c21e3a300a66532fe536d4be439f5063745a352691f97788cd0461086a88bba3b6ff269327354d75c0761b381cf29abf3
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
5
+ - 2.1.0
data/README.md CHANGED
@@ -1,27 +1,40 @@
1
1
  # Liner
2
+ [![Build Status](https://travis-ci.org/joshwlewis/liner.png?branch=master)](https://travis-ci.org/joshwlewis/liner)
3
+ [![Coverage Status](https://coveralls.io/repos/joshwlewis/liner/badge.png?branch=master)](https://coveralls.io/r/joshwlewis/liner?branch=master)
4
+ [![Gem Version](https://badge.fury.io/rb/liner.png)](http://badge.fury.io/rb/liner)
5
+ [![Dependency Status](https://gemnasium.com/joshwlewis/liner.png)](https://gemnasium.com/joshwlewis/liner)
6
+ [![Code Climate](https://codeclimate.com/github/joshwlewis/liner.png)](https://codeclimate.com/github/joshwlewis/liner)
2
7
 
3
- Lay a liner for your Ruby classes. Liner is designed to enhance simple classes
4
- with some common idioms.
8
+ Lay a liner for your Ruby classes. Liner is a lightweight library designed to
9
+ enhance simple classes with some conveniences and idioms while staying out of
10
+ your way.
5
11
 
6
12
  ## Usage
7
13
 
14
+ ### Setup
15
+
8
16
  You can setup a Liner based class in any of these equivalent ways:
9
17
 
10
18
  ```ruby
11
19
  Engine = Liner.new(:layout, :fuel)
12
20
  ```
21
+
13
22
  ```ruby
14
23
  class Engine < Liner.new(:layout, :fuel)
15
24
  end
16
25
  ```
26
+
17
27
  ```ruby
18
28
  class Engine
19
29
  liner :layout, :fuel
20
30
  end
21
31
  ```
22
32
 
33
+ ### Initialization
34
+
23
35
  Your new class comes with an initializer that takes values in the order you
24
36
  defined them.
37
+
25
38
  ```ruby
26
39
  e = Engine.new('V6', 'gasoline')
27
40
  # => #<Engine layout="V6", fuel="gasoline">
@@ -32,6 +45,8 @@ e = Engine.new(layout: 'V8', fuel: "gasoline")
32
45
  # => #<Engine layout="V8", fuel="gasoline">
33
46
  ```
34
47
 
48
+ ### Attributes
49
+
35
50
  Attribute getters and setters are built in.
36
51
  ```ruby
37
52
  e.fuel # => "gasoline"
@@ -39,18 +54,118 @@ e.fuel = "diesel" # => "diesel"
39
54
  ```
40
55
 
41
56
  Attributes are accessible via hash style lookup too.
57
+
42
58
  ```ruby
43
59
  e[:layout] # => "V8"
44
60
  e[:layout] = "V6" # => "V6"
45
61
  e[:foo] = "Bar" # => ArgumentError: Invalid liner attribute: 'foo'
46
62
  ```
47
63
 
48
- Equality methods are also availble.
64
+ If you want a full attribute hash, we have that (`to_h` and `to_hash` also work).
65
+
66
+ ```ruby
67
+ e.liner # => { :layout => 'V6', :fuel => 'diesel' }
68
+ ```
69
+
70
+ ### Inspection
71
+
72
+ It's always nice not to have to set up inspection (note that `to_s` is the same
73
+ here).
74
+
75
+ ```ruby
76
+ e.inspect
77
+ # => #<Engine layout="V6", fuel="gasoline">
78
+ ```
79
+
80
+ ### Equality
81
+
82
+ Normal equality methods are here.
49
83
  ```ruby
50
84
  e.eql? Engine.new(layout: 'I4') # => false
51
85
  e == Engine.new(layout: 'V6', fuel: 'diesel') # => true
52
86
  ```
53
87
 
88
+ ### Serialization
89
+
90
+ JSON serialization is ready after you require it.
91
+
92
+ ```ruby
93
+ require 'json'
94
+ e.to_json
95
+ # => "{\"layout\":\"V6\",\"fuel\":\"gasoline\"}"
96
+ ```
97
+
98
+ ### Overriding
99
+
100
+ #### Getters/Readers
101
+ If you want to customize the way an attribute is read, just override the method
102
+ like you would any accessor. You can access the raw value through either the instance variable,
103
+ `read_attribute`, or `super`.
104
+
105
+ ```ruby
106
+ class Taco < Liner.new(:filling)
107
+ def filling
108
+ if read_liner(:filling) == 'ground beef'
109
+ 'Steak'
110
+ elsif @filling == 'unknown fish'
111
+ 'Atlantic Cod'
112
+ else
113
+ super()
114
+ end
115
+ end
116
+ end
117
+ ```
118
+
119
+ Overridden getters will take precedence for the other features. This
120
+ is probably desirable, just don't be surprised by it.
121
+
122
+ ```ruby
123
+ taco = Taco.new("ground beef")
124
+ # => #<Taco filling="Steak">
125
+ taco[:filling] = 'unknown fish'
126
+ # => 'unknown fish'
127
+ taco.liner
128
+ # => {:filling=>"Atlantic Cod"}
129
+ ```
130
+
131
+ #### Setters/Writers
132
+ It's the same scenario for customizing the writer. Set the real value
133
+ through the instance variable, `write_attribute`, or `super`.
134
+
135
+ ```ruby
136
+ class Bacon < Liner.new(:is_good)
137
+ def is_good=(good)
138
+ @is_good = true
139
+ end
140
+ end
141
+ ```
142
+
143
+ Again, the overridden method takes precendence, even with writers.
144
+
145
+ ```ruby
146
+ generic_bacon = Bacon.new
147
+ generic_bacon[:is_good] = false
148
+ # => true
149
+ ```
150
+
151
+ ### Inheritance
152
+
153
+ Inheritance of Liner classes works like any other Ruby class, but you can tack
154
+ on extra attributes to child classes if you like.
155
+
156
+ ```ruby
157
+ class Instrument
158
+ liner :key
159
+ end
160
+
161
+ class Guitar < Instrument
162
+ liner :strings
163
+ end
164
+
165
+ Guitar.new('C', 6)
166
+ # => #<Guitar key="C", strings=6>
167
+ ```
168
+
54
169
  ## Installation
55
170
 
56
171
  Add this line to your application's Gemfile:
@@ -1,5 +1,5 @@
1
1
  Class.class_eval do
2
2
  def liner(*keys)
3
- Liner.apply self, *keys
3
+ Liner.apply! self, *keys
4
4
  end
5
5
  end
@@ -7,6 +7,8 @@ require 'liner/serializable'
7
7
  require 'ext/class'
8
8
 
9
9
  module Liner
10
+ # include all submodules when included
11
+ # @api public
10
12
  def self.included(base)
11
13
  [Base, Hashable, Equalizable, Inspectable, Serializable].each do |mod|
12
14
  base.send :include, mod
@@ -14,24 +16,39 @@ module Liner
14
16
  end
15
17
  end
16
18
 
19
+ # Setup an anonymous class with liner keys
20
+ # @param keys [Array] An array of symbols or strings which will serve as attributes
21
+ # @return [Class]
22
+ # @example Liner.new(:foo) # => #<Class:0x007fd0993bab98>
23
+ # @api public
17
24
  def Liner.new(*keys)
18
- apply Class.new, *keys
25
+ apply! Class.new, *keys
19
26
  end
20
27
 
21
- def Liner.apply(base, *keys)
28
+ # Apply a liner to a given class
29
+ # @param base [Class] A class to add a liner to
30
+ # @param keys [Array] An array of symbols or strings which will serve as attributes
31
+ # @return [Class] The class with a liner installed
32
+ # @api public
33
+ def Liner.apply!(base, *keys)
22
34
  keys = keys.map(&:to_sym).uniq.freeze
23
- base.class_eval do
24
- define_method :liner_keys do
25
- begin
26
- (super() + keys).uniq
27
- rescue NoMethodError
28
- keys
29
- end
35
+
36
+ base.send(:define_method, :liner_keys) do
37
+ begin
38
+ (super() + keys).uniq
39
+ rescue NoMethodError
40
+ keys
41
+ end
42
+ end
43
+
44
+ base.send :include, Liner unless base < Liner
45
+
46
+ keys.each do |key|
47
+ unless base.method_defined? key
48
+ base.send(:define_method, key){ liner_get key }
30
49
  end
31
- include Liner
32
- keys.each do |key|
33
- define_method(key){ self[key] }
34
- define_method("#{key}="){ |value| self[key] = value }
50
+ unless base.method_defined? "#{key}="
51
+ base.send(:define_method, "#{key}="){ |value| liner_set key, value }
35
52
  end
36
53
  end
37
54
  base
@@ -5,9 +5,31 @@ module Liner
5
5
  self.liner = args.first
6
6
  elsif args.count >= 1 && args.count <= liner_keys.count
7
7
  self.liner_values = args
8
- elsif !args.empty?
9
- raise ArgumentError, "Liner doesn't know how to initialize with `#{args}`."
10
8
  end
11
9
  end
12
10
  end
11
+
12
+ private
13
+
14
+ def liner_get(key)
15
+ key = key.to_sym
16
+ with_valid_attribute(key) do
17
+ instance_variable_get "@#{key}"
18
+ end
19
+ end
20
+
21
+ def liner_set(key, value)
22
+ key = key.to_sym
23
+ with_valid_attribute(key) do
24
+ instance_variable_set "@#{key}", value
25
+ end
26
+ end
27
+
28
+ def with_valid_attribute(key, &block)
29
+ if liner_keys.include?(key)
30
+ yield
31
+ else
32
+ raise ArgumentError, "Invalid liner attribute: '#{key}'"
33
+ end
34
+ end
13
35
  end
@@ -1,30 +1,29 @@
1
1
  module Liner
2
2
  module Hashable
3
3
 
4
- def liner
5
- @liner ||= liner_keys.inject({}) { |h,k| h[k]=nil; h }
4
+ def [](key)
5
+ send key.to_sym
6
+ rescue NoMethodError
7
+ liner_get key
6
8
  end
7
9
 
8
- def liner=(hash)
9
- hash.each { |k,v| self[k] = hash[k] }
10
+ def []=(key,value)
11
+ send :"#{key}=", value
12
+ rescue NoMethodError
13
+ liner_set key, value
10
14
  end
11
15
 
12
- def [](key)
13
- liner[key.to_sym]
16
+ def liner
17
+ liner_keys.inject({}) { |h,k| h[k] = self[k]; h }.freeze
14
18
  end
15
19
 
16
- def []=(key,value)
17
- key = key.to_sym
18
- if liner_keys.include?(key)
19
- liner[key] = value
20
- else
21
- raise ArgumentError, "Invalid liner attribute: '#{key}'."
22
- end
20
+ def liner=(hash)
21
+ hash.each { |k,v| self[k] = hash[k] }
23
22
  end
24
23
 
25
24
  def liner_values=(values)
26
- values.each_with_index do |v,i|
27
- self[liner_keys[i]] = v
25
+ values.each_with_index do |value, i|
26
+ self[liner_keys[i]] = value
28
27
  end
29
28
  end
30
29
 
@@ -1,3 +1,3 @@
1
1
  module Liner
2
- VERSION = '0.1.2'
2
+ VERSION = '0.2.0'
3
3
  end
@@ -8,8 +8,8 @@ Gem::Specification.new do |spec|
8
8
  spec.version = Liner::VERSION
9
9
  spec.authors = ["Josh Lewis"]
10
10
  spec.email = ["josh.w.lewis@gmail.com"]
11
- spec.description = %q{A liner for Ruby objects. Add attribute, inspection, and equality methods.}
12
- spec.summary = %q{A liner for Ruby objects. Add attribute, inspection, and equality methods.}
11
+ spec.description = %q{A liner for Ruby objects. Add attribute, inspection, serialization, and equality methods.}
12
+ spec.summary = %q{Liner is a lightweight library designed to enhance simple classes with a few conveniences and idioms while staying out of your way.}
13
13
  spec.homepage = "https://github.com/joshwlewis/liner"
14
14
  spec.license = "MIT"
15
15
 
@@ -21,4 +21,5 @@ Gem::Specification.new do |spec|
21
21
  spec.add_development_dependency "bundler", "~> 1.3"
22
22
  spec.add_development_dependency "rake"
23
23
  spec.add_development_dependency "minitest"
24
+ spec.add_development_dependency "coveralls"
24
25
  end
@@ -6,6 +6,10 @@ describe Liner::Hashable do
6
6
  subject[:yeast].must_equal nil
7
7
  end
8
8
 
9
+ it "[] should not read invalid attributes" do
10
+ ->{ subject[:foo] }.must_raise ArgumentError
11
+ end
12
+
9
13
  it "#[]= should set attributes" do
10
14
  subject[:yeast]= "Top Fermenting"
11
15
  subject.yeast.must_equal "Top Fermenting"
@@ -1,8 +1,10 @@
1
+ require 'coveralls'
2
+ Coveralls.wear!
3
+
1
4
  require 'minitest/autorun'
2
5
  require 'minitest/pride'
3
6
  require 'liner'
4
7
 
5
-
6
8
  # create some dummy classes to test against with each of the supported usages.
7
9
 
8
10
  Beer = Liner.new :hops, :yeast
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: liner
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Josh Lewis
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-02-02 00:00:00.000000000 Z
11
+ date: 2014-02-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -52,7 +52,22 @@ dependencies:
52
52
  - - '>='
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
- description: A liner for Ruby objects. Add attribute, inspection, and equality methods.
55
+ - !ruby/object:Gem::Dependency
56
+ name: coveralls
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: A liner for Ruby objects. Add attribute, inspection, serialization, and
70
+ equality methods.
56
71
  email:
57
72
  - josh.w.lewis@gmail.com
58
73
  executables: []
@@ -60,6 +75,7 @@ extensions: []
60
75
  extra_rdoc_files: []
61
76
  files:
62
77
  - .gitignore
78
+ - .travis.yml
63
79
  - Gemfile
64
80
  - LICENSE.txt
65
81
  - README.md
@@ -103,7 +119,8 @@ rubyforge_project:
103
119
  rubygems_version: 2.2.1
104
120
  signing_key:
105
121
  specification_version: 4
106
- summary: A liner for Ruby objects. Add attribute, inspection, and equality methods.
122
+ summary: Liner is a lightweight library designed to enhance simple classes with a
123
+ few conveniences and idioms while staying out of your way.
107
124
  test_files:
108
125
  - test/liner/base_test.rb
109
126
  - test/liner/equalizable_test.rb
@@ -112,3 +129,4 @@ test_files:
112
129
  - test/liner/serializable_test.rb
113
130
  - test/liner_test.rb
114
131
  - test/test_helper.rb
132
+ has_rdoc: