tarte 0.1.2
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/.document +5 -0
- data/.gitignore +22 -0
- data/LICENSE +20 -0
- data/README.rdoc +17 -0
- data/Rakefile +45 -0
- data/VERSION +1 -0
- data/init.rb +2 -0
- data/lib/tarte.rb +16 -0
- data/lib/tarte/baked_in_associations.rb +179 -0
- data/lib/tarte/baked_in_validation_helpers.rb +68 -0
- data/spec/debug.log +1 -0
- data/spec/schema.rb +10 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +36 -0
- data/spec/tarte_spec.rb +162 -0
- data/tarte.gemspec +61 -0
- metadata +93 -0
data/.document
ADDED
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Leandro Pedroni
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
= tarte
|
2
|
+
|
3
|
+
Description goes here.
|
4
|
+
|
5
|
+
== Note on Patches/Pull Requests
|
6
|
+
|
7
|
+
* Fork the project.
|
8
|
+
* Make your feature addition or bug fix.
|
9
|
+
* Add tests for it. This is important so I don't break it in a
|
10
|
+
future version unintentionally.
|
11
|
+
* Commit, do not mess with rakefile, version, or history.
|
12
|
+
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
13
|
+
* Send me a pull request. Bonus points for topic branches.
|
14
|
+
|
15
|
+
== Copyright
|
16
|
+
|
17
|
+
Copyright (c) 2009 Leandro Pedroni. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "tarte"
|
8
|
+
gem.summary = %Q{Baked in ActiveRecord Associations}
|
9
|
+
gem.description = %Q{Provides a lot of helper methods and uses integer indexes or bitmasks to store in a record an association to a predetermined set of symbols.}
|
10
|
+
gem.email = "ilpoldo@gmail.com"
|
11
|
+
gem.homepage = "http://github.com/ilpoldo/tarte"
|
12
|
+
gem.authors = ["Leandro Pedroni"]
|
13
|
+
gem.add_development_dependency "rspec", ">= 1.2.9"
|
14
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
15
|
+
end
|
16
|
+
Jeweler::GemcutterTasks.new
|
17
|
+
rescue LoadError
|
18
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
19
|
+
end
|
20
|
+
|
21
|
+
require 'spec/rake/spectask'
|
22
|
+
Spec::Rake::SpecTask.new(:spec) do |spec|
|
23
|
+
spec.libs << 'lib' << 'spec'
|
24
|
+
spec.spec_files = FileList['spec/**/*_spec.rb']
|
25
|
+
end
|
26
|
+
|
27
|
+
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
28
|
+
spec.libs << 'lib' << 'spec'
|
29
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
30
|
+
spec.rcov = true
|
31
|
+
end
|
32
|
+
|
33
|
+
task :spec => :check_dependencies
|
34
|
+
|
35
|
+
task :default => :spec
|
36
|
+
|
37
|
+
require 'rake/rdoctask'
|
38
|
+
Rake::RDocTask.new do |rdoc|
|
39
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
40
|
+
|
41
|
+
rdoc.rdoc_dir = 'rdoc'
|
42
|
+
rdoc.title = "tarte #{version}"
|
43
|
+
rdoc.rdoc_files.include('README*')
|
44
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
45
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.2
|
data/init.rb
ADDED
data/lib/tarte.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'tarte/baked_in_associations'
|
2
|
+
require 'tarte/baked_in_validation_helpers'
|
3
|
+
|
4
|
+
module Tarte
|
5
|
+
def self.included(base)
|
6
|
+
base.extend BakedInValidationHelpers
|
7
|
+
base.extend BakedInAssociations
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
ActiveRecord::Base.send(:include, Tarte)
|
12
|
+
|
13
|
+
# LEDO: Should I require haml? Where: gemspec, loading the plugin or the view helper?
|
14
|
+
module Tarte
|
15
|
+
VERSION = File.exist?('VERSION') ? File.read('VERSION') : ""
|
16
|
+
end
|
@@ -0,0 +1,179 @@
|
|
1
|
+
module Tarte
|
2
|
+
|
3
|
+
module Errors
|
4
|
+
class Base < Exception; end
|
5
|
+
class NotValidAssociationMask < Errors::Base; end
|
6
|
+
class NotValidValidationOptions < Errors::Base; end
|
7
|
+
end
|
8
|
+
|
9
|
+
module BakedInAssociations
|
10
|
+
|
11
|
+
def has_one_baked_in(association_name, methods = nil)
|
12
|
+
names_constant = association_name.to_s.pluralize.upcase
|
13
|
+
|
14
|
+
write_inheritable_array(:has_one_baked_in_attributes, [association_name])
|
15
|
+
|
16
|
+
class_eval <<-EOV
|
17
|
+
#{names_constant} = #{methods[:names].inspect}
|
18
|
+
|
19
|
+
def #{association_name}=(value)
|
20
|
+
if value
|
21
|
+
self.#{association_name}_code = #{names_constant}.index(value)
|
22
|
+
else
|
23
|
+
self.#{association_name}_code = nil
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def #{association_name}
|
28
|
+
if code = #{association_name}_code
|
29
|
+
#{names_constant}[code]
|
30
|
+
else
|
31
|
+
nil
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def #{association_name}_was
|
36
|
+
#{names_constant}[self.#{association_name}_code_was]
|
37
|
+
end
|
38
|
+
|
39
|
+
def #{association_name}_changed?
|
40
|
+
self.#{association_name}_code_changed?
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.#{association_name}_codes_for(names)
|
44
|
+
if names.class == Array
|
45
|
+
names.map{|n| #{names_constant}.index(n)}
|
46
|
+
else
|
47
|
+
#{names_constant}_GROUPS_CODES[names]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
EOV
|
51
|
+
|
52
|
+
methods[:names].each_with_index do |value, code|
|
53
|
+
class_eval <<-EOV
|
54
|
+
def #{value}
|
55
|
+
self.#{association_name}_code = #{code}
|
56
|
+
self
|
57
|
+
end
|
58
|
+
|
59
|
+
def #{value}?
|
60
|
+
self.#{association_name}_code == #{code}
|
61
|
+
end
|
62
|
+
|
63
|
+
def #{value}!
|
64
|
+
update_attribute(:#{association_name}_code, #{code})
|
65
|
+
end
|
66
|
+
|
67
|
+
def #{association_name}_changed_to_#{value}?
|
68
|
+
self.#{association_name}_code_changed? && self.#{association_name}_code == #{code}
|
69
|
+
end
|
70
|
+
EOV
|
71
|
+
end
|
72
|
+
|
73
|
+
if methods[:groups]
|
74
|
+
hash_with_codes = methods[:groups].merge(methods[:groups]) do |group, member_names|
|
75
|
+
member_names.map{|n| methods[:names].index(n)}
|
76
|
+
end
|
77
|
+
class_eval <<-EOV
|
78
|
+
#{names_constant}_GROUPS_CODES = #{hash_with_codes.inspect}
|
79
|
+
|
80
|
+
def self.#{association_name}_condition(condition)
|
81
|
+
{:#{association_name}_code => #{names_constant}_GROUPS_CODES[condition]}
|
82
|
+
end
|
83
|
+
EOV
|
84
|
+
methods[:groups].each_key do |group|
|
85
|
+
send(:named_scope, "is_#{group}", :conditions => {"#{association_name}_code" => hash_with_codes[group]})
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
|
91
|
+
def has_many_baked_in(association_name, methods = nil)
|
92
|
+
names_constant = association_name.to_s.upcase
|
93
|
+
methods[:verb] ||= :has
|
94
|
+
|
95
|
+
write_inheritable_hash(:has_many_baked_in_attributes, association_name => methods[:verb])
|
96
|
+
|
97
|
+
class_eval <<-EOV
|
98
|
+
#{names_constant} = #{methods[:names].inspect}
|
99
|
+
|
100
|
+
def #{association_name}=(values)
|
101
|
+
return nil unless values
|
102
|
+
|
103
|
+
if values.class == Array
|
104
|
+
new_mask = (values & #{names_constant}).map { |v| 2**#{names_constant}.index(v) }.sum
|
105
|
+
else values
|
106
|
+
new_mask = #{names_constant}_GROUPS_MASKS[values]
|
107
|
+
end
|
108
|
+
raise(Tarte::Errors::NotValidAssociationMask) if new_mask >= #{2**methods[:names].size}
|
109
|
+
|
110
|
+
self.#{association_name}_mask = new_mask
|
111
|
+
end
|
112
|
+
|
113
|
+
def #{association_name}
|
114
|
+
#{names_constant}.reject { |v| ((self.#{association_name}_mask || 0) & 2**#{names_constant}.index(v)).zero? }
|
115
|
+
end
|
116
|
+
|
117
|
+
def #{association_name}_were
|
118
|
+
#{names_constant}.reject { |v| ((self.#{association_name}_mask_was || 0) & 2**#{names_constant}.index(v)).zero? }
|
119
|
+
end
|
120
|
+
|
121
|
+
def #{association_name}_changed?
|
122
|
+
self.#{association_name}_mask_changed?
|
123
|
+
end
|
124
|
+
|
125
|
+
def self.#{association_name}_mask_for(names)
|
126
|
+
if names.class == Array
|
127
|
+
new_mask = (names & #{names_constant}).map { |v| 2**#{names_constant}.index(v.to_sym) }.sum
|
128
|
+
raise(Tarte::Errors::NotValidAssociationMask) if new_mask >= #{2**methods[:names].size}
|
129
|
+
new_mask
|
130
|
+
else
|
131
|
+
#{names_constant}_GROUPS_MASKS[names]
|
132
|
+
end
|
133
|
+
|
134
|
+
end
|
135
|
+
EOV
|
136
|
+
|
137
|
+
methods[:names].each_with_index do |value, index|
|
138
|
+
class_eval <<-EOV
|
139
|
+
def #{methods[:verb]}_#{value}?
|
140
|
+
self.#{association_name}_mask & #{2**index} > 0
|
141
|
+
end
|
142
|
+
EOV
|
143
|
+
end
|
144
|
+
|
145
|
+
# Converts arrays of options into masks.
|
146
|
+
if methods[:groups]
|
147
|
+
hash_with_masks = methods[:groups].merge(methods[:groups]) do |group, member_names|
|
148
|
+
(member_names & methods[:names]).map { |m| 2**methods[:names].index(m.to_sym) }.sum
|
149
|
+
end
|
150
|
+
|
151
|
+
class_eval <<-EOV
|
152
|
+
#{names_constant}_GROUPS_MASKS = #{hash_with_masks.inspect}
|
153
|
+
|
154
|
+
# Use this method to build scopes
|
155
|
+
def self.#{association_name}_condition(condition)
|
156
|
+
{:#{association_name}_mask => #{names_constant}_GROUPS_MASKS[condition]}
|
157
|
+
end
|
158
|
+
|
159
|
+
def #{association_name}_matches(group)
|
160
|
+
self.#{association_name}_mask == #{names_constant}_GROUPS_MASKS[group]
|
161
|
+
end
|
162
|
+
|
163
|
+
EOV
|
164
|
+
|
165
|
+
methods[:groups].each_key do |group|
|
166
|
+
send(:named_scope, "matching_#{group}", :conditions => {"#{association_name}_mask".to_sym => hash_with_masks[group]})
|
167
|
+
send(:named_scope, "with_#{group}", :conditions => ["#{association_name}_mask & ? > 0", hash_with_masks[group]])
|
168
|
+
|
169
|
+
class_eval <<-EOV
|
170
|
+
def #{association_name}_matches_#{group}?
|
171
|
+
self.#{association_name}_mask == #{names_constant}_GROUPS_MASKS[:#{group}]
|
172
|
+
end
|
173
|
+
EOV
|
174
|
+
end
|
175
|
+
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module Tarte
|
2
|
+
module BakedInValidationHelpers
|
3
|
+
def validates_baked_in (*attr_names)
|
4
|
+
configuration = { :on => :save, :message => :invalid }
|
5
|
+
configuration.update(attr_names.extract_options!)
|
6
|
+
|
7
|
+
has_one_list = read_inheritable_attribute(:has_one_baked_in_attributes) || []
|
8
|
+
codes_or_masks = attr_names.partition {|attr_name| has_one_list.include?(attr_name)}
|
9
|
+
validates_code_of(codes_or_masks[0], configuration) unless codes_or_masks[0].empty?
|
10
|
+
validates_mask_of(codes_or_masks[1], configuration) unless codes_or_masks[1].empty?
|
11
|
+
end
|
12
|
+
|
13
|
+
def validates_code_of(attr_names, configuration)
|
14
|
+
enum = configuration[:is] || configuration[:is_not]
|
15
|
+
validates_each(attr_names, configuration) do |record, attr_name, value|
|
16
|
+
value_code = record.send("#{attr_name}_code")
|
17
|
+
if value_code
|
18
|
+
if configuration[:is]
|
19
|
+
unless self.send("#{attr_name}_codes_for", enum).include? value_code
|
20
|
+
record.errors.add(attr_name, configuration[:message], :value => value)
|
21
|
+
end
|
22
|
+
elsif configuration[:is_not]
|
23
|
+
if self.send("#{attr_name}_codes_for", enum).include? value_code
|
24
|
+
record.errors.add(attr_name, configuration[:message], :value => value)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
else
|
28
|
+
record.errors.add(attr_name, (configuration[:message])) unless configuration[:allow_nil]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def validates_mask_of(attr_names, configuration)
|
34
|
+
has_many_list = read_inheritable_attribute(:has_many_baked_in_attributes)||{}
|
35
|
+
attr_names.each do |attribute|
|
36
|
+
raise(ArgumentError, "#{attribute} is not a baked in association") unless has_many_list.has_key?(attribute)
|
37
|
+
end
|
38
|
+
|
39
|
+
validates_each(attr_names, configuration) do |record, attr_name, value|
|
40
|
+
verb = has_many_list[attr_name]
|
41
|
+
enum = configuration[verb] || configuration["#{verb}_not".to_sym]
|
42
|
+
eql = configuration[:matches] || configuration[:does_not_match]
|
43
|
+
if value_mask = record.send("#{attr_name}_mask")
|
44
|
+
if configuration[:matches]
|
45
|
+
unless self.send("#{attr_name}_mask_for", eql) == value_mask
|
46
|
+
record.errors.add(attr_name, configuration[:message], :value => value)
|
47
|
+
end
|
48
|
+
elsif configuration[:does_not_match]
|
49
|
+
if self.send("#{attr_name}_mask_for", eql) == value_mask
|
50
|
+
record.errors.add(attr_name, configuration[:message], :value => value)
|
51
|
+
end
|
52
|
+
elsif configuration[verb]
|
53
|
+
unless self.send("#{attr_name}_mask_for", enum) & value_mask > 0
|
54
|
+
record.errors.add(attr_name, configuration[:message], :value => value)
|
55
|
+
end
|
56
|
+
elsif configuration["#{verb}_not".to_sym]
|
57
|
+
if send("#{attr_name}_mask_for", enum) & value_mask > 0
|
58
|
+
self.record.errors.add(attr_name, configuration[:message], :value => value)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
else
|
62
|
+
record.errors.add(attr_name, configuration[:message]) unless configuration[:allow_nil]
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
end
|
data/spec/debug.log
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
# Logfile created on Wed Dec 09 11:02:39 +0000 2009 by /
|
data/spec/schema.rb
ADDED
data/spec/spec.opts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'active_support'
|
3
|
+
require 'active_record'
|
4
|
+
|
5
|
+
TEST_DATABASE_FILE = File.join(File.dirname(__FILE__), '..', 'test.sqlite3')
|
6
|
+
|
7
|
+
File.unlink(TEST_DATABASE_FILE) if File.exist?(TEST_DATABASE_FILE)
|
8
|
+
|
9
|
+
ActiveRecord::Base.establish_connection(
|
10
|
+
"adapter" => "sqlite3", "database" => TEST_DATABASE_FILE
|
11
|
+
)
|
12
|
+
|
13
|
+
# RAILS_DEFAULT_LOGGER = Logger.new(File.join(File.dirname(__FILE__), "debug.log"))
|
14
|
+
|
15
|
+
load(File.dirname(__FILE__) + '/schema.rb')
|
16
|
+
|
17
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
18
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
19
|
+
|
20
|
+
#Mock ActiveRecord
|
21
|
+
# module ActiveRecord
|
22
|
+
# class Base
|
23
|
+
# end
|
24
|
+
# end
|
25
|
+
|
26
|
+
require 'tarte'
|
27
|
+
require 'spec'
|
28
|
+
require 'spec/autorun'
|
29
|
+
|
30
|
+
# Requires supporting files with custom matchers and macros, etc,
|
31
|
+
# in ./support/ and its subdirectories.
|
32
|
+
Dir[File.expand_path(File.join(File.dirname(__FILE__),'support','**','*.rb'))].each {|f| require f}
|
33
|
+
|
34
|
+
Spec::Runner.configure do |config|
|
35
|
+
|
36
|
+
end
|
data/spec/tarte_spec.rb
ADDED
@@ -0,0 +1,162 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
class HasOneStatus < ActiveRecord::Base
|
4
|
+
has_one_baked_in :status, :names => [:red, :yellow, :green], :groups => {:stop => [:red, :yellow]}
|
5
|
+
|
6
|
+
validates_baked_in :status, :is => :stop
|
7
|
+
end
|
8
|
+
|
9
|
+
class HasManyIngredients < ActiveRecord::Base
|
10
|
+
has_many_baked_in :ingredients, :names => [:fish, :chips, :sauce, :pudding], :groups => {:fish_and_chips => [:fish, :chips]}
|
11
|
+
|
12
|
+
validates_baked_in :ingredients, :matches => :fish_and_chips, :message => "I only like fish and chips"
|
13
|
+
end
|
14
|
+
|
15
|
+
describe Tarte, "accessors" do
|
16
|
+
it "should have accessors for an has_many_baked_in associations that hit an association_mask" do
|
17
|
+
entry = HasOneStatus.new
|
18
|
+
|
19
|
+
entry.status = :green
|
20
|
+
entry.status.should eql(:green)
|
21
|
+
|
22
|
+
entry.should_receive(:status_code).and_return(0)
|
23
|
+
entry.status.should eql(:red)
|
24
|
+
|
25
|
+
entry.should_receive(:status_code=).with(2)
|
26
|
+
entry.status = :green
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should have accessors for an has_one_baked_in association that hit an association_code" do
|
30
|
+
entry = HasManyIngredients.new
|
31
|
+
|
32
|
+
entry.ingredients = [:fish, :pudding]
|
33
|
+
entry.ingredients.should eql([:fish, :pudding])
|
34
|
+
|
35
|
+
entry.should_receive(:ingredients_mask).any_number_of_times.and_return(5)
|
36
|
+
entry.ingredients.should eql([:fish, :sauce])
|
37
|
+
|
38
|
+
entry.should_receive(:ingredients_mask=).with(3)
|
39
|
+
entry.ingredients = [:fish, :chips]
|
40
|
+
end
|
41
|
+
|
42
|
+
it "has an accessor for the has_many that supports groups" do
|
43
|
+
entry = HasManyIngredients.new
|
44
|
+
|
45
|
+
entry.ingredients = :fish_and_chips
|
46
|
+
entry.ingredients.should eql([:fish, :chips])
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe Tarte, "query methods" do
|
52
|
+
it "should have query methods for an has_many_baked_in associations that hit an association_mask" do
|
53
|
+
entry = HasOneStatus.new
|
54
|
+
|
55
|
+
entry.status = :green
|
56
|
+
entry.green?.should be_true
|
57
|
+
entry.red?.should be_false
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should have query methods for an has_one_baked_in association that hit an association_code" do
|
61
|
+
entry = HasManyIngredients.new
|
62
|
+
|
63
|
+
entry.ingredients = [:fish, :chips]
|
64
|
+
entry.has_fish?.should be_true
|
65
|
+
entry.has_sauce?.should be_false
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe Tarte, "dirty associations" do
|
70
|
+
it "should track dirty has_many_baked_in with names" do
|
71
|
+
entry = HasOneStatus.new
|
72
|
+
|
73
|
+
entry.should_receive(:status_code_changed?).and_return(true)
|
74
|
+
entry.should_receive(:status_code_was).and_return(1)
|
75
|
+
|
76
|
+
entry.status_changed?
|
77
|
+
entry.status_was.should eql(:yellow)
|
78
|
+
|
79
|
+
entry.should_receive(:status_code_was).and_return(1)
|
80
|
+
entry.status_was.should eql(:yellow)
|
81
|
+
end
|
82
|
+
|
83
|
+
it "should track dirty has_many_baked_in with names" do
|
84
|
+
entry = HasOneStatus.new
|
85
|
+
|
86
|
+
entry.should_receive(:status_code_changed?).and_return(true)
|
87
|
+
entry.should_receive(:status_code).and_return(0)
|
88
|
+
entry.status_changed_to_red?.should be_true
|
89
|
+
end
|
90
|
+
|
91
|
+
it "should track dirty has_one_baked_in" do
|
92
|
+
entry = HasManyIngredients.new
|
93
|
+
|
94
|
+
entry.should_receive(:ingredients_mask_changed?).and_return(true)
|
95
|
+
entry.should_receive(:ingredients_mask_was).any_number_of_times.and_return(3) # [:fish, :chips]
|
96
|
+
|
97
|
+
entry.ingredients_changed?
|
98
|
+
entry.ingredients_were.should eql([:fish, :chips])
|
99
|
+
|
100
|
+
# entry.had_fish?.should be_true
|
101
|
+
# entry.has_sauce?.should be_false
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
describe Tarte, "grouping and group querying" do
|
106
|
+
it "should return an array of codes for a group of has_one values for an attribute" do
|
107
|
+
HasOneStatus.status_codes_for(:stop).should eql([0,1])
|
108
|
+
end
|
109
|
+
|
110
|
+
it "should return mask for a group of has_many values for an attribute" do
|
111
|
+
HasManyIngredients.ingredients_mask_for(:fish_and_chips).should eql(3)
|
112
|
+
end
|
113
|
+
|
114
|
+
it "should query if the attribute code matches a group" do
|
115
|
+
pending
|
116
|
+
end
|
117
|
+
|
118
|
+
it "should query if the attribute mask matches a mask" do
|
119
|
+
pending
|
120
|
+
end
|
121
|
+
|
122
|
+
it "should query if the attribute mask shares an element with a mask" do
|
123
|
+
pending
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
describe Tarte, "finder methods/scopes" do
|
128
|
+
it "should define scopes based on code gorups" do
|
129
|
+
entry = HasOneStatus.create!(:status => :red)
|
130
|
+
HasOneStatus.is_stop.last.should eql(entry)
|
131
|
+
end
|
132
|
+
|
133
|
+
it "should define scopes based on mask gorups" do
|
134
|
+
entry = HasManyIngredients.create!(:ingredients => [:fish, :chips])
|
135
|
+
HasManyIngredients.matching_fish_and_chips.last.should eql(entry)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
describe Tarte, "validations using groups" do
|
140
|
+
it "should validate has_one associations" do
|
141
|
+
entry = HasOneStatus.new
|
142
|
+
entry.errors.should_receive(:add)
|
143
|
+
entry.status = :green
|
144
|
+
entry.save
|
145
|
+
end
|
146
|
+
|
147
|
+
it "has_one should pass when created with valid attributes" do
|
148
|
+
HasOneStatus.create!('status' => :red)
|
149
|
+
end
|
150
|
+
|
151
|
+
it "should validate has_many associations" do
|
152
|
+
entry = HasManyIngredients.new
|
153
|
+
entry.ingredients << :sauce
|
154
|
+
entry.save
|
155
|
+
entry.errors.on(:ingredients).should eql("I only like fish and chips")
|
156
|
+
end
|
157
|
+
|
158
|
+
it "has_many should pass when created with valid attributes" do
|
159
|
+
HasManyIngredients.create!('ingredients' => [:fish, :chips])
|
160
|
+
end
|
161
|
+
|
162
|
+
end
|
data/tarte.gemspec
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{tarte}
|
8
|
+
s.version = "0.1.2"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Leandro Pedroni"]
|
12
|
+
s.date = %q{2010-05-20}
|
13
|
+
s.description = %q{Provides a lot of helper methods and uses integer indexes or bitmasks to store in a record an association to a predetermined set of symbols.}
|
14
|
+
s.email = %q{ilpoldo@gmail.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE",
|
17
|
+
"README.rdoc"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
".gitignore",
|
22
|
+
"LICENSE",
|
23
|
+
"README.rdoc",
|
24
|
+
"Rakefile",
|
25
|
+
"VERSION",
|
26
|
+
"init.rb",
|
27
|
+
"lib/tarte.rb",
|
28
|
+
"lib/tarte/baked_in_associations.rb",
|
29
|
+
"lib/tarte/baked_in_validation_helpers.rb",
|
30
|
+
"spec/debug.log",
|
31
|
+
"spec/schema.rb",
|
32
|
+
"spec/spec.opts",
|
33
|
+
"spec/spec_helper.rb",
|
34
|
+
"spec/tarte_spec.rb",
|
35
|
+
"tarte.gemspec"
|
36
|
+
]
|
37
|
+
s.homepage = %q{http://github.com/ilpoldo/tarte}
|
38
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
39
|
+
s.require_paths = ["lib"]
|
40
|
+
s.rubygems_version = %q{1.3.6}
|
41
|
+
s.summary = %q{Baked in ActiveRecord Associations}
|
42
|
+
s.test_files = [
|
43
|
+
"spec/schema.rb",
|
44
|
+
"spec/spec_helper.rb",
|
45
|
+
"spec/tarte_spec.rb"
|
46
|
+
]
|
47
|
+
|
48
|
+
if s.respond_to? :specification_version then
|
49
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
50
|
+
s.specification_version = 3
|
51
|
+
|
52
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
53
|
+
s.add_development_dependency(%q<rspec>, [">= 1.2.9"])
|
54
|
+
else
|
55
|
+
s.add_dependency(%q<rspec>, [">= 1.2.9"])
|
56
|
+
end
|
57
|
+
else
|
58
|
+
s.add_dependency(%q<rspec>, [">= 1.2.9"])
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
metadata
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: tarte
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 1
|
8
|
+
- 2
|
9
|
+
version: 0.1.2
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Leandro Pedroni
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-05-20 00:00:00 +01:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: rspec
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 1
|
29
|
+
- 2
|
30
|
+
- 9
|
31
|
+
version: 1.2.9
|
32
|
+
type: :development
|
33
|
+
version_requirements: *id001
|
34
|
+
description: Provides a lot of helper methods and uses integer indexes or bitmasks to store in a record an association to a predetermined set of symbols.
|
35
|
+
email: ilpoldo@gmail.com
|
36
|
+
executables: []
|
37
|
+
|
38
|
+
extensions: []
|
39
|
+
|
40
|
+
extra_rdoc_files:
|
41
|
+
- LICENSE
|
42
|
+
- README.rdoc
|
43
|
+
files:
|
44
|
+
- .document
|
45
|
+
- .gitignore
|
46
|
+
- LICENSE
|
47
|
+
- README.rdoc
|
48
|
+
- Rakefile
|
49
|
+
- VERSION
|
50
|
+
- init.rb
|
51
|
+
- lib/tarte.rb
|
52
|
+
- lib/tarte/baked_in_associations.rb
|
53
|
+
- lib/tarte/baked_in_validation_helpers.rb
|
54
|
+
- spec/debug.log
|
55
|
+
- spec/schema.rb
|
56
|
+
- spec/spec.opts
|
57
|
+
- spec/spec_helper.rb
|
58
|
+
- spec/tarte_spec.rb
|
59
|
+
- tarte.gemspec
|
60
|
+
has_rdoc: true
|
61
|
+
homepage: http://github.com/ilpoldo/tarte
|
62
|
+
licenses: []
|
63
|
+
|
64
|
+
post_install_message:
|
65
|
+
rdoc_options:
|
66
|
+
- --charset=UTF-8
|
67
|
+
require_paths:
|
68
|
+
- lib
|
69
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
segments:
|
74
|
+
- 0
|
75
|
+
version: "0"
|
76
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
77
|
+
requirements:
|
78
|
+
- - ">="
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
segments:
|
81
|
+
- 0
|
82
|
+
version: "0"
|
83
|
+
requirements: []
|
84
|
+
|
85
|
+
rubyforge_project:
|
86
|
+
rubygems_version: 1.3.6
|
87
|
+
signing_key:
|
88
|
+
specification_version: 3
|
89
|
+
summary: Baked in ActiveRecord Associations
|
90
|
+
test_files:
|
91
|
+
- spec/schema.rb
|
92
|
+
- spec/spec_helper.rb
|
93
|
+
- spec/tarte_spec.rb
|