simple_objects 0.0.1 → 1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 939d6dec189dfd4d07db852f4070b3ac9a4eb025
4
- data.tar.gz: 6918a585dcafe58adfdc5854a1ce6fc407219f23
3
+ metadata.gz: c25d89aba39253b82e542b1100d04dea62df0c6d
4
+ data.tar.gz: 6dae5572e49c5eee2519de5b3e8a7d37bec27fda
5
5
  SHA512:
6
- metadata.gz: 24078705f7012cb3ceb679862528b78312136aadba25a95fe16f7b48706510b9f9f2054468e1eee450ad59e7fd6f2c37e0580c11aab71c817dbd780741181e5c
7
- data.tar.gz: c9c94f5390f310456c24ca9f26ce0fc150f6f56b859098d39cc357b1b629b2426a702c7908db0b9ec65ed76bb2d2223c8adb3ba24739f3f40844eab5f212e193
6
+ metadata.gz: 473a79775e34bfd9cd8d2fa15de5e8820b1560d711ee503f2a05bc790d5c29042d7c994c68e8120f88e509f6846e8acf3fa2eab643b2cd3d74f5aaba21ac11bf
7
+ data.tar.gz: ca526a84eed486ca988d9fc1e5470d46fccf110234d75f5959822e69657b253657cbdb3e6ebf3e61d58a39317a56817d53d59b8ec0defd67deb47f7f69510e1f
data/.gitignore CHANGED
@@ -1,14 +1,63 @@
1
- /.bundle/
2
- /.yardoc
3
- /Gemfile.lock
4
- /_yardoc/
1
+ # Created by https://www.gitignore.io
2
+
3
+ ### Ruby ###
4
+ *.gem
5
+ *.rbc
6
+ /.config
5
7
  /coverage/
6
- /doc/
8
+ /InstalledFiles
7
9
  /pkg/
8
10
  /spec/reports/
11
+ /test/tmp/
12
+ /test/version_tmp/
9
13
  /tmp/
10
- *.bundle
11
- *.so
12
- *.o
13
- *.a
14
- mkmf.log
14
+
15
+ ## Documentation cache and generated files:
16
+ /.yardoc/
17
+ /_yardoc/
18
+ /doc/
19
+ /rdoc/
20
+
21
+ ## Environment normalisation:
22
+ /.bundle/
23
+ /vendor/bundle
24
+ /lib/bundler/man/
25
+
26
+ # for a library or gem, you might want to ignore these files since the code is
27
+ # intended to run in multiple environments; otherwise, check them in:
28
+ # Gemfile.lock
29
+ .ruby-version
30
+ .ruby-gemset
31
+
32
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
33
+ .rvmrc
34
+
35
+ ### OSX ###
36
+ .DS_Store
37
+ .AppleDouble
38
+ .LSOverride
39
+
40
+ # Icon must end with two \r
41
+ Icon
42
+
43
+ # Thumbnails
44
+ ._*
45
+
46
+ # Files that might appear on external disk
47
+ .Spotlight-V100
48
+ .Trashes
49
+
50
+ # Directories potentially created on remote AFP share
51
+ .AppleDB
52
+ .AppleDesktop
53
+ Network Trash Folder
54
+ Temporary Items
55
+ .apdisk
56
+
57
+ ### Vim ###
58
+ [._]*.s[a-w][a-z]
59
+ [._]s[a-w][a-z]
60
+ *.un~
61
+ Session.vim
62
+ .netrwhist
63
+ *~
data/.travis.yml ADDED
@@ -0,0 +1,9 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.1
4
+ - 2.0.0
5
+ - ruby-head
6
+ script: bundle exec rspec
7
+ matrix:
8
+ allow_failures:
9
+ - rvm: ruby-head
data/README.md CHANGED
@@ -1,31 +1,45 @@
1
- # SimpleObject
1
+ # SimpleObjects
2
2
 
3
- TODO: Write a gem description
3
+ Simple objects for Ruby
4
+
5
+ [![Gem Version](https://badge.fury.io/rb/simple_objects.png)](https://rubygems.org/gems/simple_objects) [![Build Status](https://travis-ci.org/doomspork/simple_objects.svg?branch=master)](https://travis-ci.org/doomspork/simple_objects) [![Code Climate](https://codeclimate.com/github/doomspork/simple_objects/badges/gpa.svg)](https://codeclimate.com/github/doomspork/simple_objects) [![Coverage Status](https://coveralls.io/repos/doomspork/simple_objects/badge.png?branch=master)](https://coveralls.io/r/doomspork/simple_objects?branch=master) [![Dependency Status](https://gemnasium.com/doomspork/simple_objects.svg)](https://gemnasium.com/doomspork/simple_objects)
4
6
 
5
7
  ## Installation
6
8
 
7
9
  Add this line to your application's Gemfile:
8
10
 
9
- ```ruby
10
- gem 'simple_object'
11
- ```
11
+ gem 'simple_objects'
12
12
 
13
- And then execute:
13
+ And require it:
14
14
 
15
- $ bundle
15
+ require 'simple_objects'
16
16
 
17
- Or install it yourself as:
17
+ ## Usage
18
18
 
19
- $ gem install simple_object
19
+ ```ruby
20
+ class SuperHero
21
+ include SimpleObjects::Base
22
+
23
+ attribute :name, required: true, type: String
24
+ attribute :power, required: true, type: String
25
+ attribute :age, type: Numeric
26
+ end
20
27
 
21
- ## Usage
28
+ SuperHero.new(name: 'Superman', power: 'Super stuff', age: 34)
22
29
 
23
- TODO: Write usage instructions here
30
+ SuperHero.new(name: 'Spiderman', age: 20) #=> raises RequiredMissingError
31
+
32
+ SuperHero.new(name: 'Spiderman', power: {}) #=> raises MismatchedTypeError
33
+ ```
24
34
 
25
35
  ## Contributing
26
36
 
27
- 1. Fork it ( https://github.com/doomspork/simple_object/fork )
28
- 2. Create your feature branch (`git checkout -b my-new-feature`)
29
- 3. Commit your changes (`git commit -am 'Add some feature'`)
30
- 4. Push to the branch (`git push origin my-new-feature`)
31
- 5. Create a new Pull Request
37
+ Feedback and fixes are always welcome. Please make use of [Issues](https://github.com/doomspork/simple_objects/issues) and [Pull Requests](https://github.com/doomspork/simple_objects/pulls), all code should have test coverage.
38
+
39
+ ## Author/Contact
40
+
41
+ SimpleObjects is written and maintained by [@doomspork](github.com/doomspork).
42
+
43
+ ## License
44
+
45
+ SimpleObjects is made available under the [MIT](http://opensource.org/licenses/MIT) License.
@@ -0,0 +1,19 @@
1
+ module SimpleObjects
2
+ class Attribute
3
+ attr_accessor :default, :name, :required, :type
4
+
5
+ def initialize(name, opts = {})
6
+ @name = name
7
+ @required = false
8
+ apply_opts(opts)
9
+ end
10
+
11
+ private
12
+
13
+ def apply_opts(opts)
14
+ opts.each do |opt, value|
15
+ send("#{opt}=", value) if respond_to?(opt)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,59 @@
1
+ require_relative 'attribute'
2
+ require_relative 'errors/mismatched_type_error'
3
+ require_relative 'errors/required_missing_error'
4
+
5
+ module SimpleObjects
6
+ module Attributes
7
+
8
+ def self.included(base)
9
+ base.extend(ClassMethods)
10
+ end
11
+
12
+ module ClassMethods
13
+ attr_reader :attributes
14
+
15
+ def attribute(attribute, opts = {})
16
+ @attributes ||= {}
17
+ attr = Attribute.new(attribute, opts)
18
+ @attributes[attribute] = attr
19
+ send(:attr_reader, attribute)
20
+ end
21
+
22
+ def attributes
23
+ @attributes || {}
24
+ end
25
+ end
26
+
27
+ protected
28
+
29
+ def mass_assign(inputs)
30
+ attributes.each do |(name, attr)|
31
+ value = inputs[name]
32
+ if value
33
+ mismatch(attr, value) if (type = attr.type) && !(value.is_a? type)
34
+ set_value(name, value)
35
+ next
36
+ end
37
+
38
+ missing(name) if attr.required
39
+ set_value(name, attr.default) if attr.default
40
+ end
41
+ end
42
+
43
+ def attributes
44
+ self.class.attributes
45
+ end
46
+
47
+ def set_value(name, value)
48
+ instance_variable_set("@#{name}", value)
49
+ end
50
+
51
+ def missing(name)
52
+ raise RequiredMissingError.new("Missing required value for #{name}")
53
+ end
54
+
55
+ def mismatch(attribute, value)
56
+ raise MismatchedTypeError.new("Expected #{attribute.name} to be #{attribute.type} but was #{value.class}")
57
+ end
58
+ end
59
+ end
@@ -1,59 +1,17 @@
1
- require_relative 'defaults'
1
+ require_relative 'attributes'
2
+ require_relative 'serialization'
2
3
 
3
4
  module SimpleObjects
4
5
  module Base
6
+ include Attributes
7
+ include Serialization
5
8
 
6
9
  def self.included(base)
7
10
  base.extend(ClassMethods)
8
11
  end
9
12
 
10
- module ClassMethods
11
- def attr_accessor(*attrs)
12
- @attributes ||= []
13
- @attributes.concat(attrs)
14
- super(*attrs)
15
- end
16
-
17
- def attributes
18
- @attributes || []
19
- end
20
- end
21
-
22
13
  def initialize(attributes = {})
23
- values = defaults.merge(attributes)
24
- values.each do |attr, value|
25
- mthd = "#{attr}="
26
- send(mthd, value) if self.respond_to?(mthd)
27
- end
28
- end
29
-
30
- def to_h
31
- attributes.each_with_object({}) do |k, hsh|
32
- val = send(k)
33
- if val.nil?
34
- val = nil
35
- elsif val.respond_to?(:to_h)
36
- val = val.to_h
37
- elsif val.respond_to?(:map)
38
- val = val.map { |v| v.respond_to?(:to_h) ? v.to_h : v }
39
- end
40
- hsh[k] = val
41
- end
42
- end
43
-
44
- def as_json(opts = {})
45
- to_h.as_json(opts)
46
- end
47
-
48
- def cache_key
49
- individual_key = [@id || 'new', @updated_at].reject(&:nil?).join('-')
50
- "#{self.class.to_s.underscore}/#{individual_key}"
51
- end
52
-
53
- protected
54
-
55
- def attributes
56
- self.class.attributes
14
+ mass_assign(attributes)
57
15
  end
58
16
  end
59
17
  end
@@ -0,0 +1,2 @@
1
+ class MismatchedTypeError < RuntimeError
2
+ end
@@ -0,0 +1,2 @@
1
+ class RequiredMissingError < RuntimeError
2
+ end
@@ -0,0 +1,40 @@
1
+ module SimpleObjects
2
+ module Serialization
3
+ def to_h(opts = {})
4
+ apply_options(opts)
5
+ end
6
+
7
+ alias_method :to_hash, :to_h
8
+
9
+ private
10
+
11
+ def apply_options(opts)
12
+ hsh = hashed_values
13
+ return sub_hash(hsh, opts[:only]) if opts[:only]
14
+ opts[:except].each { |attr| hsh.delete(attr) } if opts[:except]
15
+ (opts[:methods] || {}).each_with_object(hsh) do |method, memo|
16
+ memo[method] = send(method)
17
+ end
18
+ end
19
+
20
+ def sub_hash(hash, keys)
21
+ hash.select { |(key, _)| keys.include?(key) }
22
+ end
23
+
24
+ def hashed_values
25
+ attributes.each_with_object({}) do |k, hsh|
26
+ val = send(k)
27
+ # `nil.to_h` => {} which we do not want
28
+ if val.nil?
29
+ val = nil
30
+ elsif val.respond_to?(:map)
31
+ val = val.map { |v| v.respond_to?(:to_h) ? v.to_h : v }
32
+ elsif val.respond_to?(:to_h)
33
+ val = val.to_h
34
+ end
35
+ hsh[k] = val
36
+ end
37
+ end
38
+
39
+ end
40
+ end
@@ -1,3 +1,3 @@
1
1
  module SimpleObjects
2
- VERSION = '0.0.1'
2
+ VERSION = '1.0'
3
3
  end
@@ -2,6 +2,4 @@ require 'simple_objects/version'
2
2
  require 'simple_objects/base'
3
3
 
4
4
  module SimpleObjects
5
- include Base
6
- include Defaults
7
5
  end
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ module SimpleObjects
4
+ describe Attribute do
5
+ subject { Attribute.new('test') }
6
+
7
+ it { is_expected.to respond_to :default }
8
+ it { is_expected.to respond_to :name }
9
+ it { is_expected.to respond_to :required }
10
+ it { is_expected.to respond_to :type }
11
+
12
+ context 'with options' do
13
+ subject { Attribute.new('test', default: 'foo', type: String, required: true) }
14
+
15
+ its(:name) { is_expected.to eq 'test' }
16
+ its(:default) { is_expected.to eq 'foo' }
17
+ its(:required) { is_expected.to be_truthy }
18
+ its(:type) { is_expected.to eq String }
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,93 @@
1
+ require 'spec_helper'
2
+
3
+ module SimpleObjects
4
+ describe Attributes do
5
+
6
+ describe '.attribute' do
7
+ class BasicExample
8
+ include Attributes
9
+
10
+ attribute :foo
11
+ end
12
+
13
+ subject { BasicExample.new }
14
+
15
+ it { is_expected.to respond_to :foo }
16
+ end
17
+
18
+ describe '#mass_assign' do
19
+ context 'with option' do
20
+ class OptionsExample
21
+ include Attributes
22
+
23
+ attribute :one
24
+ attribute :two, required: true
25
+ attribute :three, default: 'three'
26
+ attribute :four, type: String
27
+ end
28
+
29
+ let(:inputs) { }
30
+
31
+ subject do
32
+ example = OptionsExample.new
33
+ example.send(:mass_assign, inputs)
34
+ example
35
+ end
36
+
37
+ context ':default' do
38
+ context 'without a value' do
39
+ let(:inputs) { { two: '' } }
40
+
41
+ it 'will set the default value' do
42
+ expect(subject.three).to eq 'three'
43
+ end
44
+ end
45
+
46
+ context 'with a value' do
47
+ let(:inputs) { { two: '', three: 'one' } }
48
+
49
+ it 'will do nothing' do
50
+ expect(subject.three).to eq 'one'
51
+ end
52
+ end
53
+ end
54
+
55
+ context ':required' do
56
+ context 'without a value' do
57
+ let(:inputs) { {} }
58
+
59
+ it 'raises RequiredMissingError' do
60
+ expect { subject }.to raise_error RequiredMissingError
61
+ end
62
+ end
63
+
64
+ context 'with a value' do
65
+ let(:inputs) { { two: 'value' } }
66
+
67
+ it 'will do nothing' do
68
+ expect(subject.two).to eq 'value'
69
+ end
70
+ end
71
+ end
72
+
73
+ context ':type' do
74
+ context 'with wrong type' do
75
+ let(:inputs) { { two: 'value', four: 1 } }
76
+
77
+ it 'raises MismatchedTypeError' do
78
+ expect { subject }.to raise_error MismatchedTypeError
79
+ end
80
+ end
81
+
82
+ context 'with matching types' do
83
+ let(:inputs) { { two: 'value', four: 'a string' } }
84
+
85
+ it 'will do nothing' do
86
+ expect { subject }.to_not raise_error
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
@@ -3,12 +3,15 @@ require 'spec_helper'
3
3
  module SimpleObjects
4
4
  describe Base do
5
5
 
6
- class MockObject
6
+ class ExampleBase
7
7
  include Base
8
8
 
9
- attr_accessor :one, :two, :three
9
+ attribute :one
10
+ attribute :two
10
11
  end
11
12
 
13
+ subject { ExampleBase.new }
14
+
12
15
  describe '#initialize' do
13
16
  let(:args) do
14
17
  {
@@ -17,17 +20,12 @@ module SimpleObjects
17
20
  }
18
21
  end
19
22
 
20
- subject { MockObject.new(args) }
23
+ subject { ExampleBase.new(args) }
21
24
 
22
25
  its(:one) { is_expected.to eq 1 }
23
26
  its(:two) { is_expected.to eq 3 }
24
- its(:three) { is_expected.to be_nil }
25
27
  end
26
28
 
27
- describe '#to_h' do
28
- subject { MockObject.new(one: 'foo', two: 2).to_h }
29
-
30
- it { is_expected.to include(one: 'foo', two: 2, three: nil) }
31
- end
29
+ it { is_expected.to respond_to(:to_h) }
32
30
  end
33
31
  end
@@ -0,0 +1,67 @@
1
+ require 'spec_helper'
2
+
3
+ module SimpleObjects
4
+ describe Serialization do
5
+
6
+ class ExampleSerialization
7
+ include Serialization
8
+
9
+ attr_accessor :one, :two
10
+
11
+ def timestamp
12
+ Time.now
13
+ end
14
+
15
+ def attributes
16
+ [:one, :two]
17
+ end
18
+ end
19
+
20
+ describe '#to_h' do
21
+ let(:options) { {} }
22
+ let(:example) { ExampleSerialization.new }
23
+
24
+ subject { example.to_h(options) }
25
+
26
+ it { is_expected.to be_a Hash }
27
+ it { is_expected.to include(:one, :two) }
28
+
29
+ context 'with option' do
30
+ context ':only' do
31
+ let(:options) { { only: [:one] } }
32
+
33
+ it { is_expected.to_not include(:two) }
34
+ end
35
+
36
+ context ':except' do
37
+ let(:options) { { except: [:one] } }
38
+
39
+ it { is_expected.to_not include(:one) }
40
+ end
41
+
42
+ context ':methods' do
43
+ let(:options) { { methods: [:timestamp] } }
44
+
45
+ it { is_expected.to include(:timestamp) }
46
+ end
47
+ end
48
+
49
+ context 'with a collection' do
50
+ it 'will call .to_h on entries' do
51
+ example.one = [ExampleSerialization.new]
52
+ expect(subject[:one]).to be_a Array
53
+ expect(subject[:one].first).to include(:one, :two)
54
+ end
55
+ end
56
+
57
+ context 'with a value that responds to .to_h' do
58
+ it 'will call .to_h' do
59
+ example.one = ExampleSerialization.new
60
+
61
+ expect(subject[:one]).to include(:one, :two)
62
+ end
63
+ end
64
+
65
+ end
66
+ end
67
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: simple_objects
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: '1.0'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sean Callan
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-03-08 00:00:00.000000000 Z
11
+ date: 2015-03-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -102,16 +102,24 @@ extensions: []
102
102
  extra_rdoc_files: []
103
103
  files:
104
104
  - ".gitignore"
105
+ - ".travis.yml"
105
106
  - Gemfile
106
107
  - LICENSE
107
108
  - README.md
108
109
  - Rakefile
109
110
  - lib/simple_objects.rb
111
+ - lib/simple_objects/attribute.rb
112
+ - lib/simple_objects/attributes.rb
110
113
  - lib/simple_objects/base.rb
111
- - lib/simple_objects/defaults.rb
114
+ - lib/simple_objects/errors/mismatched_type_error.rb
115
+ - lib/simple_objects/errors/required_missing_error.rb
116
+ - lib/simple_objects/serialization.rb
112
117
  - lib/simple_objects/version.rb
113
118
  - simple_objects.gemspec
119
+ - spec/lib/simple_objects/attribute_spec.rb
120
+ - spec/lib/simple_objects/attributes_spec.rb
114
121
  - spec/lib/simple_objects/base_spec.rb
122
+ - spec/lib/simple_objects/serialization_spec.rb
115
123
  - spec/spec_helper.rb
116
124
  homepage:
117
125
  licenses:
@@ -138,5 +146,8 @@ signing_key:
138
146
  specification_version: 4
139
147
  summary: Simple objects for Ruby
140
148
  test_files:
149
+ - spec/lib/simple_objects/attribute_spec.rb
150
+ - spec/lib/simple_objects/attributes_spec.rb
141
151
  - spec/lib/simple_objects/base_spec.rb
152
+ - spec/lib/simple_objects/serialization_spec.rb
142
153
  - spec/spec_helper.rb
@@ -1,23 +0,0 @@
1
- module SimpleObjects
2
- module Defaults
3
-
4
- def self.included(base)
5
- base.extend(ClassMethods)
6
- end
7
-
8
- module ClassMethods
9
- def attr_default(*defaults)
10
- @defaults ||= {}
11
- defaults.each { |default| @defaults.merge!(default) }
12
- end
13
-
14
- def defaults
15
- @defaults || {}
16
- end
17
- end
18
-
19
- def defaults
20
- self.class.defaults
21
- end
22
- end
23
- end