assignable_values 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +8 -0
- data/.rspec +1 -0
- data/Gemfile +3 -0
- data/README.md +99 -0
- data/Rakefile +11 -0
- data/assignable_values.gemspec +22 -0
- data/lib/assignable_values.rb +6 -0
- data/lib/assignable_values/active_record.rb +25 -0
- data/lib/assignable_values/active_record/restriction/base.rb +152 -0
- data/lib/assignable_values/active_record/restriction/belongs_to_association.rb +41 -0
- data/lib/assignable_values/active_record/restriction/scalar_attribute.rb +28 -0
- data/lib/assignable_values/errors.rb +5 -0
- data/lib/assignable_values/version.rb +3 -0
- data/spec/app_root/.gitignore +4 -0
- data/spec/app_root/app/controllers/application_controller.rb +2 -0
- data/spec/app_root/app/models/artist.rb +5 -0
- data/spec/app_root/app/models/song.rb +5 -0
- data/spec/app_root/config/application.rb +31 -0
- data/spec/app_root/config/boot.rb +13 -0
- data/spec/app_root/config/database.yml +4 -0
- data/spec/app_root/config/environment.rb +5 -0
- data/spec/app_root/config/environments/in_memory.rb +0 -0
- data/spec/app_root/config/environments/mysql.rb +0 -0
- data/spec/app_root/config/environments/postgresql.rb +0 -0
- data/spec/app_root/config/environments/sqlite.rb +0 -0
- data/spec/app_root/config/environments/sqlite3.rb +0 -0
- data/spec/app_root/config/environments/test.rb +35 -0
- data/spec/app_root/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/app_root/config/initializers/inflections.rb +10 -0
- data/spec/app_root/config/initializers/mime_types.rb +5 -0
- data/spec/app_root/config/initializers/secret_token.rb +7 -0
- data/spec/app_root/config/initializers/session_store.rb +8 -0
- data/spec/app_root/config/locales/en.yml +10 -0
- data/spec/app_root/config/routes.rb +58 -0
- data/spec/app_root/db/migrate/001_create_artists.rb +11 -0
- data/spec/app_root/db/migrate/002_create_songs.rb +15 -0
- data/spec/app_root/lib/tasks/.gitkeep +0 -0
- data/spec/app_root/script/rails +6 -0
- data/spec/assignable_values/active_record_spec.rb +385 -0
- data/spec/rcov.opts +2 -0
- data/spec/spec_helper.rb +21 -0
- metadata +165 -0
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
# assignable_values - Enums on vitamins
|
2
|
+
|
3
|
+
`assignable_values` lets you restrict the values that can be assigned to attributes or associations of ActiveRecord models. You can think of it as enums where the list of allowed values is generated at runtime and the value is checked during validation.
|
4
|
+
|
5
|
+
We carefully enhanced the cure enum functionality with small tweaks that are useful for web forms, internationalized applications and common authorization patterns.
|
6
|
+
|
7
|
+
## Restricting attributes
|
8
|
+
|
9
|
+
The basic usage to restrict the values assignable to strings, integers, etc. is this:
|
10
|
+
|
11
|
+
class Song < ActiveRecord::Base
|
12
|
+
assignable_values_for :genre do
|
13
|
+
['pop', 'rock', 'electronic']
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
The assigned value is checked during validation:
|
18
|
+
|
19
|
+
Song.new(:genre => 'rock').valid? # => true
|
20
|
+
Song.new(:genre => 'elephant').valid? # => false
|
21
|
+
|
22
|
+
The validation error message is the same as the one from `validates_inclusion_of` (`errors.messages.inclusion` in your I18n dictionary).
|
23
|
+
|
24
|
+
### How assignable values are generated
|
25
|
+
|
26
|
+
The list of assignable values is generated at runtime. Since the given block is evaluated on the record instance, so you can refer to other methods:
|
27
|
+
|
28
|
+
class Song < ActiveRecord::Base
|
29
|
+
|
30
|
+
validates_numericality_of :year
|
31
|
+
|
32
|
+
assignable_values_for :genre do
|
33
|
+
genres = []
|
34
|
+
genres << 'jazz' if year > 1900
|
35
|
+
genres << 'rock' if year > 1960
|
36
|
+
genres
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
### Obtaining lists
|
42
|
+
|
43
|
+
You can ask a record for a list of values that can be assigned to attribute:
|
44
|
+
|
45
|
+
song.assignable_genres # => ['pop', 'rock', 'electronic']
|
46
|
+
|
47
|
+
This is useful for populating <select> tags in web forms:
|
48
|
+
|
49
|
+
form.select :genre, form.object.assignable_genres
|
50
|
+
|
51
|
+
### Human labels
|
52
|
+
|
53
|
+
You will often want to present internal values in a humanized form. E.g. `"pop"` should be presented as `"Pop music"`.
|
54
|
+
|
55
|
+
You can define human labels in your I18n dictionary:
|
56
|
+
|
57
|
+
en:
|
58
|
+
assignable_values:
|
59
|
+
song:
|
60
|
+
genre:
|
61
|
+
pop: 'Pop music'
|
62
|
+
rock: 'Rock music'
|
63
|
+
electronic: 'Electronic music'
|
64
|
+
|
65
|
+
When obtaining a list of assignable values, each value will have a method `#human` that returns the translation:
|
66
|
+
|
67
|
+
song.assignable_genres.first # => 'pop'
|
68
|
+
song.assignable_genres.first.human # => 'Pop music'
|
69
|
+
|
70
|
+
You can populate a <select> tag with pairs of internal values and human labels like this:
|
71
|
+
|
72
|
+
form.collection_select :genre, form.object.assignable_genres, :to_s, :human
|
73
|
+
|
74
|
+
## Restricting belongs_to associations
|
75
|
+
|
76
|
+
### No caching
|
77
|
+
|
78
|
+
## Defining default values
|
79
|
+
|
80
|
+
## Obtaining assignable values from a delegate
|
81
|
+
|
82
|
+
Text here.
|
83
|
+
|
84
|
+
## Previously saved values
|
85
|
+
|
86
|
+
- Always valid
|
87
|
+
- Are listed
|
88
|
+
|
89
|
+
## Installation
|
90
|
+
|
91
|
+
Text here.
|
92
|
+
|
93
|
+
## Development
|
94
|
+
|
95
|
+
Text here.
|
96
|
+
|
97
|
+
## Credits
|
98
|
+
|
99
|
+
Text here.
|
data/Rakefile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
$:.push File.expand_path("../lib", __FILE__)
|
2
|
+
require "assignable_values/version"
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = 'assignable_values'
|
6
|
+
s.version = AssignableValues::VERSION
|
7
|
+
s.authors = ["Henning Koch"]
|
8
|
+
s.email = 'henning.koch@makandra.de'
|
9
|
+
s.homepage = 'https://github.com/makandra/assignable_values'
|
10
|
+
s.summary = 'Restrict the values assignable to ActiveRecord attributes or associations. Or enums on steroids.'
|
11
|
+
s.description = s.summary
|
12
|
+
|
13
|
+
s.files = `git ls-files`.split("\n")
|
14
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
15
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
16
|
+
s.require_paths = ["lib"]
|
17
|
+
|
18
|
+
s.add_development_dependency('rails', '~>3.1')
|
19
|
+
s.add_development_dependency('rspec', '~>2.8')
|
20
|
+
s.add_development_dependency('rspec-rails', '~>2.8')
|
21
|
+
s.add_development_dependency('sqlite3')
|
22
|
+
end
|
@@ -0,0 +1,6 @@
|
|
1
|
+
require 'assignable_values/errors'
|
2
|
+
require 'assignable_values/active_record'
|
3
|
+
require 'assignable_values/active_record/restriction/base'
|
4
|
+
require 'assignable_values/active_record/restriction/belongs_to_association'
|
5
|
+
require 'assignable_values/active_record/restriction/scalar_attribute'
|
6
|
+
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module AssignableValues
|
2
|
+
module ActiveRecord
|
3
|
+
|
4
|
+
private
|
5
|
+
|
6
|
+
def assignable_values_for(property, options = {}, &values)
|
7
|
+
restriction_type = belongs_to_association?(property) ? Restriction::BelongsToAssociation : Restriction::ScalarAttribute
|
8
|
+
restriction_type.new(self, property, options, &values)
|
9
|
+
end
|
10
|
+
|
11
|
+
#def authorize_values_for(property, options = {})
|
12
|
+
# method_defined?(:power) or attr_accessor :power
|
13
|
+
# assignable_values_for property, options.merge(:through => :power)
|
14
|
+
#end
|
15
|
+
|
16
|
+
def belongs_to_association?(property)
|
17
|
+
reflection = reflect_on_association(property)
|
18
|
+
reflection && reflection.macro == :belongs_to
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
ActiveRecord::Base.send(:extend, AssignableValues::ActiveRecord)
|
25
|
+
|
@@ -0,0 +1,152 @@
|
|
1
|
+
module AssignableValues
|
2
|
+
module ActiveRecord
|
3
|
+
module Restriction
|
4
|
+
class Base
|
5
|
+
|
6
|
+
attr_reader :model, :property, :options, :values, :default
|
7
|
+
|
8
|
+
def initialize(model, property, options, &values)
|
9
|
+
@model = model
|
10
|
+
@property = property
|
11
|
+
@options = options
|
12
|
+
@values = values
|
13
|
+
ensure_values_given
|
14
|
+
setup_default if default?
|
15
|
+
define_assignable_values_method
|
16
|
+
setup_validation
|
17
|
+
end
|
18
|
+
|
19
|
+
def validate_record(record)
|
20
|
+
value = current_value(record)
|
21
|
+
unless allow_blank? && value.blank?
|
22
|
+
begin
|
23
|
+
assignable_values = assignable_values(record)
|
24
|
+
assignable_values.include?(value) or record.errors.add(property, I18n.t('errors.messages.inclusion'))
|
25
|
+
rescue DelegateUnavailable
|
26
|
+
# if the delegate is unavailable, the validation is skipped
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def assignable_values(record)
|
32
|
+
assignable_values = []
|
33
|
+
old_value = previously_saved_value(record)
|
34
|
+
assignable_values << old_value if old_value.present?
|
35
|
+
assignable_values |= raw_assignable_values(record)
|
36
|
+
assignable_values = decorate_values(assignable_values)
|
37
|
+
assignable_values
|
38
|
+
end
|
39
|
+
|
40
|
+
def set_default(record)
|
41
|
+
if record.new_record? && record.send(property).nil?
|
42
|
+
default_value = default
|
43
|
+
default_value = record.instance_eval(&default_value) if default_value.is_a?(Proc)
|
44
|
+
record.send("#{property}=", default_value)
|
45
|
+
end
|
46
|
+
true
|
47
|
+
end
|
48
|
+
|
49
|
+
def humanize_string_value(value)
|
50
|
+
I18n.t("assignable_values.#{model.name.underscore}.#{property}.#{value}", :default => value.humanize)
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def current_value(record)
|
56
|
+
record.send(property)
|
57
|
+
end
|
58
|
+
|
59
|
+
def previously_saved_value(record)
|
60
|
+
nil
|
61
|
+
end
|
62
|
+
|
63
|
+
def decorate_values(values)
|
64
|
+
values
|
65
|
+
end
|
66
|
+
|
67
|
+
def delegate?
|
68
|
+
@options.has_key?(:through)
|
69
|
+
end
|
70
|
+
|
71
|
+
def default?
|
72
|
+
@options.has_key?(:default)
|
73
|
+
end
|
74
|
+
|
75
|
+
def allow_blank?
|
76
|
+
@options[:allow_blank]
|
77
|
+
end
|
78
|
+
|
79
|
+
def delegate_definition
|
80
|
+
options[:through]
|
81
|
+
end
|
82
|
+
|
83
|
+
def enhance_model(&block)
|
84
|
+
@model.class_eval(&block)
|
85
|
+
end
|
86
|
+
|
87
|
+
def setup_default
|
88
|
+
@default = options[:default]
|
89
|
+
restriction = self
|
90
|
+
enhance_model do
|
91
|
+
set_default_method = "set_default_#{restriction.property}"
|
92
|
+
define_method set_default_method do
|
93
|
+
restriction.set_default(self)
|
94
|
+
end
|
95
|
+
after_initialize set_default_method
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def setup_validation
|
100
|
+
restriction = self
|
101
|
+
enhance_model do
|
102
|
+
validate_method = "validate_#{restriction.property}_assignable"
|
103
|
+
define_method validate_method do
|
104
|
+
restriction.validate_record(self)
|
105
|
+
end
|
106
|
+
validate validate_method
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def define_assignable_values_method
|
111
|
+
restriction = self
|
112
|
+
enhance_model do
|
113
|
+
assignable_values_method = "assignable_#{restriction.property.to_s.pluralize}"
|
114
|
+
define_method assignable_values_method do
|
115
|
+
restriction.assignable_values(self)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def raw_assignable_values(record)
|
121
|
+
if delegate?
|
122
|
+
assignable_values_from_delegate(record)
|
123
|
+
else
|
124
|
+
record.instance_eval(&@values)
|
125
|
+
end.to_a
|
126
|
+
end
|
127
|
+
|
128
|
+
def delegate(record)
|
129
|
+
case delegate_definition
|
130
|
+
when Symbol then record.send(delegate_definition)
|
131
|
+
when Proc then record.instance_eval(&delegate_definition)
|
132
|
+
else raise "Illegal delegate definition: #{delegate_definition.inspect}"
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def assignable_values_from_delegate(record)
|
137
|
+
delegate = delegate(record)
|
138
|
+
delegate.present? or raise DelegateUnavailable, "Cannot query a nil delegate for assignable values"
|
139
|
+
delegate_query_method = "assignable_#{model.name.underscore}_#{property.to_s.pluralize}"
|
140
|
+
args = delegate.method(delegate_query_method).arity == 1 ? [record] : []
|
141
|
+
delegate.send(delegate_query_method, *args)
|
142
|
+
end
|
143
|
+
|
144
|
+
def ensure_values_given
|
145
|
+
@values or @options[:through] or raise NoValuesGiven, 'You must supply the list of assignable values by either a block or :through option'
|
146
|
+
end
|
147
|
+
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module AssignableValues
|
2
|
+
module ActiveRecord
|
3
|
+
module Restriction
|
4
|
+
class BelongsToAssociation < Base
|
5
|
+
|
6
|
+
private
|
7
|
+
|
8
|
+
def association_class
|
9
|
+
model.reflect_on_association(property).klass
|
10
|
+
end
|
11
|
+
|
12
|
+
def association_id_method
|
13
|
+
"#{property}_id"
|
14
|
+
end
|
15
|
+
|
16
|
+
def association_id(record)
|
17
|
+
record.send(association_id_method)
|
18
|
+
end
|
19
|
+
|
20
|
+
def previously_saved_value(record)
|
21
|
+
if old_id = record.send("#{association_id_method}_was")
|
22
|
+
if old_id == association_id(record)
|
23
|
+
current_value(record) # no need to query the database if nothing changed
|
24
|
+
else
|
25
|
+
association_class.find_by_id(old_id)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def current_value(record)
|
31
|
+
value = record.send(property)
|
32
|
+
value = record.send(property, true) if (value && value.id) != association_id(record)
|
33
|
+
value
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module AssignableValues
|
2
|
+
module ActiveRecord
|
3
|
+
module Restriction
|
4
|
+
class ScalarAttribute < Base
|
5
|
+
|
6
|
+
private
|
7
|
+
|
8
|
+
def decorate_values(values)
|
9
|
+
restriction = self
|
10
|
+
values.collect do |value|
|
11
|
+
if value.is_a?(String)
|
12
|
+
value = value.dup
|
13
|
+
value.singleton_class.send(:define_method, :human) do
|
14
|
+
restriction.humanize_string_value(value)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
value
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def previously_saved_value(record)
|
22
|
+
record.send("#{property}_was")
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require File.expand_path('../boot', __FILE__)
|
2
|
+
|
3
|
+
require 'rails/all'
|
4
|
+
|
5
|
+
# If you have a Gemfile, require the gems listed there, including any gems
|
6
|
+
# you've limited to :test, :development, or :production.
|
7
|
+
Bundler.require(:default, Rails.env) if defined?(Bundler)
|
8
|
+
|
9
|
+
module AssignableValuesSpecApp
|
10
|
+
class Application < Rails::Application
|
11
|
+
config.encoding = "utf-8"
|
12
|
+
|
13
|
+
config.cache_classes = true
|
14
|
+
config.whiny_nils = true
|
15
|
+
|
16
|
+
config.consider_all_requests_local = true
|
17
|
+
config.action_controller.perform_caching = false
|
18
|
+
|
19
|
+
config.action_dispatch.show_exceptions = false
|
20
|
+
|
21
|
+
config.action_controller.allow_forgery_protection = false
|
22
|
+
|
23
|
+
config.action_mailer.delivery_method = :test
|
24
|
+
|
25
|
+
config.active_support.deprecation = :stderr
|
26
|
+
|
27
|
+
config.root = File.expand_path('../..', __FILE__)
|
28
|
+
|
29
|
+
# railties.plugins << Rails::Plugin.new(File.expand_path('../../../../..', __FILE__))
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
|
3
|
+
# Set up gems listed in the Gemfile.
|
4
|
+
gemfile = File.expand_path('../../Gemfile', __FILE__)
|
5
|
+
begin
|
6
|
+
ENV['BUNDLE_GEMFILE'] = gemfile
|
7
|
+
require 'bundler'
|
8
|
+
Bundler.setup
|
9
|
+
rescue Bundler::GemNotFound => e
|
10
|
+
STDERR.puts e.message
|
11
|
+
STDERR.puts "Try running `bundle install`."
|
12
|
+
exit!
|
13
|
+
end if File.exist?(gemfile)
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
@@ -0,0 +1,35 @@
|
|
1
|
+
AssignableValuesSpecApp::Application.configure do
|
2
|
+
# Settings specified here will take precedence over those in config/application.rb
|
3
|
+
|
4
|
+
# The test environment is used exclusively to run your application's
|
5
|
+
# test suite. You never need to work with it otherwise. Remember that
|
6
|
+
# your test database is "scratch space" for the test suite and is wiped
|
7
|
+
# and recreated between test runs. Don't rely on the data there!
|
8
|
+
config.cache_classes = false
|
9
|
+
|
10
|
+
# Log error messages when you accidentally call methods on nil.
|
11
|
+
config.whiny_nils = true
|
12
|
+
|
13
|
+
# Show full error reports and disable caching
|
14
|
+
config.consider_all_requests_local = true
|
15
|
+
config.action_controller.perform_caching = false
|
16
|
+
|
17
|
+
# Raise exceptions instead of rendering exception templates
|
18
|
+
config.action_dispatch.show_exceptions = false
|
19
|
+
|
20
|
+
# Disable request forgery protection in test environment
|
21
|
+
config.action_controller.allow_forgery_protection = false
|
22
|
+
|
23
|
+
# Tell Action Mailer not to deliver emails to the real world.
|
24
|
+
# The :test delivery method accumulates sent emails in the
|
25
|
+
# ActionMailer::Base.deliveries array.
|
26
|
+
config.action_mailer.delivery_method = :test
|
27
|
+
|
28
|
+
# Use SQL instead of Active Record's schema dumper when creating the test database.
|
29
|
+
# This is necessary if your schema can't be completely dumped by the schema dumper,
|
30
|
+
# like if you have constraints or database-specific column types
|
31
|
+
# config.active_record.schema_format = :sql
|
32
|
+
|
33
|
+
# Print deprecation notices to the stderr
|
34
|
+
config.active_support.deprecation = :stderr
|
35
|
+
end
|
@@ -0,0 +1,7 @@
|
|
1
|
+
# Be sure to restart your server when you modify this file.
|
2
|
+
|
3
|
+
# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
|
4
|
+
# Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }
|
5
|
+
|
6
|
+
# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.
|
7
|
+
# Rails.backtrace_cleaner.remove_silencers!
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# Be sure to restart your server when you modify this file.
|
2
|
+
|
3
|
+
# Add new inflection rules using the following format
|
4
|
+
# (all these examples are active by default):
|
5
|
+
# ActiveSupport::Inflector.inflections do |inflect|
|
6
|
+
# inflect.plural /^(ox)$/i, '\1en'
|
7
|
+
# inflect.singular /^(ox)en/i, '\1'
|
8
|
+
# inflect.irregular 'person', 'people'
|
9
|
+
# inflect.uncountable %w( fish sheep )
|
10
|
+
# end
|
@@ -0,0 +1,7 @@
|
|
1
|
+
# Be sure to restart your server when you modify this file.
|
2
|
+
|
3
|
+
# Your secret key for verifying the integrity of signed cookies.
|
4
|
+
# If you change this key, all old signed cookies will become invalid!
|
5
|
+
# Make sure the secret is at least 30 characters and all random,
|
6
|
+
# no regular words or you'll be exposed to dictionary attacks.
|
7
|
+
AssignableValuesSpecApp::Application.config.secret_token = 'cb014a08a45243e7143f31e04774c342c1fba329fd594ae1a480d8283b1a851f425dc08044311fb4be6d000b6e6681de7c76d19148419a5ffa0a9f84556d3b33'
|
@@ -0,0 +1,8 @@
|
|
1
|
+
# Be sure to restart your server when you modify this file.
|
2
|
+
|
3
|
+
AssignableValuesSpecApp::Application.config.session_store :cookie_store, :key => '_app_root_session'
|
4
|
+
|
5
|
+
# Use the database for sessions instead of the cookie-based default,
|
6
|
+
# which shouldn't be used to store highly confidential information
|
7
|
+
# (create the session table with "rails generate session_migration")
|
8
|
+
# AssignableValuesSpecApp::Application.config.session_store :active_record_store
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# Sample localization file for English. Add more files in this directory for other locales.
|
2
|
+
# See http://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points.
|
3
|
+
|
4
|
+
en:
|
5
|
+
assignable_values:
|
6
|
+
song:
|
7
|
+
genre:
|
8
|
+
pop: 'Pop music'
|
9
|
+
rock: 'Rock music'
|
10
|
+
|
@@ -0,0 +1,58 @@
|
|
1
|
+
AssignableValuesSpecApp::Application.routes.draw do
|
2
|
+
# The priority is based upon order of creation:
|
3
|
+
# first created -> highest priority.
|
4
|
+
|
5
|
+
# Sample of regular route:
|
6
|
+
# match 'products/:id' => 'catalog#view'
|
7
|
+
# Keep in mind you can assign values other than :controller and :action
|
8
|
+
|
9
|
+
# Sample of named route:
|
10
|
+
# match 'products/:id/purchase' => 'catalog#purchase', :as => :purchase
|
11
|
+
# This route can be invoked with purchase_url(:id => product.id)
|
12
|
+
|
13
|
+
# Sample resource route (maps HTTP verbs to controller actions automatically):
|
14
|
+
# resources :products
|
15
|
+
|
16
|
+
# Sample resource route with options:
|
17
|
+
# resources :products do
|
18
|
+
# member do
|
19
|
+
# get 'short'
|
20
|
+
# post 'toggle'
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# collection do
|
24
|
+
# get 'sold'
|
25
|
+
# end
|
26
|
+
# end
|
27
|
+
|
28
|
+
# Sample resource route with sub-resources:
|
29
|
+
# resources :products do
|
30
|
+
# resources :comments, :sales
|
31
|
+
# resource :seller
|
32
|
+
# end
|
33
|
+
|
34
|
+
# Sample resource route with more complex sub-resources
|
35
|
+
# resources :products do
|
36
|
+
# resources :comments
|
37
|
+
# resources :sales do
|
38
|
+
# get 'recent', :on => :collection
|
39
|
+
# end
|
40
|
+
# end
|
41
|
+
|
42
|
+
# Sample resource route within a namespace:
|
43
|
+
# namespace :admin do
|
44
|
+
# # Directs /admin/products/* to Admin::ProductsController
|
45
|
+
# # (app/controllers/admin/products_controller.rb)
|
46
|
+
# resources :products
|
47
|
+
# end
|
48
|
+
|
49
|
+
# You can have the root of your site routed with "root"
|
50
|
+
# just remember to delete public/index.html.
|
51
|
+
# root :to => "welcome#index"
|
52
|
+
|
53
|
+
# See how all your routes lay out with "rake routes"
|
54
|
+
|
55
|
+
# This is a legacy wild controller route that's not recommended for RESTful applications.
|
56
|
+
# Note: This route will make all actions in every controller accessible via GET requests.
|
57
|
+
match ':controller(/:action(/:id(.:format)))'
|
58
|
+
end
|
File without changes
|
@@ -0,0 +1,6 @@
|
|
1
|
+
#!/usr/bin/env ruby1.8
|
2
|
+
# This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
|
3
|
+
|
4
|
+
APP_PATH = File.expand_path('../../config/application', __FILE__)
|
5
|
+
require File.expand_path('../../config/boot', __FILE__)
|
6
|
+
require 'rails/commands'
|
@@ -0,0 +1,385 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'ostruct'
|
3
|
+
|
4
|
+
describe AssignableValues::ActiveRecord do
|
5
|
+
|
6
|
+
def disposable_song_class(&block)
|
7
|
+
@klass = Class.new(Song, &block)
|
8
|
+
@klass.class_eval do
|
9
|
+
def self.name
|
10
|
+
'Song'
|
11
|
+
end
|
12
|
+
end
|
13
|
+
@klass
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '.assignable_values' do
|
17
|
+
|
18
|
+
it 'should raise an error when not called with a block or :through option' do
|
19
|
+
expect do
|
20
|
+
disposable_song_class do
|
21
|
+
assignable_values_for :genre
|
22
|
+
end
|
23
|
+
end.to raise_error(AssignableValues::NoValuesGiven)
|
24
|
+
end
|
25
|
+
|
26
|
+
context 'when validating scalar attributes' do
|
27
|
+
|
28
|
+
context 'without options' do
|
29
|
+
|
30
|
+
before :each do
|
31
|
+
@klass = disposable_song_class do
|
32
|
+
assignable_values_for :genre do
|
33
|
+
%w[pop rock]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should validate that the attribute is allowed' do
|
39
|
+
@klass.new(:genre => 'pop').should be_valid
|
40
|
+
@klass.new(:genre => 'disallowed value').should_not be_valid
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'should use the same error message as validates_inclusion_of' do
|
44
|
+
record = @klass.new(:genre => 'disallowed value')
|
45
|
+
record.valid?
|
46
|
+
record.errors[:genre].first.should == I18n.t('errors.messages.inclusion')
|
47
|
+
record.errors[:genre].first.should == 'is not included in the list'
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'should not allow nil for the attribute value' do
|
51
|
+
@klass.new(:genre => nil).should_not be_valid
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'should allow a previously saved value even if that value is no longer allowed' do
|
55
|
+
song = @klass.new(:genre => 'disallowed value')
|
56
|
+
song.save!(:validate => false)
|
57
|
+
song.should be_valid
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
context 'if the :allow_blank option is set' do
|
63
|
+
|
64
|
+
before :each do
|
65
|
+
@klass = disposable_song_class do
|
66
|
+
assignable_values_for :genre, :allow_blank => true do
|
67
|
+
%w[pop rock]
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'should allow nil for the attribute value' do
|
73
|
+
@klass.new(:genre => nil).should be_valid
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'should allow an empty string as value if the :allow_blank option is set' do
|
77
|
+
@klass.new(:genre => '').should be_valid
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
context 'when validating belongs_to associations' do
|
85
|
+
|
86
|
+
it 'should validate that the association is allowed' do
|
87
|
+
allowed_association = Artist.create!
|
88
|
+
disallowed_association = Artist.create!
|
89
|
+
@klass = disposable_song_class do
|
90
|
+
assignable_values_for :artist do
|
91
|
+
[allowed_association]
|
92
|
+
end
|
93
|
+
end
|
94
|
+
@klass.new(:artist => allowed_association).should be_valid
|
95
|
+
@klass.new(:artist => disallowed_association).should_not be_valid
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'should allow a nil association if the :allow_blank option is set' do
|
99
|
+
@klass = disposable_song_class do
|
100
|
+
assignable_values_for :artist, :allow_blank => true do
|
101
|
+
[]
|
102
|
+
end
|
103
|
+
end
|
104
|
+
record = @klass.new
|
105
|
+
record.artist.should be_nil
|
106
|
+
record.should be_valid
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'should allow a previously saved association even if that association is no longer allowed' do
|
110
|
+
allowed_association = Artist.create!
|
111
|
+
disallowed_association = Artist.create!
|
112
|
+
@klass = disposable_song_class
|
113
|
+
record = @klass.create!(:artist => disallowed_association)
|
114
|
+
@klass.class_eval do
|
115
|
+
assignable_values_for :artist do
|
116
|
+
[allowed_association]
|
117
|
+
end
|
118
|
+
end
|
119
|
+
record.should be_valid
|
120
|
+
end
|
121
|
+
|
122
|
+
it "should not load a previously saved association if the association's foreign key hasn't changed" do
|
123
|
+
association = Artist.create!
|
124
|
+
@klass = disposable_song_class do
|
125
|
+
assignable_values_for :artist do
|
126
|
+
[association] # This example doesn't care about what's assignable. We're only interested in behavior up to the validation.
|
127
|
+
end
|
128
|
+
end
|
129
|
+
record = @klass.create!(:artist => association)
|
130
|
+
Artist.should_not_receive(:find_by_id)
|
131
|
+
record.valid?
|
132
|
+
end
|
133
|
+
|
134
|
+
it 'should not fail or allow nil if a previously saved association no longer exists in the database' do
|
135
|
+
allowed_association = Artist.create!
|
136
|
+
disposable_song_class.class_eval do
|
137
|
+
assignable_values_for :artist do
|
138
|
+
[allowed_association]
|
139
|
+
end
|
140
|
+
end
|
141
|
+
record = @klass.new
|
142
|
+
record.stub :artist_id_was => -1
|
143
|
+
record.should_not be_valid
|
144
|
+
end
|
145
|
+
|
146
|
+
it 'should uncache a stale association before validating' do
|
147
|
+
@klass = disposable_song_class do
|
148
|
+
assignable_values_for :artist do
|
149
|
+
[] # This example doesn't care about what's assignable. We're only interested in behavior up to the validation.
|
150
|
+
end
|
151
|
+
end
|
152
|
+
association = Artist.create!
|
153
|
+
record = @klass.new
|
154
|
+
record.stub(:artist => association, :artist_id => -1) # This is a stale association: The associated object's id doesn't match the foreign key. This can happen in Rails 2, not Rails 3.
|
155
|
+
record.should_receive(:artist).ordered.and_return(association)
|
156
|
+
record.should_receive(:artist).ordered.with(true).and_return(association)
|
157
|
+
record.valid?
|
158
|
+
end
|
159
|
+
|
160
|
+
it 'should not uncache a fresh association before validating' do
|
161
|
+
@klass = disposable_song_class do
|
162
|
+
assignable_values_for :artist do
|
163
|
+
[] # This example doesn't care about what's assignable. We're only interested in behavior up to the validation.
|
164
|
+
end
|
165
|
+
end
|
166
|
+
association = Artist.create!
|
167
|
+
record = @klass.new
|
168
|
+
record.stub(:artist => association, :artist_id => association.id) # This is a fresh association: The associated object's id matches the foreign key.
|
169
|
+
record.should_receive(:artist).with(no_args).and_return(association)
|
170
|
+
record.valid?
|
171
|
+
end
|
172
|
+
|
173
|
+
end
|
174
|
+
|
175
|
+
context 'when delegating using the :through option' do
|
176
|
+
|
177
|
+
it 'should obtain allowed values from a method with the given name' do
|
178
|
+
@klass = disposable_song_class do
|
179
|
+
assignable_values_for :genre, :through => :delegate
|
180
|
+
def delegate
|
181
|
+
OpenStruct.new(:assignable_song_genres => %w[pop rock])
|
182
|
+
end
|
183
|
+
end
|
184
|
+
@klass.new(:genre => 'pop').should be_valid
|
185
|
+
@klass.new(:genre => 'disallowed value').should_not be_valid
|
186
|
+
end
|
187
|
+
|
188
|
+
it 'should be able to delegate to a lambda, which is evaluated in the context of the record instance' do
|
189
|
+
@klass = disposable_song_class do
|
190
|
+
assignable_values_for :genre, :through => lambda { delegate }
|
191
|
+
def delegate
|
192
|
+
OpenStruct.new(:assignable_song_genres => %w[pop rock])
|
193
|
+
end
|
194
|
+
end
|
195
|
+
@klass.new(:genre => 'pop').should be_valid
|
196
|
+
@klass.new(:genre => 'disallowed value').should_not be_valid
|
197
|
+
end
|
198
|
+
|
199
|
+
it 'should skip the validation if that method returns nil' do
|
200
|
+
@klass = disposable_song_class do
|
201
|
+
assignable_values_for :genre, :through => :delegate
|
202
|
+
def delegate
|
203
|
+
nil
|
204
|
+
end
|
205
|
+
end
|
206
|
+
@klass.new(:genre => 'pop').should be_valid
|
207
|
+
end
|
208
|
+
|
209
|
+
end
|
210
|
+
|
211
|
+
context 'with :default option' do
|
212
|
+
|
213
|
+
it 'should allow to set a default' do
|
214
|
+
@klass = disposable_song_class do
|
215
|
+
assignable_values_for :genre, :default => 'pop' do
|
216
|
+
%w[pop rock]
|
217
|
+
end
|
218
|
+
end
|
219
|
+
@klass.new.genre.should == 'pop'
|
220
|
+
end
|
221
|
+
|
222
|
+
it 'should allow to set a default through a lambda' do
|
223
|
+
@klass = disposable_song_class do
|
224
|
+
assignable_values_for :genre, :default => lambda { 'pop' } do
|
225
|
+
%w[pop rock]
|
226
|
+
end
|
227
|
+
end
|
228
|
+
@klass.new.genre.should == 'pop'
|
229
|
+
end
|
230
|
+
|
231
|
+
it 'should evaluate a lambda default in the context of the record instance' do
|
232
|
+
@klass = disposable_song_class do
|
233
|
+
assignable_values_for :genre, :default => lambda { default_genre } do
|
234
|
+
%w[pop rock]
|
235
|
+
end
|
236
|
+
def default_genre
|
237
|
+
'pop'
|
238
|
+
end
|
239
|
+
end
|
240
|
+
@klass.new.genre.should == 'pop'
|
241
|
+
end
|
242
|
+
|
243
|
+
end
|
244
|
+
|
245
|
+
context 'when generating methods to list assignable values' do
|
246
|
+
|
247
|
+
it 'should generate an instance method returning a list of assignable values' do
|
248
|
+
@klass = disposable_song_class do
|
249
|
+
assignable_values_for :genre do
|
250
|
+
%w[pop rock]
|
251
|
+
end
|
252
|
+
end
|
253
|
+
@klass.new.assignable_genres.should == %w[pop rock]
|
254
|
+
end
|
255
|
+
|
256
|
+
it "should define a method #human on strings in that list, which return up the value's' translation" do
|
257
|
+
@klass = disposable_song_class do
|
258
|
+
assignable_values_for :genre do
|
259
|
+
%w[pop rock]
|
260
|
+
end
|
261
|
+
end
|
262
|
+
@klass.new.assignable_genres.collect(&:human).should == ['Pop music', 'Rock music']
|
263
|
+
end
|
264
|
+
|
265
|
+
it 'should use String#humanize as a default translation' do
|
266
|
+
@klass = disposable_song_class do
|
267
|
+
assignable_values_for :genre do
|
268
|
+
%w[electronic]
|
269
|
+
end
|
270
|
+
end
|
271
|
+
@klass.new.assignable_genres.collect(&:human).should == ['Electronic']
|
272
|
+
end
|
273
|
+
|
274
|
+
it 'should not define a method #human on values that are not strings' do
|
275
|
+
@klass = disposable_song_class do
|
276
|
+
assignable_values_for :year do
|
277
|
+
[1999, 2000, 2001]
|
278
|
+
end
|
279
|
+
end
|
280
|
+
years = @klass.new.assignable_years
|
281
|
+
years.should == [1999, 2000, 2001]
|
282
|
+
years.first.should_not respond_to(:human)
|
283
|
+
end
|
284
|
+
|
285
|
+
it 'should call #to_a on the list of assignable values, allowing ranges and scopes to be passed as allowed value descriptors' do
|
286
|
+
@klass = disposable_song_class do
|
287
|
+
assignable_values_for :year do
|
288
|
+
1999..2001
|
289
|
+
end
|
290
|
+
end
|
291
|
+
@klass.new.assignable_years.should == [1999, 2000, 2001]
|
292
|
+
end
|
293
|
+
|
294
|
+
it 'should evaluate the value block in the context of the record instance' do
|
295
|
+
@klass = disposable_song_class do
|
296
|
+
assignable_values_for :genre do
|
297
|
+
genres
|
298
|
+
end
|
299
|
+
def genres
|
300
|
+
%w[pop rock]
|
301
|
+
end
|
302
|
+
end
|
303
|
+
@klass.new.assignable_genres.should == %w[pop rock]
|
304
|
+
end
|
305
|
+
|
306
|
+
it 'should include a previously saved value, even if is no longer allowed' do
|
307
|
+
@klass = disposable_song_class do
|
308
|
+
assignable_values_for :genre do
|
309
|
+
%w[pop rock]
|
310
|
+
end
|
311
|
+
end
|
312
|
+
record = @klass.new(:genre => 'ballad')
|
313
|
+
record.save!(:validate => false)
|
314
|
+
record.assignable_genres.should =~ %w[pop rock ballad]
|
315
|
+
end
|
316
|
+
|
317
|
+
context 'with :through option' do
|
318
|
+
|
319
|
+
it 'should retrieve assignable values from the given method' do
|
320
|
+
@klass = disposable_song_class do
|
321
|
+
assignable_values_for :genre, :through => :delegate
|
322
|
+
def delegate
|
323
|
+
@delegate ||= 'delegate'
|
324
|
+
end
|
325
|
+
end
|
326
|
+
record = @klass.new
|
327
|
+
record.delegate.should_receive(:assignable_song_genres).and_return %w[pop rock]
|
328
|
+
record.assignable_genres.should == %w[pop rock]
|
329
|
+
end
|
330
|
+
|
331
|
+
it "should pass the record to the given method if the delegate's query method takes an argument" do
|
332
|
+
delegate = Object.new
|
333
|
+
def delegate.assignable_song_genres(record)
|
334
|
+
record_received(record)
|
335
|
+
%w[pop rock]
|
336
|
+
end
|
337
|
+
@klass = disposable_song_class do
|
338
|
+
assignable_values_for :genre, :through => :delegate
|
339
|
+
define_method :delegate do
|
340
|
+
delegate
|
341
|
+
end
|
342
|
+
end
|
343
|
+
record = @klass.new
|
344
|
+
delegate.should_receive(:record_received).with(record)
|
345
|
+
record.assignable_genres.should == %w[pop rock]
|
346
|
+
end
|
347
|
+
|
348
|
+
it 'should raise an error if the given method returns nil' do
|
349
|
+
@klass = disposable_song_class do
|
350
|
+
assignable_values_for :genre, :through => :delegate
|
351
|
+
def delegate
|
352
|
+
nil
|
353
|
+
end
|
354
|
+
end
|
355
|
+
expect { @klass.new.assignable_genres }.to raise_error(AssignableValues::DelegateUnavailable)
|
356
|
+
end
|
357
|
+
|
358
|
+
end
|
359
|
+
|
360
|
+
end
|
361
|
+
|
362
|
+
end
|
363
|
+
|
364
|
+
#describe '.authorize_values_for' do
|
365
|
+
#
|
366
|
+
# it 'should be a shortcut for .assignable_values_for :attribute, :through => :power' do
|
367
|
+
# @klass = disposable_song_class
|
368
|
+
# @klass.should_receive(:assignable_values_for).with(:attribute, :option => 'option', :through => :power)
|
369
|
+
# @klass.class_eval do
|
370
|
+
# authorize_values_for :attribute, :option => 'option'
|
371
|
+
# end
|
372
|
+
# end
|
373
|
+
#
|
374
|
+
# it 'should generate a getter and setter for a @power field' do
|
375
|
+
# @klass = disposable_song_class do
|
376
|
+
# authorize_values_for :attribute
|
377
|
+
# end
|
378
|
+
# song = @klass.new
|
379
|
+
# song.should respond_to(:power)
|
380
|
+
# song.should respond_to(:power=)
|
381
|
+
# end
|
382
|
+
#
|
383
|
+
#end
|
384
|
+
|
385
|
+
end
|
data/spec/rcov.opts
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
$: << File.join(File.dirname(__FILE__), "/../lib" )
|
2
|
+
|
3
|
+
# Set the default environment to sqlite3's in_memory database
|
4
|
+
ENV['RAILS_ENV'] ||= 'in_memory'
|
5
|
+
ENV['RAILS_ROOT'] = 'app_root'
|
6
|
+
|
7
|
+
# Load the Rails environment and testing framework
|
8
|
+
require "#{File.dirname(__FILE__)}/app_root/config/environment"
|
9
|
+
require "#{File.dirname(__FILE__)}/../lib/assignable_values"
|
10
|
+
require 'rspec/rails'
|
11
|
+
|
12
|
+
# Undo changes to RAILS_ENV
|
13
|
+
silence_warnings {RAILS_ENV = ENV['RAILS_ENV']}
|
14
|
+
|
15
|
+
# Run the migrations
|
16
|
+
ActiveRecord::Migrator.migrate("#{Rails.root}/db/migrate")
|
17
|
+
|
18
|
+
RSpec.configure do |config|
|
19
|
+
config.use_transactional_fixtures = true
|
20
|
+
config.use_instantiated_fixtures = false
|
21
|
+
end
|
metadata
ADDED
@@ -0,0 +1,165 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: assignable_values
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 0
|
10
|
+
version: 0.1.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Henning Koch
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2012-02-20 00:00:00 +01:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ~>
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
hash: 5
|
29
|
+
segments:
|
30
|
+
- 3
|
31
|
+
- 1
|
32
|
+
version: "3.1"
|
33
|
+
name: rails
|
34
|
+
version_requirements: *id001
|
35
|
+
type: :development
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ~>
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
hash: 19
|
44
|
+
segments:
|
45
|
+
- 2
|
46
|
+
- 8
|
47
|
+
version: "2.8"
|
48
|
+
name: rspec
|
49
|
+
version_requirements: *id002
|
50
|
+
type: :development
|
51
|
+
- !ruby/object:Gem::Dependency
|
52
|
+
prerelease: false
|
53
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
54
|
+
none: false
|
55
|
+
requirements:
|
56
|
+
- - ~>
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
hash: 19
|
59
|
+
segments:
|
60
|
+
- 2
|
61
|
+
- 8
|
62
|
+
version: "2.8"
|
63
|
+
name: rspec-rails
|
64
|
+
version_requirements: *id003
|
65
|
+
type: :development
|
66
|
+
- !ruby/object:Gem::Dependency
|
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
|
+
name: sqlite3
|
78
|
+
version_requirements: *id004
|
79
|
+
type: :development
|
80
|
+
description: Restrict the values assignable to ActiveRecord attributes or associations. Or enums on steroids.
|
81
|
+
email: henning.koch@makandra.de
|
82
|
+
executables: []
|
83
|
+
|
84
|
+
extensions: []
|
85
|
+
|
86
|
+
extra_rdoc_files: []
|
87
|
+
|
88
|
+
files:
|
89
|
+
- .gitignore
|
90
|
+
- .rspec
|
91
|
+
- Gemfile
|
92
|
+
- README.md
|
93
|
+
- Rakefile
|
94
|
+
- assignable_values.gemspec
|
95
|
+
- lib/assignable_values.rb
|
96
|
+
- lib/assignable_values/active_record.rb
|
97
|
+
- lib/assignable_values/active_record/restriction/base.rb
|
98
|
+
- lib/assignable_values/active_record/restriction/belongs_to_association.rb
|
99
|
+
- lib/assignable_values/active_record/restriction/scalar_attribute.rb
|
100
|
+
- lib/assignable_values/errors.rb
|
101
|
+
- lib/assignable_values/version.rb
|
102
|
+
- spec/app_root/.gitignore
|
103
|
+
- spec/app_root/app/controllers/application_controller.rb
|
104
|
+
- spec/app_root/app/models/artist.rb
|
105
|
+
- spec/app_root/app/models/song.rb
|
106
|
+
- spec/app_root/config/application.rb
|
107
|
+
- spec/app_root/config/boot.rb
|
108
|
+
- spec/app_root/config/database.yml
|
109
|
+
- spec/app_root/config/environment.rb
|
110
|
+
- spec/app_root/config/environments/in_memory.rb
|
111
|
+
- spec/app_root/config/environments/mysql.rb
|
112
|
+
- spec/app_root/config/environments/postgresql.rb
|
113
|
+
- spec/app_root/config/environments/sqlite.rb
|
114
|
+
- spec/app_root/config/environments/sqlite3.rb
|
115
|
+
- spec/app_root/config/environments/test.rb
|
116
|
+
- spec/app_root/config/initializers/backtrace_silencers.rb
|
117
|
+
- spec/app_root/config/initializers/inflections.rb
|
118
|
+
- spec/app_root/config/initializers/mime_types.rb
|
119
|
+
- spec/app_root/config/initializers/secret_token.rb
|
120
|
+
- spec/app_root/config/initializers/session_store.rb
|
121
|
+
- spec/app_root/config/locales/en.yml
|
122
|
+
- spec/app_root/config/routes.rb
|
123
|
+
- spec/app_root/db/migrate/001_create_artists.rb
|
124
|
+
- spec/app_root/db/migrate/002_create_songs.rb
|
125
|
+
- spec/app_root/lib/tasks/.gitkeep
|
126
|
+
- spec/app_root/script/rails
|
127
|
+
- spec/assignable_values/active_record_spec.rb
|
128
|
+
- spec/rcov.opts
|
129
|
+
- spec/spec_helper.rb
|
130
|
+
has_rdoc: true
|
131
|
+
homepage: https://github.com/makandra/assignable_values
|
132
|
+
licenses: []
|
133
|
+
|
134
|
+
post_install_message:
|
135
|
+
rdoc_options: []
|
136
|
+
|
137
|
+
require_paths:
|
138
|
+
- lib
|
139
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
140
|
+
none: false
|
141
|
+
requirements:
|
142
|
+
- - ">="
|
143
|
+
- !ruby/object:Gem::Version
|
144
|
+
hash: 3
|
145
|
+
segments:
|
146
|
+
- 0
|
147
|
+
version: "0"
|
148
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
149
|
+
none: false
|
150
|
+
requirements:
|
151
|
+
- - ">="
|
152
|
+
- !ruby/object:Gem::Version
|
153
|
+
hash: 3
|
154
|
+
segments:
|
155
|
+
- 0
|
156
|
+
version: "0"
|
157
|
+
requirements: []
|
158
|
+
|
159
|
+
rubyforge_project:
|
160
|
+
rubygems_version: 1.3.9.3
|
161
|
+
signing_key:
|
162
|
+
specification_version: 3
|
163
|
+
summary: Restrict the values assignable to ActiveRecord attributes or associations. Or enums on steroids.
|
164
|
+
test_files: []
|
165
|
+
|