domain_neutral 1.0.0
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.
- 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
|