rrod 1.0.0.alpha.1 → 1.0.0.alpha.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,15 @@
1
+ module Rrod
2
+ module Model
3
+ module Validations
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ include ActiveModel::Validations
8
+ end
9
+
10
+ def save(options={})
11
+ options.fetch(:validate, true) ? (valid? and super()) : super()
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,29 @@
1
+ module Rrod
2
+ module Model
3
+ module Validations
4
+
5
+ class AssociatedValidator < ActiveModel::EachValidator
6
+ def validate_each(record, attribute, value)
7
+ return if Array(value).collect{ |r| r.nil? || r.valid? }.all?
8
+ record.errors.add(attribute, error_message_for(attribute, value))
9
+ end
10
+
11
+ private
12
+
13
+ def error_message_for(attribute, associated_records)
14
+ Array(associated_records).map(&:errors).reject(&:blank?).map(&:full_messages).map(&:to_sentence).flatten.join('; ')
15
+ end
16
+
17
+ end
18
+
19
+ module ClassMethods
20
+
21
+ def validates_associated(*attrs)
22
+ validates_with AssociatedValidator, _merge_attributes(attrs)
23
+ end
24
+
25
+ end
26
+
27
+ end
28
+ end
29
+ end
data/lib/rrod/query.rb CHANGED
@@ -27,4 +27,4 @@ module Rrod
27
27
  end
28
28
 
29
29
  end
30
- end
30
+ end
@@ -18,7 +18,7 @@ module Rrod
18
18
  fail "Test server not working: #{Rrod::TestServer.fatal}"
19
19
  end
20
20
 
21
- if example.metadata[:test_server] == false
21
+ if ::RSpec.current_example.metadata[:test_server] == false
22
22
  Rrod::TestServer.stop
23
23
  else
24
24
  Rrod::TestServer.create unless Rrod::TestServer.exist?
@@ -32,9 +32,10 @@ module Rrod
32
32
 
33
33
  config.after(:each, :integration => true) do
34
34
  # i really don't understand this...
35
+ # well the 'example' needs to be accessed another way, Rspec.current_example.
35
36
  if !Rrod::TestServer.fatal &&
36
37
  Rrod::TestServer.started? &&
37
- example.metadata[:test_server] != false
38
+ ::RSpec.current_example.metadata[:test_server] != false
38
39
  Rrod::TestServer.drop
39
40
  end
40
41
  end
data/lib/rrod/version.rb CHANGED
@@ -1,2 +1,2 @@
1
1
  Rrod = Module.new unless defined? Rrod
2
- Rrod::VERSION = '1.0.0.alpha.1'
2
+ Rrod::VERSION = '1.0.0.alpha.2'
@@ -10,7 +10,7 @@ describe Rrod::Model do
10
10
  describe "instantiation" do
11
11
  it "can create an object with an arbitrary hash" do
12
12
  expect(instance.wheels).to eq 4
13
- expect(instance.color).to eq :black
13
+ # @TODO expect(instance.color).to eq :black
14
14
  expect(instance.make).to eq 'Jeep'
15
15
  end
16
16
 
@@ -20,7 +20,7 @@ describe Rrod::Model do
20
20
  end
21
21
 
22
22
  it "manages attribute keys as strings" do
23
- expect(instance.attributes).to eq hash.stringify_keys
23
+ expect(instance.attributes.keys).to eq hash.stringify_keys.keys
24
24
  end
25
25
 
26
26
  it "ignores modifications to the attribute hash" do
@@ -57,8 +57,19 @@ describe Rrod::Model do
57
57
  end
58
58
  end
59
59
 
60
- describe "query generation" do
61
- it "lets you use strings with apostrophes in them"
60
+ end
61
+
62
+ describe "defaults" do
63
+ let(:model) { Class.new(Car) { attribute :wheels, Integer, default: 4 } }
64
+ let(:instance) { model.new }
65
+
66
+ it "will return the default when reading a nil value" do
67
+ expect(instance.wheels).to eq 4
68
+ end
69
+
70
+ it "will set the default to the read value" do
71
+ instance.wheels
72
+ expect(instance.instance_variable_get(:@attributes)['wheels']).to eq 4
62
73
  end
63
74
  end
64
75
  end
@@ -30,10 +30,13 @@ describe Rrod::Model::Attribute do
30
30
  end
31
31
 
32
32
  describe "defaults" do
33
+ let(:default) { attribute.default(instance) }
34
+
33
35
  context "when a value" do
34
36
  let(:options) { {default: 'SOO fluffy!'} }
37
+
35
38
  it "can provide a default value" do
36
- expect(attribute.default).to eq 'SOO fluffy!'
39
+ expect(default).to eq 'SOO fluffy!'
37
40
  end
38
41
  end
39
42
 
@@ -41,7 +44,17 @@ describe Rrod::Model::Attribute do
41
44
  let(:options) { {default: -> { 'alligator' }} }
42
45
 
43
46
  it "can provide a default value if a proc" do
44
- expect(attribute.default).to eq 'alligator'
47
+ expect(default).to eq 'alligator'
48
+ end
49
+
50
+ context "given an instance" do
51
+ let(:model) { Class.new { include Rrod::Model; attr_accessor :foo } }
52
+ let(:options) { {default: -> { foo.upcase }} }
53
+
54
+ it "evaluates in the context of the given instance" do
55
+ instance.foo = 'whoah'
56
+ expect(default).to eq('WHOAH')
57
+ end
45
58
  end
46
59
  end
47
60
  end
@@ -0,0 +1,60 @@
1
+ require 'spec_helper'
2
+ require 'support/models/person'
3
+
4
+ describe Rrod::Model::Callbacks do
5
+ let(:instance) { Person.new }
6
+
7
+ describe "assignment" do
8
+ it "provides callbacks" do
9
+ expect(instance).to receive(:poke)
10
+ instance.attributes = {name: 'Pooka'}
11
+ end
12
+ end
13
+
14
+ describe "validation" do
15
+ it "provides callbacks" do
16
+ expect(instance).to receive(:stuffs)
17
+ instance.valid?
18
+ end
19
+ end
20
+
21
+ describe "saving" do
22
+ it "provides callbacks" do
23
+ expect(instance).to receive(:other_stuffs)
24
+ instance.save
25
+ end
26
+
27
+ describe "create" do
28
+ it "runs when new" do
29
+ expect(instance).to receive(:created!)
30
+ expect(instance).not_to receive(:updated!)
31
+ instance.save
32
+ end
33
+
34
+ it "does not run when persisted" do
35
+ instance.instance_variable_set :@persisted, true
36
+ expect(instance).not_to receive(:created!)
37
+ instance.save
38
+ end
39
+ end
40
+
41
+ describe "update" do
42
+ before :each do
43
+ instance.instance_variable_set :@persisted, true
44
+ end
45
+
46
+ it "runs when persisted" do
47
+ expect(instance).to receive(:updated!)
48
+ expect(instance).not_to receive(:created!)
49
+ instance.save
50
+ end
51
+
52
+ it "does not run when new" do
53
+ instance.instance_variable_set :@persisted, false
54
+ expect(instance).not_to receive(:updated!)
55
+ instance.save
56
+ end
57
+ end
58
+ end
59
+
60
+ end
@@ -3,7 +3,7 @@ require 'support/models/person'
3
3
 
4
4
  describe Rrod::Model::Collection do
5
5
  let(:array) { [Pet.new] }
6
- let(:collection) { described_class.new(array) }
6
+ let(:collection) { described_class.new(Pet, array) }
7
7
 
8
8
  describe "initialization" do
9
9
  it "takes a collection" do
@@ -11,9 +11,18 @@ describe Rrod::Model::Collection do
11
11
  end
12
12
 
13
13
  it "defaults to an empty collection" do
14
- expect(described_class.new.collection).to be_an Array
14
+ expect(described_class.new(Pet).collection).to be_an Array
15
15
  end
16
16
 
17
+ describe "#build" do
18
+ it "adds to the collection" do
19
+ collection.build(name: 'Lion')
20
+ expect(collection.collection.length).to eq 2
21
+ expect(collection.collection.last.name).to eq('Lion')
22
+ end
23
+ end
24
+
25
+
17
26
  describe "errors" do
18
27
  describe "non enumerable" do
19
28
  let(:array) { Object.new }
@@ -22,14 +31,14 @@ describe Rrod::Model::Collection do
22
31
  end
23
32
  end
24
33
 
25
- describe "not all Rrod::Model" do
26
- let(:array) { [Pet.new, Object.new] }
34
+ describe "not all same Rrod::Model" do
35
+ let(:array) { [Pet.new, Address.new] }
27
36
  it "raises if not all `Rrod::Model`s" do
28
37
  expect { collection }.to raise_error(Rrod::Model::Collection::InvalidMemberTypeError)
29
38
  end
30
39
 
31
40
  it "clears the collection" do
32
- collection = described_class.new
41
+ collection = described_class.new(Pet)
33
42
  begin; collection.collection = array; rescue; end
34
43
  expect(collection.collection).to be_empty
35
44
  end
@@ -31,29 +31,29 @@ describe Rrod::Model::Finders, integration: true do
31
31
  end
32
32
 
33
33
  describe "finding by attributes in the hash" do
34
- describe "find_first_by" do
34
+ describe "find_by" do
35
35
  it "can find one" do
36
- found = model.find_first_by(make: 'Jeep')
36
+ found = model.find_by(make: 'Jeep')
37
37
  expect(found).to be_a model
38
38
  expect(found.make).to eq "Jeep"
39
39
  end
40
40
 
41
41
  it "will work properly when finding by id" do
42
- found = model.find_first_by(id: instance.id)
42
+ found = model.find_by(id: instance.id)
43
43
  expect(found).to be_a model
44
44
  end
45
45
 
46
46
  it "will return nil if one can't be found" do
47
- found = model.find_first_by(id: "id that is not there")
47
+ found = model.find_by(id: "id that is not there")
48
48
  expect(found).to be nil
49
49
  end
50
50
 
51
51
  it "will raise an exception if one can't be found with a !" do
52
- expect { model.find_first_by! zombies: true }.to raise_error(Rrod::Model::NotFound)
52
+ expect { model.find_by! zombies: true }.to raise_error(ArgumentError)
53
53
  end
54
54
  end
55
55
 
56
- describe "find_all_by" do
56
+ describe "search" do
57
57
  it "can find all" do
58
58
  founds = model.find_all_by(make: 'Jeep', wheels: 4)
59
59
  found = founds.first
@@ -62,17 +62,18 @@ describe Rrod::Model::Finders, integration: true do
62
62
  expect(found.make).to eq "Jeep"
63
63
  end
64
64
 
65
- it "will raise an exception if finding all by id" do
65
+ it "will raise an exception if searching by id" do
66
66
  expect {model.find_all_by(id: instance.id)}.to raise_error(ArgumentError)
67
67
  end
68
68
 
69
- it "will return [] if none can be found" do
70
- expect(model.find_all_by zombies: 'yes plz').to eq []
69
+ it "will return an exeception if none can be found" do
70
+ expect { model.find_all_by zombies: 'yes plz'}.to raise_error(ArgumentError)
71
71
  end
72
72
 
73
73
  it "will raise an exception if none can be found with a !" do
74
- expect { model.find_all_by! brains: :none }.to raise_error(Rrod::Model::NotFound)
74
+ expect { model.find_all_by! brains: :none }.to raise_error(ArgumentError)
75
75
  end
76
76
  end
77
77
  end
78
+
78
79
  end
@@ -20,7 +20,7 @@ describe Rrod::Model::Schema do
20
20
  end
21
21
 
22
22
  it "is using a schema if an attribute is declared" do
23
- expect(Person.schema?).to be_true
23
+ expect(Person.schema?).to be_truthy
24
24
  end
25
25
 
26
26
  describe "associations", integration: true do
@@ -54,6 +54,24 @@ describe Rrod::Model::Schema do
54
54
  expect(hash['address']).to eq 'street' => '123 Fancy Pants Lane'
55
55
  end
56
56
 
57
+ it "will set the parent on a nested model when assigned" do
58
+ instance.address = Address.new
59
+ expect(instance.address._parent).to eq(instance)
60
+ end
61
+
62
+ it "will alias the parent with method given to nested_in" do
63
+ instance.pets = [Pet.new]
64
+ expect(instance.pets.first.owner).to eq(instance)
65
+ end
66
+
67
+ end
68
+
69
+ it "raises an UncastableObjectError if object is not castable" do
70
+ expect { Pet.rrod_cast(Object.new) }.to raise_error(Rrod::Model::UncastableObjectError)
71
+ end
72
+
73
+ it "will not add ids to models instantiated via `rrod_cast`" do
74
+ expect(Pet.rrod_cast(name: 'Molle').attributes).to_not have_key('id')
57
75
  end
58
76
 
59
77
  end
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+
3
+ describe Rrod::Model::Timestamps do
4
+ let(:klass) { Class.new {include Rrod::Model; timestamps!} }
5
+ let(:instance) { klass.new }
6
+ let(:attribute) { Rrod::Model::Attribute }
7
+ let(:now) { Time.now }
8
+
9
+ it "provides a created_at attribute" do
10
+ expect(klass.attributes[:created_at]).to be_an attribute
11
+ end
12
+
13
+ it "provides a updated_at attribute" do
14
+ expect(klass.attributes[:updated_at]).to be_an attribute
15
+ end
16
+
17
+ describe "Time.now" do
18
+ before(:each) { Time.stub(:now).and_return(now) }
19
+
20
+ it "sets created_at upon instantiation" do
21
+ expect(instance.created_at).to eq now
22
+ end
23
+
24
+ it "sets updated_at when the object is updated" do
25
+ instance.stub(:persist).and_return(true)
26
+ expect(instance).to receive(:updated_at=).with(now)
27
+ instance.save
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,46 @@
1
+ require 'spec_helper'
2
+ require 'support/models/player'
3
+ require 'support/models/team'
4
+
5
+ describe Rrod::Model::Validations::AssociatedValidator do
6
+ context 'for a many association' do
7
+ # this is the same as a one association..
8
+ let(:team) { Team.new }
9
+ let(:ichiro) { Player.new(name: 'Ichiro', position: 'RF') }
10
+ let(:arod) { Player.new(position: '3B') }
11
+ let(:joeschmo) { Player.new }
12
+
13
+ before(:each) do
14
+ team.players = [ichiro, arod, joeschmo]
15
+ end
16
+
17
+ it 'is invalid when the associated records are invalid' do
18
+ expect(arod.valid?).to be false
19
+ expect(joeschmo.valid?).to be false
20
+ expect(team.valid?).to be false
21
+ end
22
+
23
+ it 'includes the associated records validation error messages in the error message' do
24
+ team.valid?
25
+ expect(team.errors[:players].size).to be 1
26
+ # maybe change this to better error messages
27
+ error_message = team.errors[:players].first
28
+ expect(error_message).to eq "Name can't be blank; Name can't be blank and Position can't be blank"
29
+ end
30
+
31
+
32
+ it 'is valid when the associated records are valid' do
33
+ expect(ichiro.valid?).to be true
34
+
35
+ arod.name = 'Alex Rodriguez'
36
+ expect(arod.valid?).to be true
37
+
38
+ joeschmo.name = 'Joe Schmo'
39
+ joeschmo.position = 'C'
40
+ expect(joeschmo.valid?).to be true
41
+
42
+ expect(team.valid?).to be true
43
+ end
44
+
45
+ end
46
+ end
@@ -1,50 +1,48 @@
1
1
  require 'spec_helper'
2
2
  require 'support/models/person'
3
3
 
4
- describe Rrod::Model do
5
- describe "validations" do
6
- let(:klass) { Person }
7
- let(:validators) { klass.validators }
8
- let(:instance) { klass.new }
9
-
10
- it "adds validations as class methods" do
11
- expect(validators.any? { |v| ActiveModel::Validations::LengthValidator === v }).to be_true
12
- end
13
-
14
- it "allows describing validations in the attribute" do
15
- expect(validators.any? { |v| ActiveModel::Validations::PresenceValidator === v }).to be_true
16
- end
17
-
18
- it "raises a no method error if no validation exists" do
19
- expect {
20
- Class.new {
21
- include Rrod::Model
22
- attribute :kittens, Integer, bunnies: true
23
- }
24
- }.to raise_error(ArgumentError, /BunniesValidator/)
25
- end
26
-
27
-
28
- it "can determine an instances validity" do
29
- expect(instance).not_to be_valid
30
- end
31
-
32
- it "will not save if invalid" do
33
- expect(instance).not_to receive(:persist)
34
- instance.save
35
- end
36
-
37
- it "will return false from saving if invalid" do
38
- expect(instance.save).to be_false
39
- end
40
-
41
- it "sets up errors properlies" do
42
- instance.valid?
43
- expect(instance.errors).to be_present
44
- end
45
-
46
- it "will allow saving when not validating" do
47
- expect(instance.save(validate: false)).to be_true
48
- end
4
+ describe Rrod::Model::Validations do
5
+ let(:klass) { Person }
6
+ let(:validators) { klass.validators }
7
+ let(:instance) { klass.new }
8
+
9
+ it "adds validations as class methods" do
10
+ expect(validators.any? { |v| ActiveModel::Validations::LengthValidator === v }).to be_truthy
11
+ end
12
+
13
+ it "allows describing validations in the attribute" do
14
+ expect(validators.any? { |v| ActiveModel::Validations::PresenceValidator === v }).to be_truthy
15
+ end
16
+
17
+ it "raises a no method error if no validation exists" do
18
+ expect {
19
+ Class.new {
20
+ include Rrod::Model
21
+ attribute :kittens, Integer, bunnies: true
22
+ }
23
+ }.to raise_error(ArgumentError, /BunniesValidator/)
24
+ end
25
+
26
+
27
+ it "can determine an instances validity" do
28
+ expect(instance).not_to be_valid
29
+ end
30
+
31
+ it "will not save if invalid" do
32
+ expect(instance).not_to receive(:persist)
33
+ instance.save
34
+ end
35
+
36
+ it "will return false from saving if invalid" do
37
+ expect(instance.save).to be_falsey
38
+ end
39
+
40
+ it "sets up errors properlies" do
41
+ instance.valid?
42
+ expect(instance.errors).to be_present
43
+ end
44
+
45
+ it "will allow saving when not validating" do
46
+ expect(instance.save(validate: false)).to be_truthy
49
47
  end
50
48
  end