ricordami 0.0.3 → 0.0.4

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.
data/CHANGELOG.md CHANGED
@@ -1,16 +1,22 @@
1
1
  # Changelog #
2
2
 
3
- ## 0.0.3 (March 12th, 2011)
3
+ ## 0.0.4 (March 13th, 2011) ##
4
+
5
+ - add a scope option to validates_uniqueness_of validation macro
6
+ - allow to pass nil when creating a new instance
7
+ - added a spec for update_attributes with an invalid model
8
+
9
+ ## 0.0.3 (March 12th, 2011) ##
4
10
 
5
11
  - added serialization (use: "model\_can :be_serialized") to have
6
12
  access to Model#to_json and Model#to_xml
7
13
 
8
- ## 0.0.2 (March 5th, 2011)
14
+ ## 0.0.2 (March 5th, 2011) ##
9
15
 
10
16
  - added examples and fixed README
11
17
  - created a basic Ricordami::Model#to_s method
12
18
 
13
- ## 0.0.1 (March 5th, 2011)
19
+ ## 0.0.1 (March 5th, 2011) ##
14
20
 
15
21
  Initial release.
16
22
 
@@ -36,6 +36,7 @@ module Ricordami
36
36
  attr_reader :attributes
37
37
 
38
38
  def initialize(attrs = {})
39
+ attrs = {} if attrs.nil?
39
40
  @attributes = {}.with_indifferent_access
40
41
  @reloading = false
41
42
  update_mem_attributes(attrs) unless attrs.empty?
@@ -12,8 +12,7 @@ module Ricordami
12
12
  end
13
13
 
14
14
  def index(options = {})
15
- # for now we can only create unique indices
16
- options.assert_valid_keys(:unique, :get_by, :value)
15
+ options.assert_valid_keys(:unique, :get_by, :value, :scope)
17
16
  fields = options.delete(:unique)
18
17
  return unique_index(fields, options) if fields.present?
19
18
  field = options.delete(:value)
@@ -4,13 +4,17 @@ module Ricordami
4
4
  class UniqueIndex
5
5
  SEPARATOR = "_-::-_"
6
6
 
7
- attr_reader :model, :fields, :name, :need_get_by
7
+ attr_reader :model, :fields, :name, :need_get_by, :scope
8
8
 
9
9
  def initialize(model, fields, options = {})
10
10
  @model = model
11
- @fields = [fields].flatten.map(&:to_sym)
12
- @need_get_by = options[:get_by] && @fields != [:id]
11
+ @fields = normalize_array(fields)
13
12
  @name = @fields.join("_").to_sym
13
+ @need_get_by = options[:get_by] && @fields != [:id]
14
+ if options.has_key?(:scope)
15
+ @scope = normalize_array(options[:scope])
16
+ @fields.push(*@scope)
17
+ end
14
18
  end
15
19
 
16
20
  def uidx_key_name
@@ -22,7 +26,7 @@ module Ricordami
22
26
  end
23
27
 
24
28
  def add(id, value)
25
- value = value.join(SEPARATOR) if value.is_a?(Array)
29
+ value = normalize_value(value)
26
30
  @model.redis.sadd(uidx_key_name, value)
27
31
  @model.redis.hset(ref_key_name, value, id) if @need_get_by
28
32
  end
@@ -35,7 +39,7 @@ module Ricordami
35
39
  return commands
36
40
  end
37
41
  @model.redis.hdel(ref_key_name, id) if @need_get_by
38
- value = value.join(SEPARATOR) if value.is_a?(Array)
42
+ value = normalize_value(value)
39
43
  @model.redis.srem(uidx_key_name, value)
40
44
  end
41
45
 
@@ -55,5 +59,16 @@ module Ricordami
55
59
  def include?(value)
56
60
  @model.redis.sismember(uidx_key_name, value)
57
61
  end
62
+
63
+ def normalize_value(value)
64
+ value.is_a?(Array) ? value.join(SEPARATOR) : value
65
+ end
66
+
67
+ private
68
+
69
+ def normalize_array(array)
70
+ return [] if array.nil?
71
+ [array].flatten.map(&:to_sym)
72
+ end
58
73
  end
59
74
  end
@@ -6,6 +6,10 @@ module Ricordami
6
6
  return true unless record.new_record? || record.send(:attribute_changed?, attribute)
7
7
  index_name = attribute.to_sym
8
8
  index = record.class.indices[index_name]
9
+ if index.scope
10
+ scope_values = index.scope.map { |field| record.send(field) }
11
+ value = index.normalize_value([value].push(*scope_values))
12
+ end
9
13
  if index.include?(value)
10
14
  attr_def = record.class.attributes[attribute]
11
15
  unless record.persisted? && attr_def.read_only?
@@ -15,7 +19,7 @@ module Ricordami
15
19
  end
16
20
 
17
21
  def setup(klass)
18
- attributes.each { |attribute| klass.index :unique => attribute }
22
+ attributes.each { |attribute| klass.index :unique => attribute, :scope => options[:scope] }
19
23
  end
20
24
  end
21
25
  end
@@ -1,3 +1,3 @@
1
1
  module Ricordami
2
- VERSION = "0.0.3"
2
+ VERSION = "0.0.4"
3
3
  end
@@ -50,7 +50,7 @@ describe Ricordami::CanBeValidated do
50
50
  end
51
51
  end
52
52
 
53
- describe "#save" do
53
+ describe "#save and #update_attributes" do
54
54
  uses_constants("Post")
55
55
 
56
56
  before(:each) do
@@ -69,6 +69,11 @@ describe Ricordami::CanBeValidated do
69
69
  it "can save if it is not valid but passed :validate => false" do
70
70
  Post.new.save(:validate => false).should be_true
71
71
  end
72
+
73
+ it "can't #update_attributes if it is not valid" do
74
+ post = Post.create(:title => "blah")
75
+ post.update_attributes(:title => "").should be_false
76
+ end
72
77
  end
73
78
 
74
79
  describe "validate attribute uniqueness" do
@@ -111,5 +116,13 @@ describe Ricordami::CanBeValidated do
111
116
  fred.should have(1).error
112
117
  fred.errors[:wife].should == ["is already used"]
113
118
  end
119
+
120
+ it "allows to validate the uniqueness using a scope" do
121
+ User.validates_uniqueness_of(:wife, :scope => :username)
122
+ serge = User.create(:id => "serge", :username => "Gainsbourg", :wife => "Rita")
123
+ fred = User.new(:username => "Chichin", :wife => "Rita")
124
+ fred.should be_valid
125
+ fred.should have(0).errors
126
+ end
114
127
  end
115
128
  end
@@ -38,6 +38,12 @@ describe Ricordami::HasAttributes do
38
38
  attribute.initial_value.should == "2"
39
39
  attribute.initial_value.should == "3"
40
40
  end
41
+
42
+ it "can create a new instance without parameters, with nil or an empty hash" do
43
+ Boat.new.attributes.should == {"id" => nil}
44
+ Boat.new(nil).attributes.should == {"id" => nil}
45
+ Boat.new({}).attributes.should == {"id" => nil}
46
+ end
41
47
  end
42
48
 
43
49
  describe "an instance" do
@@ -27,6 +27,13 @@ describe Ricordami::HasIndices do
27
27
  Car.indices[:model].should == index
28
28
  end
29
29
 
30
+ it "can give a scope to a unique index with :scope option" do
31
+ Car.attribute :brand
32
+ index = Car.index :unique => :model, :scope => :brand
33
+ Car.indices[:model].scope.should == [:brand]
34
+ Car.indices[:model].fields.should == [:model, :brand]
35
+ end
36
+
30
37
  it "discards the subsequent declarations if the same index is created more than once" do
31
38
  Car.index :unique => :model, :get_by => true
32
39
  Car.indices[:model].need_get_by.should be_true
@@ -28,9 +28,6 @@ describe Ricordami::Model do
28
28
  user.to_model.should == user
29
29
  end
30
30
 
31
- it "has a simple to_s method" do
32
- end
33
-
34
31
  describe "#to_key" do
35
32
  before(:each) do
36
33
  @user = User.new
@@ -6,6 +6,7 @@ describe Ricordami::UniqueIndex do
6
6
  before(:each) do
7
7
  create_constant("DataSource")
8
8
  DataSource.attribute :name
9
+ DataSource.attribute :owner
9
10
  @index = subject.new(DataSource, :id)
10
11
  end
11
12
 
@@ -15,6 +16,12 @@ describe Ricordami::UniqueIndex do
15
16
  @index.name.should == :id
16
17
  end
17
18
 
19
+ it "can have a scope" do
20
+ index = subject.new(DataSource, :id, :scope => :owner)
21
+ index.scope.should == [:owner]
22
+ index.fields.should == [:id, :owner]
23
+ end
24
+
18
25
  it "returns its internal index name with #uidx_key_name" do
19
26
  @index.uidx_key_name.should == "DataSource:udx:id"
20
27
  end
@@ -38,4 +38,18 @@ describe Ricordami::UniqueValidator do
38
38
  validator.validate_each(sophie, :name, record.name)
39
39
  sophie.errors[:name].should == ["come on, man!"]
40
40
  end
41
+
42
+ it "accepts a scope option to limit the unicity constraint" do
43
+ Call.attribute :family
44
+ validator = Ricordami::UniqueValidator.new(:attributes => [:name], :scope => :family)
45
+ validator.setup(Call)
46
+ Call.create(:name => "john", :family => "jones")
47
+ john = Call.new(:name => "john", :family => "jones")
48
+ validator.validate_each(john, :name, john.name)
49
+ john.should have(1).error
50
+ john.family = "doe"
51
+ john.errors.clear
52
+ validator.validate_each(john, :name, john.name)
53
+ john.should have(0).errors
54
+ end
41
55
  end
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: ricordami
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.0.3
5
+ version: 0.0.4
6
6
  platform: ruby
7
7
  authors:
8
8
  - Mathieu Lajugie
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2011-03-12 00:00:00 -08:00
13
+ date: 2011-03-12 23:00:00 -08:00
14
14
  default_executable:
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency