domain_neutral 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/Rakefile +37 -0
- data/app/models/domain_neutral/descriptor.rb +55 -0
- data/config/routes.rb +2 -0
- data/db/migrate/20160531124022_create_domain_neutral_descriptors.rb +20 -0
- data/lib/domain_neutral/association.rb +21 -0
- data/lib/domain_neutral/engine.rb +5 -0
- data/lib/domain_neutral/railtie.rb +24 -0
- data/lib/domain_neutral/seed.rb +8 -0
- data/lib/domain_neutral/seeder/descriptors.rb +195 -0
- data/lib/domain_neutral/symbolized_class.rb +170 -0
- data/lib/domain_neutral/version.rb +3 -0
- data/lib/domain_neutral.rb +18 -0
- data/lib/tasks/domain_neutral_tasks.rake +10 -0
- data/test/domain_neutral/symbolized_class_test.rb +162 -0
- data/test/domain_neutral_test.rb +7 -0
- data/test/dummy/README.rdoc +28 -0
- data/test/dummy/Rakefile +6 -0
- data/test/dummy/app/assets/javascripts/application.js +13 -0
- data/test/dummy/app/assets/stylesheets/application.css +15 -0
- data/test/dummy/app/controllers/application_controller.rb +5 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/models/role.rb +2 -0
- data/test/dummy/app/models/user.rb +3 -0
- data/test/dummy/app/views/layouts/application.html.erb +13 -0
- data/test/dummy/bin/bundle +3 -0
- data/test/dummy/bin/rails +4 -0
- data/test/dummy/bin/rake +4 -0
- data/test/dummy/bin/setup +29 -0
- data/test/dummy/config/application.rb +33 -0
- data/test/dummy/config/boot.rb +5 -0
- data/test/dummy/config/database.yml +25 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +41 -0
- data/test/dummy/config/environments/production.rb +79 -0
- data/test/dummy/config/environments/test.rb +44 -0
- data/test/dummy/config/initializers/assets.rb +11 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/cookies_serializer.rb +3 -0
- data/test/dummy/config/initializers/domain_neutral.rb +5 -0
- data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/test/dummy/config/initializers/inflections.rb +16 -0
- data/test/dummy/config/initializers/mime_types.rb +4 -0
- data/test/dummy/config/initializers/session_store.rb +3 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +13 -0
- data/test/dummy/config/locales/nb.yml +9 -0
- data/test/dummy/config/routes.rb +56 -0
- data/test/dummy/config/secrets.yml +22 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/db/development.sqlite3 +0 -0
- data/test/dummy/db/migrate/20160601072742_create_domain_neutral_descriptors.domain_neutral.rb +21 -0
- data/test/dummy/db/migrate/20160607100350_create_users.rb +10 -0
- data/test/dummy/db/schema.rb +42 -0
- data/test/dummy/db/test.sqlite3 +0 -0
- data/test/dummy/log/development.log +350 -0
- data/test/dummy/log/test.log +5133 -0
- data/test/dummy/public/404.html +67 -0
- data/test/dummy/public/422.html +67 -0
- data/test/dummy/public/500.html +66 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/tmp/cache/36E/B10/Role%2Fmap +1 -0
- data/test/dummy/tmp/cache/657/A10/Role%2Fuser_admin +1 -0
- data/test/fixtures/domain_neutral/descriptors.yml +14 -0
- data/test/fixtures/users.yml +9 -0
- data/test/integration/navigation_test.rb +8 -0
- data/test/models/domain_neutral/descriptor_test.rb +111 -0
- data/test/test_helper.rb +22 -0
- data/test/test_helpers/caching.rb +57 -0
- metadata +198 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 54827517aef3ef85bc7fc7611cc8e6e8eb2ca76d
|
4
|
+
data.tar.gz: 4daf91bc5ef08666fb404936acf55b862392a444
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 8a63fe76ae198a714472a9abb3414000ef66f9243369b8095c483ac0c70a4cf3f0b4fdc65a795a0d198027eb2390622a3a6b1a88a2d9a57d7ca9f1642309b8f3
|
7
|
+
data.tar.gz: f83b2574943c1a25f9abb0efcb74c8b1ad8c3b44b55cbfdf8d43f57eba69a9134348f354a4b30a99df93e131adc1281dcc00bfc4167eda3a4ea5dea29d61c0b4
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2016 knut
|
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/Rakefile
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'rdoc/task'
|
8
|
+
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
11
|
+
rdoc.title = 'DomainNeutral'
|
12
|
+
rdoc.options << '--line-numbers'
|
13
|
+
rdoc.rdoc_files.include('README.rdoc')
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
|
+
end
|
16
|
+
|
17
|
+
APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
|
18
|
+
load 'rails/tasks/engine.rake'
|
19
|
+
|
20
|
+
|
21
|
+
load 'rails/tasks/statistics.rake'
|
22
|
+
|
23
|
+
|
24
|
+
|
25
|
+
Bundler::GemHelper.install_tasks
|
26
|
+
|
27
|
+
require 'rake/testtask'
|
28
|
+
|
29
|
+
Rake::TestTask.new(:test) do |t|
|
30
|
+
t.libs << 'lib'
|
31
|
+
t.libs << 'test'
|
32
|
+
t.pattern = 'test/**/*_test.rb'
|
33
|
+
t.verbose = false
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
task default: :test
|
@@ -0,0 +1,55 @@
|
|
1
|
+
#require 'descriptor_model/symbolized_class'
|
2
|
+
module DomainNeutral
|
3
|
+
class Descriptor < ActiveRecord::Base
|
4
|
+
include SymbolizedClass
|
5
|
+
include Comparable
|
6
|
+
# Enable caching by default
|
7
|
+
|
8
|
+
belongs_to :parent, :polymorphic => true
|
9
|
+
validates_presence_of :name, :symbol
|
10
|
+
validates_uniqueness_of :symbol, scope: :type
|
11
|
+
|
12
|
+
class << self
|
13
|
+
def symbols
|
14
|
+
@symbols ||= begin
|
15
|
+
# ensure that we really have some items stored. This method may be called before fixtures are loaded in test environment
|
16
|
+
s = all.map(&:symbol)
|
17
|
+
s.size.zero? ? nil : s
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def <=>(other)
|
23
|
+
index <=> other.index
|
24
|
+
end
|
25
|
+
|
26
|
+
def ==(other)
|
27
|
+
self.class == other.class && symbol == other.symbol
|
28
|
+
end
|
29
|
+
|
30
|
+
def !=(other)
|
31
|
+
self.class == other.class && symbol != other.symbol
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_i
|
35
|
+
id
|
36
|
+
end
|
37
|
+
|
38
|
+
def name
|
39
|
+
t :name
|
40
|
+
end
|
41
|
+
|
42
|
+
def description
|
43
|
+
t :description
|
44
|
+
end
|
45
|
+
|
46
|
+
protected
|
47
|
+
def i18n_scope
|
48
|
+
@i18n_scope ||= [:descriptors, self.class.to_s.underscore, to_sym ]
|
49
|
+
end
|
50
|
+
|
51
|
+
def t(attribute)
|
52
|
+
I18n.t( attribute, scope: i18n_scope, default: self[attribute] || '') # || '' here is used to fix validation
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
data/config/routes.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
class CreateDomainNeutralDescriptors < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
table_name = DomainNeutral::Descriptor.table_name
|
4
|
+
create_table table_name, :force => true do |t|
|
5
|
+
t.string "type"
|
6
|
+
t.string "parent_type"
|
7
|
+
t.integer "parent_id"
|
8
|
+
t.string "symbol"
|
9
|
+
t.string "name"
|
10
|
+
t.text "description"
|
11
|
+
t.integer "index"
|
12
|
+
t.integer "value"
|
13
|
+
t.timestamps null: false
|
14
|
+
end
|
15
|
+
|
16
|
+
add_index table_name, ["type", "symbol"], name: "index_#{table_name}_on_type_and_symbol"
|
17
|
+
add_index table_name, ["type", "index"], name: "index_#{table_name}_on_type_and_index"
|
18
|
+
add_index table_name, ["parent_type", "parent_id"], name: "index_#{table_name}_on_parent_type_and_parent_id"
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module DomainNeutral
|
2
|
+
module Association
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
module ClassMethods
|
6
|
+
def has_descriptor(name, options = {})
|
7
|
+
# TODO: add delegate method
|
8
|
+
belongs_to name, options
|
9
|
+
r = reflect_on_association(name)
|
10
|
+
class_eval <<-CODE, __FILE__, __LINE__
|
11
|
+
def #{name}
|
12
|
+
#{r.klass}.find(#{r.foreign_key})
|
13
|
+
end
|
14
|
+
CODE
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
class ActiveRecord::Base
|
20
|
+
include DomainNeutral::Association
|
21
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require "rails"
|
2
|
+
|
3
|
+
module DomainNeutral
|
4
|
+
# = Domain Neutral Railtie
|
5
|
+
class Railtie < Rails::Railtie
|
6
|
+
config.domain_neutral = ActiveSupport::OrderedOptions.new
|
7
|
+
config.domain_neutral.seed = ActiveSupport::OrderedOptions.new
|
8
|
+
|
9
|
+
|
10
|
+
config.domain_neutral.table_prefix = 'domain_neutral' # Table prefix
|
11
|
+
config.domain_neutral.cache = true # Use cache
|
12
|
+
config.domain_neutral.seed.master_locale = :en # Default master data locale. Language used for seeding
|
13
|
+
config.domain_neutral.seed.locale_alternatives = [] # Alternative locale data. These will be parsed and checked for consistency with the master
|
14
|
+
config.domain_neutral.seed.verbose = true # Display progress when seeding
|
15
|
+
config.domain_neutral.seed.create_fixtures = true # Create fixtures from master file
|
16
|
+
|
17
|
+
initializer 'domain_neutral.configure' do |app|
|
18
|
+
DomainNeutral::Descriptor.table_name = app.config.domain_neutral.table_prefix.blank? ?
|
19
|
+
'descriptors' : "#{app.config.domain_neutral.prefix}_#{table_name}"
|
20
|
+
DomainNeutral::Descriptor.caching_enabled = app.config.domain_neutral.cache
|
21
|
+
DomainNeutral.seed_options = config.domain_neutral.seed
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,195 @@
|
|
1
|
+
# Seeds descriptors from yml file, using norwegian as default
|
2
|
+
module DomainNeutral
|
3
|
+
module Seeder
|
4
|
+
class Descriptors
|
5
|
+
def self.seed(context)
|
6
|
+
seeder = new(context)
|
7
|
+
seeder.setup
|
8
|
+
seeder.load_master_data
|
9
|
+
seeder.validate_master
|
10
|
+
seeder.validate_locale_alternatives
|
11
|
+
seeder.create_fixtures_file
|
12
|
+
seeder.seed_parents
|
13
|
+
seeder.seed_everything_else
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(context)
|
17
|
+
@context = context
|
18
|
+
end
|
19
|
+
|
20
|
+
def seed_options
|
21
|
+
DomainNeutral.seed_options
|
22
|
+
end
|
23
|
+
|
24
|
+
delegate :verbose, :master_locale, :locale_alternatives, :create_fixtures, to: :seed_options
|
25
|
+
|
26
|
+
def setup
|
27
|
+
if DomainNeutral.rails_generation < 4
|
28
|
+
Descriptor.attr_accessible :name, :description, :index, :externalid, :parent, :value
|
29
|
+
end
|
30
|
+
Descriptor.enable_caching false
|
31
|
+
end
|
32
|
+
|
33
|
+
attr_reader :master
|
34
|
+
|
35
|
+
# Load master descriptors from yaml file
|
36
|
+
def load_master_data
|
37
|
+
#@master = load_yaml(DomainNeutral.master_locale, :master)
|
38
|
+
@master = load_yaml(master_locale, :master)
|
39
|
+
end
|
40
|
+
|
41
|
+
def seed_parents
|
42
|
+
# Collect parents
|
43
|
+
log "Collect parents"
|
44
|
+
parents = []
|
45
|
+
master.each do |descriptor_set, descriptors|
|
46
|
+
parents << descriptors[:parent] if descriptors[:parent]
|
47
|
+
end
|
48
|
+
log "Parents collected: #{parents.inspect}"
|
49
|
+
# Seed parents
|
50
|
+
return if parents.empty?
|
51
|
+
log "Seed parents"
|
52
|
+
parents.map { |p| p.split('.')[0].underscore }.uniq.each do |parent|
|
53
|
+
klass = parent.classify.constantize
|
54
|
+
# Create only objects of Descriptor class
|
55
|
+
if klass.new.is_a?( Descriptor) && section = master.delete(parent)
|
56
|
+
seed_descriptor_set parent, section
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def seed_everything_else
|
62
|
+
# seed rest
|
63
|
+
master.each do |descriptor_set, descriptors|
|
64
|
+
seed_descriptor_set descriptor_set, descriptors
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Validate descriptors in master yaml file
|
69
|
+
def validate_master
|
70
|
+
log 'Validating master locale file'
|
71
|
+
undefined = []
|
72
|
+
master.each do |descriptor_set, descriptors|
|
73
|
+
descriptors.reject { |k,v| k == 'parent' }.each do |symbol, descriptor|
|
74
|
+
unless descriptor[:name]
|
75
|
+
undefined << "Name not defined for #{descriptor_set}"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
abort undefined if undefined.size > 0
|
80
|
+
end
|
81
|
+
|
82
|
+
def validate_locale_alternatives
|
83
|
+
undefined = []
|
84
|
+
#DomainNeutral.locale_alternatives.each do |locale|
|
85
|
+
locale_alternatives.each do |locale|
|
86
|
+
log "Validating alternative locale file: #{locale}"
|
87
|
+
loc_descriptors = load_yaml(locale, 'locale_alternative')
|
88
|
+
master.each do |descriptor_set, descriptors|
|
89
|
+
unless loc_descriptors[descriptor_set]
|
90
|
+
undefined << "Locale: #{locale}. Keys not defined for '#{descriptor_set}'"
|
91
|
+
next
|
92
|
+
end
|
93
|
+
descriptors.keys.reject{ |k| k == 'parent' }.each do |descriptor|
|
94
|
+
unless loc_descriptors[descriptor_set][descriptor]
|
95
|
+
undefined << "Locale: #{locale}. Key not defined for '#{descriptor}' in '#{descriptor_set}'"
|
96
|
+
next
|
97
|
+
end
|
98
|
+
%w(name description).each do |key|
|
99
|
+
if descriptors[descriptor][key] && !loc_descriptors[descriptor_set][descriptor][key]
|
100
|
+
undefined << "Locale: #{locale}. Attribute '#{key}' not defined for '#{descriptor}' in '#{descriptor_set}'"
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
abort undefined if undefined.size > 0
|
107
|
+
end
|
108
|
+
|
109
|
+
def create_fixtures_file
|
110
|
+
return unless create_fixtures
|
111
|
+
fixtures = {}
|
112
|
+
master.each do |descriptor_set, descriptors|
|
113
|
+
base = {type: descriptor_set.classify}
|
114
|
+
if parent = descriptors[:parent]
|
115
|
+
abort "Parent for fixtures not yet implemented.", descriptors.inspect
|
116
|
+
base[:parent] = parent
|
117
|
+
end
|
118
|
+
descriptors.reject { |k,v| k == 'parent' }.each do |symbol, descriptor|
|
119
|
+
fixtures["#{descriptor_set}_#{symbol}"] = descriptor.merge(base.merge(symbol: symbol)).to_hash
|
120
|
+
end
|
121
|
+
end
|
122
|
+
path = fixtures_path.join('domain_neutral', 'descriptors.yml')
|
123
|
+
FileUtils.mkdir_p path.dirname
|
124
|
+
File.open(path, 'w') do |f|
|
125
|
+
f.write fixtures.to_yaml
|
126
|
+
end
|
127
|
+
log "Seeders file created (#{path}):"
|
128
|
+
end
|
129
|
+
|
130
|
+
private
|
131
|
+
def load_yaml(locale, type)
|
132
|
+
file = locate_locale_file(locale, type)
|
133
|
+
yml = HashWithIndifferentAccess.new YAML.load_file(file.to_s)
|
134
|
+
yml[locale.to_sym][:descriptors]
|
135
|
+
end
|
136
|
+
|
137
|
+
def locate_locale_file(locale, type)
|
138
|
+
alternative_locations = [
|
139
|
+
Rails.root.join('config', 'locales', locale.to_s, "descriptors.yml"),
|
140
|
+
Rails.root.join('config', 'locales', "#{locale}.descriptors.yml"),
|
141
|
+
Rails.root.join('config', 'locales', "#{locale}.yml"),
|
142
|
+
Rails.root.join('config', 'locales', "descriptors.yml")
|
143
|
+
]
|
144
|
+
unless file = alternative_locations.select { |fname| fname.exist? }.first
|
145
|
+
abort "Could not load #{type} file for descriptors. Expected to find any of the following files:\n" +
|
146
|
+
alternative_locations.map { |fname| "\t'#{fname}'" }.join("\n")
|
147
|
+
end
|
148
|
+
file
|
149
|
+
end
|
150
|
+
|
151
|
+
def seed_descriptor_set(descriptor_set, descriptors, default_parent = nil)
|
152
|
+
log "seed_descriptor_set: #{descriptor_set.inspect}, descriptors: #{descriptors.inspect}"
|
153
|
+
klass = descriptor_set.classify.constantize
|
154
|
+
default_parent = descriptors.delete('parent')
|
155
|
+
descriptors.each do |descriptor, keypairs|
|
156
|
+
log "descriptor: #{descriptor.inspect}, keypairs: #{keypairs.inspect}"
|
157
|
+
parent = keypairs['parent'] || default_parent || 'nil'
|
158
|
+
unless object = klass[descriptor]
|
159
|
+
if DomainNeutral.rails_generation < 4
|
160
|
+
klass.create! keypairs.merge(symbol: descriptor, parent: eval(parent)), without_protection: true
|
161
|
+
else
|
162
|
+
klass.create! keypairs.merge(symbol: descriptor, parent: eval(parent))
|
163
|
+
end
|
164
|
+
else
|
165
|
+
if DomainNeutral.rails_generation < 4
|
166
|
+
object.update_attributes(keypairs.merge(parent: eval(parent)), without_protection: true)
|
167
|
+
else
|
168
|
+
object.update_attributes(keypairs.merge(parent: eval(parent)))
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
def fixtures_path
|
175
|
+
if ENV['FIXTURES_PATH']
|
176
|
+
Rails.root.join(ENV['FIXTURES_PATH'])
|
177
|
+
else
|
178
|
+
Rails.root.join('test', 'fixtures')
|
179
|
+
end
|
180
|
+
end
|
181
|
+
def abort(*messages)
|
182
|
+
raise "#{self.class.name}: #{messages.join("\n")}"
|
183
|
+
end
|
184
|
+
|
185
|
+
def log(message)
|
186
|
+
# @context.say message if DomainNeutral.verbose_seed
|
187
|
+
if verbose
|
188
|
+
@messenger ||= @context.is_a?( ActiveRecord::Migration) ? :say : :puts
|
189
|
+
@context.send @messenger, message
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
@@ -0,0 +1,170 @@
|
|
1
|
+
module DomainNeutral
|
2
|
+
module SymbolizedClass
|
3
|
+
# = Domain Neutral SymbolizedClass
|
4
|
+
#
|
5
|
+
# This mudule implements two important features:
|
6
|
+
# * access (finding) to objects, using a symbol
|
7
|
+
# * cache of objects that imcludes this module, if enabled
|
8
|
+
#
|
9
|
+
# A requirement for including this module is that the class has an attribute named symbol.
|
10
|
+
#
|
11
|
+
# Usage:
|
12
|
+
#
|
13
|
+
# class MySymbolizedClass < ActiveRecord::Base
|
14
|
+
# include DomainNeutral::SymbolizedClass
|
15
|
+
# ...
|
16
|
+
# end
|
17
|
+
|
18
|
+
|
19
|
+
extend ActiveSupport::Concern
|
20
|
+
|
21
|
+
included do
|
22
|
+
class_attribute :caching_enabled
|
23
|
+
after_save :flush_cache, if: :caching_enabled
|
24
|
+
end
|
25
|
+
|
26
|
+
module ClassMethods
|
27
|
+
# Access descriptor by symbol, e.g.
|
28
|
+
#
|
29
|
+
# Role[:manager]
|
30
|
+
# => Manager role
|
31
|
+
# Same as Role.where(symbol: :manager).first
|
32
|
+
def [](symbol)
|
33
|
+
find_by_symbol(symbol)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Access like Role.manager
|
37
|
+
#
|
38
|
+
# Role.manager
|
39
|
+
# => Manager role
|
40
|
+
# Same as Role.where(symbol: :manager).first
|
41
|
+
def method_missing(method, *args)
|
42
|
+
begin
|
43
|
+
if method != :find_by_symbol
|
44
|
+
if obj = find_by_symbol(method)
|
45
|
+
redefine_method method do
|
46
|
+
find_by_symbol(method)
|
47
|
+
end
|
48
|
+
return obj
|
49
|
+
end
|
50
|
+
end
|
51
|
+
rescue
|
52
|
+
# Ignore errors, and call super
|
53
|
+
end
|
54
|
+
super
|
55
|
+
end
|
56
|
+
|
57
|
+
# Overrides find by using cache.
|
58
|
+
# The key in cache is [class_name, id] or ':class_name/:id', e.g. 'Role/1'
|
59
|
+
def find(id)
|
60
|
+
if caching_enabled
|
61
|
+
Rails.cache.fetch([name, id]) { super }
|
62
|
+
else
|
63
|
+
super
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Find object by symbol
|
68
|
+
#
|
69
|
+
# See also
|
70
|
+
# Descriptor[:symbol]
|
71
|
+
# Descriptor.symbol
|
72
|
+
def find_by_symbol(symbol)
|
73
|
+
if caching_enabled
|
74
|
+
Rails.cache.fetch([name, symbol.to_s]) do
|
75
|
+
where(symbol: symbol).first
|
76
|
+
end
|
77
|
+
else
|
78
|
+
where(symbol: symbol).first
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# Role.collection(:site_admin, :user_admin, :admin)
|
83
|
+
# => Role[] consisting of Role.site_admin, Role.user_admin, Role.admin
|
84
|
+
def collection(*syms)
|
85
|
+
syms.flatten.map { |s| self[s] }
|
86
|
+
end
|
87
|
+
|
88
|
+
|
89
|
+
# Turn cache on or off
|
90
|
+
# Calling enable_caching without parameter or true will turn on caching
|
91
|
+
# By default cache is off.
|
92
|
+
#
|
93
|
+
# Example - enabling caching
|
94
|
+
# class MySymbolizedClass < ActiveRecord::Base
|
95
|
+
# include DomainNeutral::SymbolizedClass
|
96
|
+
# enable_caching
|
97
|
+
# ...
|
98
|
+
# end
|
99
|
+
#
|
100
|
+
# Example - disable caching
|
101
|
+
# class MySymbolizedClass < ActiveRecord::Base
|
102
|
+
# include DomainNeutral::SymbolizedClass
|
103
|
+
# enable_caching false
|
104
|
+
# ...
|
105
|
+
# end
|
106
|
+
|
107
|
+
def enable_caching(*args)
|
108
|
+
self.caching_enabled = args.size > 0 ? args.first : true
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# Store symbol
|
113
|
+
def symbol=(name)
|
114
|
+
write_attribute(:symbol, name.to_s)
|
115
|
+
end
|
116
|
+
|
117
|
+
# Retrieve symbol
|
118
|
+
def symbol
|
119
|
+
@symbol ||= begin
|
120
|
+
s = read_attribute(:symbol)
|
121
|
+
s && s.to_sym
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def to_sym
|
126
|
+
symbol
|
127
|
+
end
|
128
|
+
|
129
|
+
# Role.admin.is_one_of?(:admin, :site_admin)
|
130
|
+
# => true
|
131
|
+
# Role.admin.is_one_of?(:site_admin, :user_admin)
|
132
|
+
# => false
|
133
|
+
def is_one_of?(*syms)
|
134
|
+
syms.flatten.include?(to_sym)
|
135
|
+
end
|
136
|
+
|
137
|
+
# Role.admin.is_none_of?(:site_admin, :user_admin)
|
138
|
+
# => true
|
139
|
+
# Role.admin.is_none_of?(:admin, :site_admin)
|
140
|
+
# => false
|
141
|
+
def is_none_of?(*syms)
|
142
|
+
!syms.flatten.include?(to_sym)
|
143
|
+
end
|
144
|
+
|
145
|
+
# Allow to test for a specific role or similar like Role.accountant?
|
146
|
+
def method_missing(method, *args)
|
147
|
+
if method.to_s =~ /^(\w+)\?$/
|
148
|
+
v = self.class.find_by_symbol($1)
|
149
|
+
raise NameError unless v
|
150
|
+
other = v.to_sym
|
151
|
+
self.class.class_eval { define_method(method) { self.to_sym == other }}
|
152
|
+
return self.to_sym == other
|
153
|
+
end
|
154
|
+
super
|
155
|
+
end
|
156
|
+
|
157
|
+
def respond_to?(meth, include_private = false) # :nodoc
|
158
|
+
if m = /^(\w+)\?$/.match(meth.to_s)
|
159
|
+
return true if self.to_sym == m[1].to_sym
|
160
|
+
end
|
161
|
+
super
|
162
|
+
end
|
163
|
+
|
164
|
+
# Flushes cache if record is saved
|
165
|
+
def flush_cache
|
166
|
+
Rails.cache.delete([self.class.name, symbol_was.to_s])
|
167
|
+
Rails.cache.delete([self.class.name, id])
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'domain_neutral/symbolized_class'
|
2
|
+
require "domain_neutral/engine"
|
3
|
+
require 'domain_neutral/railtie' if defined?(Rails)
|
4
|
+
require 'domain_neutral/association'
|
5
|
+
module DomainNeutral
|
6
|
+
mattr_accessor :seed_options
|
7
|
+
# Identifies the Rails generation version
|
8
|
+
# Rails.version "4.2.1"
|
9
|
+
# => 4
|
10
|
+
def self.rails_generation
|
11
|
+
@@__generation ||= Rails.version.split('.')[0].to_i
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.seed
|
15
|
+
require 'domain_neutral/seed'
|
16
|
+
Seed.new
|
17
|
+
end
|
18
|
+
end
|