simple_objects 0.0.1 → 1.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: 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