id 0.0.0 → 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,6 @@
1
+ /.bundle
2
+ /log/*.log
3
+ /tmp
4
+ /tags
5
+ /coverage
6
+ *.gem
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source :rubygems
2
+ gemspec
@@ -0,0 +1,34 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ id (0.0.1)
5
+ active_support
6
+
7
+ GEM
8
+ remote: http://rubygems.org/
9
+ specs:
10
+ active_support (3.0.0)
11
+ activesupport (= 3.0.0)
12
+ activesupport (3.0.0)
13
+ diff-lcs (1.2.1)
14
+ multi_json (1.7.2)
15
+ rspec (2.13.0)
16
+ rspec-core (~> 2.13.0)
17
+ rspec-expectations (~> 2.13.0)
18
+ rspec-mocks (~> 2.13.0)
19
+ rspec-core (2.13.1)
20
+ rspec-expectations (2.13.0)
21
+ diff-lcs (>= 1.1.3, < 2.0)
22
+ rspec-mocks (2.13.0)
23
+ simplecov (0.7.1)
24
+ multi_json (~> 1.0)
25
+ simplecov-html (~> 0.7.1)
26
+ simplecov-html (0.7.1)
27
+
28
+ PLATFORMS
29
+ ruby
30
+
31
+ DEPENDENCIES
32
+ id!
33
+ rspec
34
+ simplecov
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 On The Beach Ltd
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,19 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'id'
3
+ s.version = '0.0.1'
4
+ s.date = '2013-03-28'
5
+ s.summary = "Simple models based on hashes"
6
+ s.description = "Developed at On The Beach Ltd. Contact russell.dunphy@onthebeach.co.uk"
7
+ s.authors = ["Russell Dunphy", "Radek Molenda"]
8
+ s.email = ['russell@russelldunphy.com', 'radek.molenda@gmail.com']
9
+ s.files = `git ls-files`.split($\)
10
+ s.test_files = s.files.grep(%r{^(test|spec|features)/})
11
+ s.require_paths = ["lib"]
12
+ s.homepage = 'http://rubygems.org/gems/id'
13
+
14
+ s.add_dependency "active_support"
15
+
16
+ s.add_development_dependency "rspec"
17
+ s.add_development_dependency "simplecov"
18
+
19
+ end
data/lib/id.rb CHANGED
@@ -1,2 +1,3 @@
1
- module Id
2
- end
1
+ require 'active_support/inflector'
2
+ require 'active_support/core_ext/string/inflections'
3
+ require_relative 'id/all'
@@ -0,0 +1,3 @@
1
+ require_relative 'missing_attribute_error'
2
+ require_relative 'model/all'
3
+ require_relative 'model'
@@ -0,0 +1,4 @@
1
+ module Id
2
+ class MissingAttributeError < StandardError
3
+ end
4
+ end
@@ -0,0 +1,35 @@
1
+ module Id
2
+ module Model
3
+
4
+ attr_reader :data
5
+
6
+ def initialize(data)
7
+ @data = Hash[data.map { |k, v| [k.to_s, v] }]
8
+ end
9
+
10
+ def self.included(base)
11
+ base.extend(Descriptor)
12
+ end
13
+
14
+ private
15
+
16
+ def memoize(f, &b)
17
+ instance_variable_get("@#{f}") || instance_variable_set("@#{f}", b.call)
18
+ end
19
+
20
+ module Descriptor
21
+ def field(f, options={})
22
+ Field.new(self, f, options).define
23
+ end
24
+
25
+ def has_one(f, options={})
26
+ HasOne.new(self, f, options).define
27
+ end
28
+
29
+ def has_many(f, options={})
30
+ HasMany.new(self, f, options).define
31
+ end
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,4 @@
1
+ require_relative 'field'
2
+ require_relative 'association'
3
+ require_relative 'has_one'
4
+ require_relative 'has_many'
@@ -0,0 +1,47 @@
1
+ module Id
2
+ module Model
3
+ class Association < Field
4
+
5
+ def type
6
+ options.fetch(:type) { inferred_class }
7
+ end
8
+
9
+ def inferred_class
10
+ hierarchy.parent.const_get(inferred_class_name)
11
+ end
12
+
13
+ def inferred_class_name
14
+ @inferred_class_name ||= name.to_s.classify
15
+ end
16
+
17
+ def hierarchy
18
+ @hierarchy ||= Hierarchy.new(model.name, inferred_class_name)
19
+ end
20
+
21
+ class Hierarchy
22
+
23
+ def initialize(path, child)
24
+ @path = path
25
+ @child = child
26
+ end
27
+
28
+ def parent
29
+ @parent ||= constants.find { |c| c.const_defined? child }
30
+ end
31
+
32
+ def constants
33
+ hierarchy.map(&:constantize)
34
+ end
35
+
36
+ private
37
+
38
+ def hierarchy(name=path)
39
+ name.match /(.*)::.*$/
40
+ $1 ? [name] + hierarchy($1) : [name]
41
+ end
42
+
43
+ attr_reader :path, :child
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,30 @@
1
+ module Id
2
+ module Model
3
+ class Field
4
+
5
+ def initialize(model, name, options)
6
+ @model = model
7
+ @name = name
8
+ @options = options
9
+ end
10
+
11
+ def define
12
+ field = self
13
+ model.send :define_method, name do
14
+ data.fetch(field.key) { field.default or raise MissingAttributeError }
15
+ end
16
+ end
17
+
18
+ def key
19
+ options.fetch(:key, name).to_s
20
+ end
21
+
22
+ def default
23
+ options.fetch(:default, nil)
24
+ end
25
+
26
+ attr_reader :model, :name, :options
27
+ end
28
+
29
+ end
30
+ end
@@ -0,0 +1,16 @@
1
+ module Id
2
+ module Model
3
+ class HasMany < Association
4
+
5
+ def define
6
+ field = self
7
+ model.send :define_method, name do
8
+ memoize field.name do
9
+ data.fetch(field.key, []).map { |r| field.type.new(r) }
10
+ end
11
+ end
12
+ end
13
+
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ module Id
2
+ module Model
3
+ class HasOne < Association
4
+
5
+ def define
6
+ field = self
7
+ model.send :define_method, name do
8
+ memoize field.name do
9
+ field.type.new(data.fetch(field.key) { raise MissingAttributeError })
10
+ end
11
+ end
12
+ end
13
+
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,101 @@
1
+ require 'spec_helper'
2
+
3
+ class NestedModel
4
+ include Id::Model
5
+ field :yak
6
+ end
7
+
8
+ class TestModel
9
+ include Id::Model
10
+
11
+ field :foo
12
+ field :bar, key: 'baz'
13
+ field :quux, default: 'kwak'
14
+
15
+ has_one :aliased_model, type: NestedModel
16
+ has_one :nested_model, key: 'aliased_model'
17
+ has_one :extra_nested_model
18
+ has_one :test_model
19
+ has_many :nested_models
20
+
21
+ class ExtraNestedModel
22
+ include Id::Model
23
+ field :cats!
24
+ end
25
+ end
26
+
27
+ describe Id::Model do
28
+ let (:model) { TestModel.new(foo: 3,
29
+ baz: 6,
30
+ test_model: {},
31
+ aliased_model: { 'yak' => 11},
32
+ nested_models: [{ 'yak' => 11}, { yak: 14 }],
33
+ extra_nested_model: { cats!: "MIAOW" }) }
34
+
35
+ describe ".field" do
36
+ it 'defines an accessor on the model' do
37
+ model.foo.should eq 3
38
+ end
39
+
40
+ it 'allows key aliases' do
41
+ model.bar.should eq 6
42
+ end
43
+
44
+ it 'allows default values' do
45
+ model.quux.should eq 'kwak'
46
+ end
47
+
48
+ end
49
+
50
+ describe ".has_one" do
51
+ it "allows nested models" do
52
+ model.aliased_model.should be_a NestedModel
53
+ end
54
+ it "allows nested models" do
55
+ model.nested_model.should be_a NestedModel
56
+ model.nested_model.yak.should eq 11
57
+ end
58
+ it "allows associations to be nested within the class" do
59
+ model.extra_nested_model.should be_a TestModel::ExtraNestedModel
60
+ model.extra_nested_model.cats!.should eq 'MIAOW'
61
+ end
62
+ it "allows recursively defined models" do
63
+ model.test_model.should be_a TestModel
64
+ end
65
+ end
66
+
67
+ describe ".has_many" do
68
+ it 'creates an array of nested models' do
69
+ model.nested_models.should be_a Array
70
+ model.nested_models.first.should be_a NestedModel
71
+ model.nested_models.first.yak.should eq 11
72
+ model.nested_models.last.yak.should eq 14
73
+ end
74
+ end
75
+ end
76
+
77
+ describe Id::Model::HasOne do
78
+
79
+ module Foo
80
+ module Bar
81
+ module Baz
82
+ class Quux
83
+ end
84
+ end
85
+ end
86
+ end
87
+
88
+ let (:model) { stub(name: "Foo::Bar::Baz::Quux") }
89
+ let (:has_one) { Id::Model::HasOne.new(model, "yak", {}) }
90
+
91
+ describe "hierarchy" do
92
+ it "builds the class and module hierarchy for the model" do
93
+ has_one.hierarchy.constants.should eq [
94
+ Foo::Bar::Baz::Quux,
95
+ Foo::Bar::Baz,
96
+ Foo::Bar,
97
+ Foo
98
+ ]
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,13 @@
1
+ require 'simplecov'
2
+ SimpleCov.start do
3
+ add_filter '/spec'
4
+ end
5
+
6
+ require 'rspec'
7
+ require_relative '../lib/id'
8
+
9
+
10
+ RSpec.configure do |config|
11
+ config.order = :rand
12
+ config.color_enabled = true
13
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: id
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.0
4
+ version: 0.0.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -11,8 +11,56 @@ autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
13
  date: 2013-03-28 00:00:00.000000000 Z
14
- dependencies: []
15
- description: ''
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: active_support
17
+ requirement: !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ! '>='
21
+ - !ruby/object:Gem::Version
22
+ version: '0'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - ! '>='
29
+ - !ruby/object:Gem::Version
30
+ version: '0'
31
+ - !ruby/object:Gem::Dependency
32
+ name: rspec
33
+ requirement: !ruby/object:Gem::Requirement
34
+ none: false
35
+ requirements:
36
+ - - ! '>='
37
+ - !ruby/object:Gem::Version
38
+ version: '0'
39
+ type: :development
40
+ prerelease: false
41
+ version_requirements: !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ! '>='
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ - !ruby/object:Gem::Dependency
48
+ name: simplecov
49
+ requirement: !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: !ruby/object:Gem::Requirement
58
+ none: false
59
+ requirements:
60
+ - - ! '>='
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ description: Developed at On The Beach Ltd. Contact russell.dunphy@onthebeach.co.uk
16
64
  email:
17
65
  - russell@russelldunphy.com
18
66
  - radek.molenda@gmail.com
@@ -20,7 +68,22 @@ executables: []
20
68
  extensions: []
21
69
  extra_rdoc_files: []
22
70
  files:
71
+ - .gitignore
72
+ - Gemfile
73
+ - Gemfile.lock
74
+ - LICENSE.md
75
+ - id.gemspec
23
76
  - lib/id.rb
77
+ - lib/id/all.rb
78
+ - lib/id/missing_attribute_error.rb
79
+ - lib/id/model.rb
80
+ - lib/id/model/all.rb
81
+ - lib/id/model/association.rb
82
+ - lib/id/model/field.rb
83
+ - lib/id/model/has_many.rb
84
+ - lib/id/model/has_one.rb
85
+ - spec/lib/id_spec.rb
86
+ - spec/spec_helper.rb
24
87
  homepage: http://rubygems.org/gems/id
25
88
  licenses: []
26
89
  post_install_message:
@@ -45,4 +108,6 @@ rubygems_version: 1.8.25
45
108
  signing_key:
46
109
  specification_version: 3
47
110
  summary: Simple models based on hashes
48
- test_files: []
111
+ test_files:
112
+ - spec/lib/id_spec.rb
113
+ - spec/spec_helper.rb