kludge 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .rbenv-gemsets
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in kludge.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 hubert
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.
data/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # Kludge
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'kludge'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install kludge
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/kludge.gemspec ADDED
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'kludge/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "kludge"
8
+ spec.version = Kludge::VERSION
9
+ spec.authors = ["hubert", "ryan moran"]
10
+ spec.email = ["hubert77@gmail.com", 'ryan.moran@gmail.com']
11
+ spec.description = %q{Nested attributes alternative}
12
+ spec.summary = %q{hello}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency 'activemodel', '>= 3.0'
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.3"
24
+ spec.add_development_dependency "rake"
25
+ spec.add_development_dependency 'rspec'
26
+ end
@@ -0,0 +1,40 @@
1
+ module Kludge
2
+ class Many < Part
3
+
4
+ def save
5
+ value.each(&:save)
6
+ super
7
+ end
8
+
9
+ def validate
10
+ if value.any? { |v| !v.valid? }
11
+ value.map(&:errors).each do |e|
12
+ e.each do |attribute, errors_array|
13
+ errors_array.each do |msg|
14
+ errors.add(attribute, msg) unless errors.added?(attribute, msg)
15
+ end
16
+ end
17
+ end
18
+ false
19
+ else
20
+ true
21
+ end
22
+ end
23
+
24
+ def value=(value)
25
+ p value
26
+ @value = value.map do |val|
27
+ if val.kind_of?(Hash)
28
+ if val[:id].present?
29
+ @name.to_s.classify.constantize.find(val.delete(:id)).tap { |v| v.assign_attributes(val) }
30
+ else
31
+ @name.to_s.classify.constantize.new(val)
32
+ end
33
+ else
34
+ val
35
+ end
36
+ end
37
+ end
38
+
39
+ end
40
+ end
@@ -0,0 +1,43 @@
1
+ module Kludge
2
+ class Mishmash
3
+
4
+ def self.parts
5
+ @parts ||= Parts.new
6
+ end
7
+
8
+ def self.one(name, options = {})
9
+ parts << One.new(name, options)
10
+ attr_reader name
11
+
12
+ define_method "#{name}=" do |value|
13
+ @parts.find { |part| part.name == name }.value = value
14
+ instance_variable_set("@#{name}", value)
15
+ end
16
+ end
17
+
18
+ def self.many(name, options = {})
19
+ parts << Many.new(name, options)
20
+ attr_reader name
21
+
22
+ define_method "#{name}=" do |value|
23
+ @parts.find { |part| part.name == name }.value = value
24
+ instance_variable_set("@#{name}", value)
25
+ end
26
+ end
27
+
28
+ def initialize(attributes = {})
29
+ @parts = self.class.parts.dup
30
+ attributes.each do |key, value|
31
+ send("#{key}=", value)
32
+ end
33
+ end
34
+
35
+ def save
36
+ @parts.save
37
+ end
38
+
39
+ def valid?
40
+ @parts.valid?
41
+ end
42
+ end
43
+ end
data/lib/kludge/one.rb ADDED
@@ -0,0 +1,35 @@
1
+ module Kludge
2
+ class One < Part
3
+
4
+ def save
5
+ value.save
6
+ super
7
+ end
8
+
9
+ def validate
10
+ if !value.valid?
11
+ value.errors.each do |attribute, errors_array|
12
+ errors_array.each do |msg|
13
+ errors.add(attribute, msg) unless errors.added?(attribute, msg)
14
+ end
15
+ end
16
+ false
17
+ else
18
+ true
19
+ end
20
+ end
21
+
22
+ def value=(value)
23
+ p value
24
+ @value = if value.kind_of?(Hash)
25
+ if value[:id]
26
+ @name.to_s.classify.constantize.find(value.delete(:id)).tap { |v| v.assign_attributes(value) }
27
+ else
28
+ @name.to_s.classify.constantize.new(value)
29
+ end
30
+ else
31
+ value
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,41 @@
1
+ module Kludge
2
+ class Part
3
+ extend ActiveModel::Naming
4
+ extend ActiveModel::Translation
5
+
6
+ attr_reader :name, :errors, :value
7
+ attr_accessor :parent, :children
8
+
9
+ def initialize(name, options = {})
10
+ @name = name
11
+ @options = options
12
+ @value = options[:value]
13
+ @children = []
14
+ @errors = ActiveModel::Errors.new(self)
15
+ end
16
+
17
+ def many?
18
+ self.class == Many
19
+ end
20
+
21
+ def one?
22
+ self.class == One
23
+ end
24
+
25
+ def dependent?
26
+ @options[:belongs_to]
27
+ end
28
+
29
+ def dependency
30
+ @options[:belongs_to]
31
+ end
32
+
33
+ def save
34
+ children.each(&:save)
35
+ end
36
+
37
+ def valid?
38
+ validate && children.map(&:valid?).all?
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,60 @@
1
+ module Kludge
2
+ class Parts
3
+ include Enumerable
4
+
5
+ def initialize
6
+ @parts = []
7
+ end
8
+
9
+ def each
10
+ @parts.each do |part|
11
+ yield part
12
+ end
13
+ end
14
+
15
+ def save
16
+ graph.each do |root|
17
+ root.save
18
+ end
19
+ end
20
+
21
+ def valid?
22
+ graph.map do |root|
23
+ root.valid?
24
+ end.all?
25
+ end
26
+
27
+ def names
28
+ @parts.map(&:name)
29
+ end
30
+
31
+ def <<(part)
32
+ @parts << part
33
+ end
34
+
35
+ def graph
36
+ @graph ||= begin
37
+ leaves.map { |leaf|
38
+ assign_family(leaf)
39
+ }.uniq
40
+ end
41
+ end
42
+
43
+ def assign_family(node)
44
+ parent = @parts.detect { |part| part.name == node.dependency }
45
+ return node unless parent
46
+ node.parent = parent
47
+ parent.children << node
48
+ assign_family(parent)
49
+ end
50
+
51
+ def leaves
52
+ leaves = @parts.dup
53
+ @parts.each do |part|
54
+ leaves.delete_if { |leaf| leaf.name == part.dependency }
55
+ end
56
+ leaves
57
+ end
58
+ end
59
+ end
60
+
@@ -0,0 +1,3 @@
1
+ module Kludge
2
+ VERSION = "0.0.1"
3
+ end
data/lib/kludge.rb ADDED
@@ -0,0 +1,10 @@
1
+ require 'active_model'
2
+
3
+ module Kludge
4
+ autoload :Mishmash, 'kludge/mishmash'
5
+ autoload :Parts, 'kludge/parts'
6
+ autoload :Part, 'kludge/part'
7
+ autoload :One, 'kludge/one'
8
+ autoload :Many, 'kludge/many'
9
+ autoload :Version, 'kludge/version'
10
+ end
@@ -0,0 +1,32 @@
1
+ require 'spec_helper'
2
+
3
+ describe Kludge::Many do
4
+ class Leaf
5
+ extend ActiveModel::Naming
6
+ end
7
+
8
+ let(:leaf1) { Leaf.new }
9
+ let(:leaf2) { Leaf.new }
10
+ let(:many) { Kludge::Many.new(:leaves, :value => [leaf1, leaf2]) }
11
+
12
+ describe '#valid?' do
13
+ it 'returns true if all of its values are valid' do
14
+ Leaf.any_instance.stub(:valid? => true)
15
+ expect(many).to be_valid
16
+ end
17
+
18
+ it 'returns false if any of its values are not valid' do
19
+ leaf1.stub(:valid? => true, :errors => [])
20
+ leaf2.stub(:valid? => false, :errors => [])
21
+ expect(many).to_not be_valid
22
+ end
23
+
24
+ it 'collects the uniq error messages of its values' do
25
+ errors = ActiveModel::Errors.new(leaf1)
26
+ errors.add(:name, 'cannot be blank')
27
+ Leaf.any_instance.stub(:valid? => false, :errors => errors)
28
+ many.valid?
29
+ expect(many.errors.full_messages).to eql(['Name cannot be blank'])
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,28 @@
1
+ require 'spec_helper'
2
+
3
+ describe Kludge::Mishmash do
4
+ class Tree < Kludge::Mishmash
5
+ one :trunk
6
+ many :branches, :belongs_to => :trunk
7
+ end
8
+
9
+ let(:trunk) { mock :trunk }
10
+ let(:branch) { mock :branch }
11
+ let(:branches) { [branch, branch] }
12
+ let(:tree) { Tree.new(:trunk => trunk, :branches => branches) }
13
+
14
+ it 'contains many parts' do
15
+ Tree.parts.names.should eql([:trunk, :branches])
16
+ end
17
+
18
+ it 'defines parts as "many" or "one"' do
19
+ expect(Tree.parts.select(&:many?).length).to eql(1)
20
+ expect(Tree.parts.select(&:one?).length).to eql(1)
21
+ end
22
+
23
+ it 'defines accessor methods for each part' do
24
+ expect(tree.trunk).to eql(trunk)
25
+ expect(tree.branches).to eql(branches)
26
+ end
27
+
28
+ end
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ describe Kludge::One do
4
+ describe '#errors' do
5
+ class Trunk
6
+ extend ActiveModel::Naming
7
+ end
8
+ let(:trunk) { Trunk.new }
9
+ let(:errors) { ActiveModel::Errors.new(trunk).tap { |errors| errors.add(:name, "can't be blank") } }
10
+
11
+ before do
12
+ trunk.stub(:invalid? => true, :valid? => false, :errors => errors)
13
+ end
14
+
15
+ it 'returns the errors of its underlying value' do
16
+ one = Kludge::One.new(:trunk, :value => trunk)
17
+ expect(one).to_not be_valid
18
+ expect(one.errors.full_messages).to eql(["Name can't be blank"])
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+
3
+ describe Kludge::Part do
4
+ describe '#name' do
5
+ it 'returns the given name for the part' do
6
+ expect(Kludge::Part.new('branch').name).to eql('branch')
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,70 @@
1
+ require 'spec_helper'
2
+
3
+ describe Kludge::Parts do
4
+ let(:parts) { Kludge::Parts.new }
5
+ let(:trunk_val) { mock :trunk_val, :valid? => true }
6
+ let(:branch_val) { mock :branch_val, :valid? => true }
7
+ let(:leaves_val) { mock :leaves_val, :valid? => true }
8
+ let(:trunk) { Kludge::One.new(:trunk, :value => trunk_val) }
9
+ let(:branch) { Kludge::One.new(:branch, :belongs_to => :trunk, :value => branch_val) }
10
+ let(:leaves) { Kludge::Many.new(:leaves, :belongs_to => :branch, :value => [leaves_val]) }
11
+
12
+ before { parts << branch << trunk << leaves }
13
+
14
+ describe '#names' do
15
+ it 'returns the names of all parts contained' do
16
+ expect(parts.names).to eql([:branch, :trunk, :leaves])
17
+ end
18
+ end
19
+
20
+ describe '#save' do
21
+ it 'calls saves on all of its parts' do
22
+ [branch_val, trunk_val, leaves_val].each { |part| part.should_receive(:save).once }
23
+ parts.save
24
+ end
25
+ end
26
+
27
+ describe '#valid?' do
28
+ context 'all parts are valid' do
29
+ it 'returns true if all of its parts return true' do
30
+ expect(parts).to be_valid
31
+ end
32
+ end
33
+
34
+ context 'any of its parts are not valid' do
35
+ it 'returns false if any of its parts return false' do
36
+ leaves_val.stub(:valid? => false, :errors => [])
37
+ expect(parts).to_not be_valid
38
+ end
39
+ end
40
+ end
41
+
42
+ describe '#graph' do
43
+ before { parts.graph }
44
+
45
+ it 'sets parent and children for trunk' do
46
+ expect(trunk.parent).to be_nil
47
+ expect(trunk.children).to eql([branch])
48
+ end
49
+
50
+ it 'sets parent and children for branch' do
51
+ expect(branch.parent).to eql(trunk)
52
+ expect(branch.children).to eql([leaves])
53
+ end
54
+
55
+ it 'sets parent and children for leaves' do
56
+ expect(leaves.parent).to eql(branch)
57
+ expect(leaves.children).to be_empty
58
+ end
59
+
60
+ it 'returns roots of graph' do
61
+ expect(parts.graph).to eql([trunk])
62
+ end
63
+ end
64
+
65
+ describe '#leaves' do
66
+ it 'returns branch' do
67
+ expect(parts.leaves).to eql([leaves])
68
+ end
69
+ end
70
+ end
@@ -0,0 +1 @@
1
+ require 'kludge'
metadata ADDED
@@ -0,0 +1,148 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: kludge
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - hubert
14
+ - ryan moran
15
+ autorequire:
16
+ bindir: bin
17
+ cert_chain: []
18
+
19
+ date: 2013-05-14 00:00:00 Z
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: activemodel
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 7
30
+ segments:
31
+ - 3
32
+ - 0
33
+ version: "3.0"
34
+ type: :runtime
35
+ version_requirements: *id001
36
+ - !ruby/object:Gem::Dependency
37
+ name: bundler
38
+ prerelease: false
39
+ requirement: &id002 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ~>
43
+ - !ruby/object:Gem::Version
44
+ hash: 9
45
+ segments:
46
+ - 1
47
+ - 3
48
+ version: "1.3"
49
+ type: :development
50
+ version_requirements: *id002
51
+ - !ruby/object:Gem::Dependency
52
+ name: rake
53
+ prerelease: false
54
+ requirement: &id003 !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ hash: 3
60
+ segments:
61
+ - 0
62
+ version: "0"
63
+ type: :development
64
+ version_requirements: *id003
65
+ - !ruby/object:Gem::Dependency
66
+ name: rspec
67
+ prerelease: false
68
+ requirement: &id004 !ruby/object:Gem::Requirement
69
+ none: false
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ hash: 3
74
+ segments:
75
+ - 0
76
+ version: "0"
77
+ type: :development
78
+ version_requirements: *id004
79
+ description: Nested attributes alternative
80
+ email:
81
+ - hubert77@gmail.com
82
+ - ryan.moran@gmail.com
83
+ executables: []
84
+
85
+ extensions: []
86
+
87
+ extra_rdoc_files: []
88
+
89
+ files:
90
+ - .gitignore
91
+ - Gemfile
92
+ - LICENSE.txt
93
+ - README.md
94
+ - Rakefile
95
+ - kludge.gemspec
96
+ - lib/kludge.rb
97
+ - lib/kludge/many.rb
98
+ - lib/kludge/mishmash.rb
99
+ - lib/kludge/one.rb
100
+ - lib/kludge/part.rb
101
+ - lib/kludge/parts.rb
102
+ - lib/kludge/version.rb
103
+ - spec/kludge/many_spec.rb
104
+ - spec/kludge/mishmash_spec.rb
105
+ - spec/kludge/one_spec.rb
106
+ - spec/kludge/part_spec.rb
107
+ - spec/kludge/parts_spec.rb
108
+ - spec/spec_helper.rb
109
+ homepage: ""
110
+ licenses:
111
+ - MIT
112
+ post_install_message:
113
+ rdoc_options: []
114
+
115
+ require_paths:
116
+ - lib
117
+ required_ruby_version: !ruby/object:Gem::Requirement
118
+ none: false
119
+ requirements:
120
+ - - ">="
121
+ - !ruby/object:Gem::Version
122
+ hash: 3
123
+ segments:
124
+ - 0
125
+ version: "0"
126
+ required_rubygems_version: !ruby/object:Gem::Requirement
127
+ none: false
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ hash: 3
132
+ segments:
133
+ - 0
134
+ version: "0"
135
+ requirements: []
136
+
137
+ rubyforge_project:
138
+ rubygems_version: 1.8.15
139
+ signing_key:
140
+ specification_version: 3
141
+ summary: hello
142
+ test_files:
143
+ - spec/kludge/many_spec.rb
144
+ - spec/kludge/mishmash_spec.rb
145
+ - spec/kludge/one_spec.rb
146
+ - spec/kludge/part_spec.rb
147
+ - spec/kludge/parts_spec.rb
148
+ - spec/spec_helper.rb