wakari 0.0.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/.gitignore +5 -0
- data/CHANGELOG +0 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +29 -0
- data/Rakefile +1 -0
- data/app/controllers/wakari_controller.rb +18 -0
- data/lib/wakari.rb +11 -0
- data/lib/wakari/bundle.rb +136 -0
- data/lib/wakari/controllers/action_controller_extension.rb +16 -0
- data/lib/wakari/controllers/controller.rb +14 -0
- data/lib/wakari/engine.rb +4 -0
- data/lib/wakari/helpers/action_view_extension.rb +10 -0
- data/lib/wakari/models/active_record_extension.rb +15 -0
- data/lib/wakari/models/content.rb +7 -0
- data/lib/wakari/models/content/model.rb +45 -0
- data/lib/wakari/models/meta.rb +31 -0
- data/lib/wakari/models/meta/model.rb +22 -0
- data/lib/wakari/models/meta/reference.rb +14 -0
- data/lib/wakari/models/proxy.rb +42 -0
- data/lib/wakari/models/proxy/model.rb +113 -0
- data/lib/wakari/models/support/errors.rb +4 -0
- data/lib/wakari/models/support/naming.rb +53 -0
- data/lib/wakari/models/translation.rb +12 -0
- data/lib/wakari/models/translation/model.rb +223 -0
- data/lib/wakari/multilang.rb +14 -0
- data/lib/wakari/railtie.rb +20 -0
- data/lib/wakari/version.rb +3 -0
- data/wakari.gemspec +24 -0
- metadata +107 -0
data/.gitignore
ADDED
data/CHANGELOG
ADDED
File without changes
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Valery Kvon
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# Wakari
|
2
|
+
|
3
|
+
TODO: Write a gem description
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'wakari'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install wakari
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
TODO: Write usage instructions here
|
22
|
+
|
23
|
+
## Contributing
|
24
|
+
|
25
|
+
1. Fork it
|
26
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
27
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
28
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
29
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class WakariController < ApplicationController
|
2
|
+
|
3
|
+
def self.controller_path
|
4
|
+
unless anonymous?
|
5
|
+
@controller_path ||= begin
|
6
|
+
model_name = name.sub(/Controller$/, '').singularize
|
7
|
+
model_name.constantize.model_name.collection.tap do |t|
|
8
|
+
t.instance_eval do
|
9
|
+
def include?(arg) # Rails hack! actionpack/lib/action_view/renderer/partial_renderer.rb
|
10
|
+
arg == "/" ? false : super
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
data/lib/wakari.rb
ADDED
@@ -0,0 +1,136 @@
|
|
1
|
+
require 'wakari/models/content'
|
2
|
+
require 'wakari/models/proxy'
|
3
|
+
require 'wakari/models/translation'
|
4
|
+
require 'wakari/models/meta'
|
5
|
+
|
6
|
+
module Wakari
|
7
|
+
|
8
|
+
class Bundle
|
9
|
+
|
10
|
+
attr_reader :content_class, :proxy_class, :translation_class, :meta_class, :options
|
11
|
+
|
12
|
+
def initialize(content_class, *args)
|
13
|
+
@content_class = content_class
|
14
|
+
@name = args.first
|
15
|
+
@options = args.extract_options!
|
16
|
+
end
|
17
|
+
|
18
|
+
def name
|
19
|
+
@name.to_s.underscore if @name
|
20
|
+
end
|
21
|
+
|
22
|
+
def translation_name
|
23
|
+
name||"translation"
|
24
|
+
end
|
25
|
+
|
26
|
+
def full_translation_name
|
27
|
+
[content_class.model_name.param_key, translation_name].join("_")
|
28
|
+
end
|
29
|
+
|
30
|
+
def translation_class
|
31
|
+
@translation_class ||= case options[:class_name]
|
32
|
+
when nil then
|
33
|
+
content_class.const_set(translation_name.classify, Class.new(Wakari::Translation::Base))
|
34
|
+
when String, Symbol then
|
35
|
+
options[:class_name].to_s.constantize
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def meta_class
|
40
|
+
@meta_class ||= case options[:meta]
|
41
|
+
when :text then
|
42
|
+
translation_class.const_set("Meta", Class.new(Wakari::Meta::Text))
|
43
|
+
when :string then
|
44
|
+
translation_class.const_set("Meta", Class.new(Wakari::Meta::String))
|
45
|
+
when String, Symbol then
|
46
|
+
options[:meta].to_s.constantize
|
47
|
+
else
|
48
|
+
raise "#{content_class.name}: Meta model expected for multilang definition#{" :" + name if name }."
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def proxy_class
|
53
|
+
if proxy_need?
|
54
|
+
@proxy_class ||= content_class.const_set("#{name.classify}Proxy", Class.new(Wakari::Proxy::Base))
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def prepare!
|
59
|
+
meta_class.send(:include, Wakari::Meta::Model)
|
60
|
+
meta_class.send(:acts_as_meta_class, *meta_class_args)
|
61
|
+
|
62
|
+
translation_class.send(:include, Wakari::Translation::Model)
|
63
|
+
translation_class.send(:acts_as_translation_class, *translation_class_args)
|
64
|
+
|
65
|
+
content_class.send(:include, Wakari::Content::Model)
|
66
|
+
content_class.send(:acts_as_content_class, *content_class_args)
|
67
|
+
|
68
|
+
if proxy_class
|
69
|
+
proxy_class.send(:include, Wakari::Proxy::Model)
|
70
|
+
proxy_class.send(:acts_as_proxy, *proxy_class_args)
|
71
|
+
content_class.class_eval <<-EOV
|
72
|
+
attr_accessible :#{name}
|
73
|
+
|
74
|
+
def #{name}
|
75
|
+
#{proxy_class}.new(self)
|
76
|
+
end
|
77
|
+
def #{name}=(*args)
|
78
|
+
#{proxy_class}.new(self).write(*args)
|
79
|
+
end
|
80
|
+
EOV
|
81
|
+
else
|
82
|
+
content_class.send(:include, Wakari::Proxy::Model) unless content_class < Wakari::Proxy::Model
|
83
|
+
content_class.send(:acts_as_proxy, *proxy_class_args)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
def proxy_need?
|
90
|
+
name.present?
|
91
|
+
end
|
92
|
+
|
93
|
+
def association_name
|
94
|
+
translation_name.pluralize.to_sym
|
95
|
+
end
|
96
|
+
|
97
|
+
def full_association_name
|
98
|
+
full_translation_name.pluralize.to_sym
|
99
|
+
end
|
100
|
+
|
101
|
+
def translation_class_options
|
102
|
+
options
|
103
|
+
end
|
104
|
+
|
105
|
+
def translation_class_args
|
106
|
+
[content_class, association_name, meta_class, full_association_name, translation_class_options]
|
107
|
+
end
|
108
|
+
|
109
|
+
def meta_class_options
|
110
|
+
options
|
111
|
+
end
|
112
|
+
|
113
|
+
def meta_class_args
|
114
|
+
[translation_class, full_association_name, meta_class_options]
|
115
|
+
end
|
116
|
+
|
117
|
+
def proxy_class_options
|
118
|
+
options
|
119
|
+
end
|
120
|
+
|
121
|
+
def proxy_class_args
|
122
|
+
[translation_class, association_name, proxy_class_options]
|
123
|
+
end
|
124
|
+
|
125
|
+
def content_class_options
|
126
|
+
options
|
127
|
+
end
|
128
|
+
|
129
|
+
def content_class_args
|
130
|
+
[translation_class, association_name, content_class_options]
|
131
|
+
end
|
132
|
+
|
133
|
+
end
|
134
|
+
|
135
|
+
|
136
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Wakari
|
2
|
+
module Content
|
3
|
+
module Model
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
end
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
|
11
|
+
def acts_as_content_class(translation_class, association_name, options)
|
12
|
+
has_many association_name, :class_name => translation_class.name, :inverse_of => :content, :order => :position, :autosave => true, :foreign_key => :content_id
|
13
|
+
default_scope { includes(association_name) }
|
14
|
+
end
|
15
|
+
|
16
|
+
def i18n_namespaced_scope(resource) #:nodoc:
|
17
|
+
:"#{self.i18n_scope}.#{resource}.#{self.model_name.i18n_key.to_s.gsub(/\//,".")}"
|
18
|
+
end
|
19
|
+
|
20
|
+
def i18n_default_scope(resource) #:nodoc:
|
21
|
+
:"#{self.i18n_scope}.#{resource}.#{self.model_name.i18n_key.to_s}"
|
22
|
+
end
|
23
|
+
|
24
|
+
def dom_class
|
25
|
+
model_name.param_key
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
def dom_id(prefix = nil)
|
31
|
+
[prefix, self.class.dom_class, to_key].compact.join("_")
|
32
|
+
end
|
33
|
+
|
34
|
+
def current_locale
|
35
|
+
I18n.locale.to_s
|
36
|
+
end
|
37
|
+
|
38
|
+
def current_lang
|
39
|
+
Gaigo::LANGS.get(current_locale)
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'wakari/models/meta/model'
|
2
|
+
require 'wakari/models/meta/reference'
|
3
|
+
|
4
|
+
module Wakari
|
5
|
+
module Meta
|
6
|
+
class String < ActiveRecord::Base
|
7
|
+
self.table_name = "meta_strings"
|
8
|
+
attr_accessible :value
|
9
|
+
validates_presence_of :value
|
10
|
+
end
|
11
|
+
|
12
|
+
class Text < ActiveRecord::Base
|
13
|
+
self.table_name = "meta_texts"
|
14
|
+
attr_accessible :value
|
15
|
+
validates_presence_of :value
|
16
|
+
end
|
17
|
+
|
18
|
+
# class StringReference < ActiveRecord::Base
|
19
|
+
# self.table_name = "references"
|
20
|
+
# attr_accessible :meta_id, :reference_id
|
21
|
+
# include Meta::Reference
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# class TextReference < ActiveRecord::Base
|
25
|
+
# self.table_name = "references"
|
26
|
+
# attr_accessible :meta_id, :reference_id
|
27
|
+
# include Meta::Reference
|
28
|
+
# end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Wakari
|
2
|
+
module Meta
|
3
|
+
|
4
|
+
module Model
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
end
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
def acts_as_meta_class(translation_class, full_association_name, options)
|
12
|
+
has_many full_association_name, :class_name => translation_class.name, :inverse_of => :meta, :order => :created_at, :foreign_key => :meta_id
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_s
|
17
|
+
value
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'wakari/models/proxy/model'
|
2
|
+
|
3
|
+
module Wakari
|
4
|
+
module Proxy
|
5
|
+
class Base
|
6
|
+
#include ActiveModel::AttributeMethods
|
7
|
+
include ActiveModel::MassAssignmentSecurity
|
8
|
+
attr_reader :content
|
9
|
+
delegate :current_locale, :current_lang, :to => :content
|
10
|
+
delegate :==, :nil?, :present?, :to => :current_translation
|
11
|
+
|
12
|
+
extend ActiveModel::Naming
|
13
|
+
|
14
|
+
def initialize(content)
|
15
|
+
@content = content
|
16
|
+
end
|
17
|
+
|
18
|
+
# def translations
|
19
|
+
# _translations_proc.call(content)
|
20
|
+
# end
|
21
|
+
|
22
|
+
def write(params={})
|
23
|
+
detect_current_translation.tap do |t|
|
24
|
+
t.attributes = params.delete_if {|k| k.to_s == "locale"}
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def inspect
|
29
|
+
translations.inspect
|
30
|
+
end
|
31
|
+
|
32
|
+
def method_missing(*args, &block)
|
33
|
+
current_translation.send(*args, &block)
|
34
|
+
end
|
35
|
+
|
36
|
+
def dom_id(prefix = nil)
|
37
|
+
[prefix, content.dom_id, translations.klass.dom_class].compact.join("_")
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
require 'wakari/models/support/errors'
|
2
|
+
|
3
|
+
module Wakari
|
4
|
+
module Proxy
|
5
|
+
module Model
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
included do
|
9
|
+
end
|
10
|
+
|
11
|
+
module ClassMethods
|
12
|
+
def acts_as_proxy(translation_class, association_name, options)
|
13
|
+
lang_methods = translation_class._locales.collect do |lang|
|
14
|
+
method = lang.to_method
|
15
|
+
class_eval <<-EOV
|
16
|
+
def #{method}
|
17
|
+
translation?(\"#{lang.code}\")
|
18
|
+
end
|
19
|
+
def #{method}=(params={})
|
20
|
+
detect_translation(\"#{lang.code}\").tap do |t|
|
21
|
+
t.attributes = params.delete_if {|k| k.to_s == \"locale\"}
|
22
|
+
end
|
23
|
+
end
|
24
|
+
EOV
|
25
|
+
method
|
26
|
+
end
|
27
|
+
attr_accessible *lang_methods
|
28
|
+
delegate *translation_class._meta_attributes, :to => :current_translation, :allow_nil => true
|
29
|
+
delegate *translation_class._meta_attributes.collect {|attribute| "#{attribute}="}, :to => :detect_current_translation, :allow_nil => true
|
30
|
+
|
31
|
+
if self < Wakari::Proxy::Base
|
32
|
+
define_method :translations do |*args|
|
33
|
+
content.send(association_name, *args)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def translations_attributes
|
41
|
+
Hash[translations.map do |t|
|
42
|
+
if (lang = t.lang) && (attributes = t.meta_attributes).present?
|
43
|
+
[lang.to_method, attributes]
|
44
|
+
end
|
45
|
+
end.compact]
|
46
|
+
end
|
47
|
+
|
48
|
+
def possible_langs
|
49
|
+
translations.klass._locales
|
50
|
+
end
|
51
|
+
|
52
|
+
def used_langs
|
53
|
+
translations.map {|t| t.lang}
|
54
|
+
end
|
55
|
+
|
56
|
+
def available_langs
|
57
|
+
possible_langs - used_langs
|
58
|
+
end
|
59
|
+
|
60
|
+
def default_translation
|
61
|
+
translations.min_by {|t| t.position}
|
62
|
+
end
|
63
|
+
|
64
|
+
def translation(locale)
|
65
|
+
find_translation(:locale => locale)
|
66
|
+
end
|
67
|
+
|
68
|
+
def translation?(locale)
|
69
|
+
begin
|
70
|
+
translation(locale)
|
71
|
+
rescue TranslationNotFound
|
72
|
+
nil
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def detect_translation(locale)
|
77
|
+
find_or_build_translation(:locale => locale)
|
78
|
+
end
|
79
|
+
|
80
|
+
def current_translation
|
81
|
+
translation?(current_locale)||translations.first
|
82
|
+
end
|
83
|
+
|
84
|
+
def detect_current_translation
|
85
|
+
detect_translation(current_locale)
|
86
|
+
end
|
87
|
+
|
88
|
+
def to_s(locale = nil)
|
89
|
+
(locale ? translation?(locale) : current_translation).try(:to_s)
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
def build_translation(params = {})
|
95
|
+
translations.build(params)
|
96
|
+
end
|
97
|
+
|
98
|
+
def find_or_build_translation(params = {})
|
99
|
+
begin
|
100
|
+
find_translation(params)
|
101
|
+
rescue TranslationNotFound
|
102
|
+
build_translation(params)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def find_translation(params = {})
|
107
|
+
translations.find {|t| t.match?(params)} || raise(TranslationNotFound, params)
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Wakari
|
2
|
+
module Support
|
3
|
+
module Naming
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
class Name < ActiveModel::Name
|
7
|
+
attr_reader :parent_class
|
8
|
+
|
9
|
+
def initialize(parent_class, klass, namespace = nil, name = nil)
|
10
|
+
super(klass, namespace, name)
|
11
|
+
@parent_class = parent_class
|
12
|
+
@clear_name = (Rails.version <= "3.2.8" ? (name||klass.name) : @name).sub(/^#{parent_class.name}\W*/,'')
|
13
|
+
@singular = _singularize(@clear_name)
|
14
|
+
@plural = ActiveSupport::Inflector.pluralize(@singular)
|
15
|
+
@element = ActiveSupport::Inflector.underscore(ActiveSupport::Inflector.demodulize(@clear_name))
|
16
|
+
@collection = @parent_class.model_name.collection + "/" + ActiveSupport::Inflector.tableize(@clear_name)
|
17
|
+
@param_key = (namespace ? _singularize(@unnamespaced) : @singular)
|
18
|
+
@i18n_key = (@parent_class.model_name.i18n_key.to_s + "/" + @clear_name.underscore).to_sym
|
19
|
+
@route_key = (namespace ? ActiveSupport::Inflector.pluralize(@param_key) : @plural.dup)
|
20
|
+
@singular_route_key = ActiveSupport::Inflector.singularize(@route_key)
|
21
|
+
@route_key << "_index" if @plural == @singular
|
22
|
+
end
|
23
|
+
|
24
|
+
def human(options = {})
|
25
|
+
try_human(options)
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
module ClassMethods
|
31
|
+
def parent_class
|
32
|
+
self.name.split("::")[0..-2].join("::").constantize
|
33
|
+
end
|
34
|
+
|
35
|
+
def _to_partial_path #:nodoc:
|
36
|
+
@_to_partial_path ||= begin
|
37
|
+
"#{model_name.collection}/#{model_name.element}".freeze
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def model_name
|
42
|
+
@_model_name ||= begin
|
43
|
+
namespace = self.parents.detect do |n|
|
44
|
+
n.respond_to?(:use_relative_model_naming?) && n.use_relative_model_naming?
|
45
|
+
end
|
46
|
+
Name.new(parent_class, self, namespace)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,223 @@
|
|
1
|
+
require 'wakari/models/support/naming'
|
2
|
+
|
3
|
+
module Wakari
|
4
|
+
module Translation
|
5
|
+
|
6
|
+
module Model
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
include Wakari::Support::Naming
|
9
|
+
|
10
|
+
included do
|
11
|
+
class_attribute :_meta_attributes unless defined?(_meta_attributes)
|
12
|
+
|
13
|
+
validates_presence_of :locale
|
14
|
+
validates_uniqueness_of :locale, :scope => [:type, :content_id]
|
15
|
+
before_save :commit_meta
|
16
|
+
after_destroy :clear_meta
|
17
|
+
validate do
|
18
|
+
unless meta_valid?
|
19
|
+
template_meta.errors.messages.each do |attribute, suberrors|
|
20
|
+
suberrors.each do |suberror|
|
21
|
+
errors.add(attribute, suberror)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
after_initialize do
|
27
|
+
template_meta
|
28
|
+
end
|
29
|
+
acts_as_list :scope => [:type, :content_id]
|
30
|
+
default_scope { includes(:meta) }
|
31
|
+
end
|
32
|
+
|
33
|
+
module ClassMethods
|
34
|
+
|
35
|
+
def dom_class
|
36
|
+
model_name.element
|
37
|
+
end
|
38
|
+
|
39
|
+
def acts_as_translation_class(content_class, association_name, meta_class, full_association_name, options)
|
40
|
+
has_locale! *Array.wrap(options[:locales])#, :accessible => false
|
41
|
+
|
42
|
+
belongs_to :content, :class_name => content_class.name, :inverse_of => association_name, :counter_cache => :"#{association_name}_count"
|
43
|
+
belongs_to :meta, :class_name => meta_class.name, :inverse_of => full_association_name, :counter_cache => :wakari_used
|
44
|
+
|
45
|
+
self._meta_attributes =
|
46
|
+
if meta_class < Wakari::Meta::Text || meta_class < Wakari::Meta::String
|
47
|
+
[:value]
|
48
|
+
else
|
49
|
+
Array.wrap(options[:attributes]).map {|a| a.to_sym}
|
50
|
+
end
|
51
|
+
|
52
|
+
attr_accessible *_meta_attributes, :content_id, :meta_id, :_destroy, :position
|
53
|
+
delegate *_meta_attributes, :to_s, :to => "(@_template_meta||meta)"
|
54
|
+
delegate *_meta_attributes.collect {|attribute| "#{attribute}="}, :to => :template_meta
|
55
|
+
|
56
|
+
(class << self; self; end).instance_eval do
|
57
|
+
define_method :i18n_inherited_namespaced_scope do |resource| #:nodoc:
|
58
|
+
:"#{content_class.i18n_namespaced_scope(resource)}.#{model_name.element}"
|
59
|
+
end
|
60
|
+
|
61
|
+
define_method :i18n_inherited_default_scope do |resource| #:nodoc:
|
62
|
+
:"#{content_class.i18n_default_scope(resource)}.#{model_name.element}"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
define_method :stack do
|
67
|
+
content.send(association_name)
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
def try_human_attribute_name(attribute, options = {})
|
73
|
+
super(attribute, options) do |defaults|
|
74
|
+
defaults << :"#{i18n_inherited_namespaced_scope(:attributes)}.#{attribute}" ## added
|
75
|
+
defaults << :"#{i18n_inherited_default_scope(:attributes)}.#{attribute}" ## added
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def human_attribute_name(attribute, options = {})
|
80
|
+
try_human_attribute_name(attribute, options)
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
def dom_id(prefix = nil)
|
86
|
+
[prefix, content.dom_id, self.class.dom_class, locale].compact.join("_")
|
87
|
+
end
|
88
|
+
|
89
|
+
def _destroy=(value)
|
90
|
+
case value
|
91
|
+
when 1, '1', true, 'true' then
|
92
|
+
mark_for_destruction
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def siblings
|
97
|
+
stack - [self]
|
98
|
+
end
|
99
|
+
|
100
|
+
def meta_counter
|
101
|
+
counter = association(:meta).options[:counter_cache]
|
102
|
+
counter = self.class.model_name.plural + "_count" if counter == true
|
103
|
+
meta.send(counter) if counter
|
104
|
+
end
|
105
|
+
|
106
|
+
def only_once_used_meta?
|
107
|
+
meta_counter && meta_counter <= 1
|
108
|
+
end
|
109
|
+
|
110
|
+
def changed?
|
111
|
+
meta_changed? || super
|
112
|
+
end
|
113
|
+
|
114
|
+
def meta_changed?
|
115
|
+
eval(_meta_attributes.map {|a| "template_meta.send(:#{a}) != meta.send(:#{a})"}.join(" || ")) ## content changed?
|
116
|
+
end
|
117
|
+
|
118
|
+
def meta_valid?
|
119
|
+
template_meta.valid?
|
120
|
+
end
|
121
|
+
|
122
|
+
def match?(params = {})
|
123
|
+
m = params.map {|key, value| send(key) == (value.is_a?(Symbol) ? value.to_s : value)}
|
124
|
+
(m.include?(false) || m.blank?) ? false : true
|
125
|
+
end
|
126
|
+
|
127
|
+
def meta_attributes
|
128
|
+
Hash[_meta_attributes.map {|attr| value = send(attr); [attr.to_s, value] if value}.compact]
|
129
|
+
end
|
130
|
+
|
131
|
+
def lang
|
132
|
+
Gaigo::LANGS.get(locale)
|
133
|
+
end
|
134
|
+
|
135
|
+
def to_param
|
136
|
+
locale
|
137
|
+
end
|
138
|
+
|
139
|
+
private
|
140
|
+
|
141
|
+
def template_meta
|
142
|
+
@_template_meta ||= meta ? meta.dup : build_meta.dup
|
143
|
+
end
|
144
|
+
|
145
|
+
def clear_meta
|
146
|
+
if only_once_used_meta?
|
147
|
+
destroy_meta
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def commit_meta
|
152
|
+
if meta_changed? ## writer attributes touched
|
153
|
+
attributes = extract_attributes(template_meta)
|
154
|
+
if meta.persisted?
|
155
|
+
if only_once_used_meta?
|
156
|
+
if exists_meta?(attributes)
|
157
|
+
destroy_meta
|
158
|
+
attach_or_create_meta(attributes)
|
159
|
+
else
|
160
|
+
update_meta(attributes)
|
161
|
+
end
|
162
|
+
else
|
163
|
+
attach_or_create_meta(attributes)
|
164
|
+
end
|
165
|
+
else
|
166
|
+
attach_or_create_meta(attributes)
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def extract_attributes(object)
|
172
|
+
object.attributes.delete_if {|a| !a.to_sym.in?(_meta_attributes)}
|
173
|
+
end
|
174
|
+
|
175
|
+
def destroy_meta
|
176
|
+
begin
|
177
|
+
meta.destroy
|
178
|
+
puts "Translation '#{locale}': PERSISTED META DESTROYED!"
|
179
|
+
rescue ActiveRecord::StaleObjectError
|
180
|
+
meta.reload
|
181
|
+
commit_meta
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def update_meta(attributes = {})
|
186
|
+
begin
|
187
|
+
meta.update_attributes(attributes)
|
188
|
+
puts "Translation '#{locale}': PERSISTED META UPDATED!"
|
189
|
+
rescue ActiveRecord::StaleObjectError
|
190
|
+
meta.reload
|
191
|
+
commit_meta
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
def find_existed_meta(attributes = {})
|
196
|
+
association(:meta).klass.where(attributes).limit(1).first
|
197
|
+
end
|
198
|
+
|
199
|
+
def create_new_meta(attributes = {})
|
200
|
+
create_meta(attributes).tap do |record|
|
201
|
+
record.send(:increment_lock) # Rails BUG! Counter caching process increments lock version in the DB table,
|
202
|
+
# but not in the associated object. So saving raises ActiveRecord::StaleObjectError.
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
def exists_meta?(attributes = {})
|
207
|
+
association(:meta).klass.exists?(attributes)
|
208
|
+
end
|
209
|
+
|
210
|
+
def attach_or_create_meta(attributes = {})
|
211
|
+
if existed = find_existed_meta(attributes)
|
212
|
+
self.meta = existed
|
213
|
+
puts "Translation '#{locale}': EXISTED META USED!"
|
214
|
+
else
|
215
|
+
self.meta = create_new_meta(attributes)
|
216
|
+
puts "Translation '#{locale}': NEW META CREATED!"
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'rails'
|
2
|
+
|
3
|
+
module Wakari
|
4
|
+
class Railtie < ::Rails::Railtie
|
5
|
+
config.before_initialize do
|
6
|
+
ActiveSupport.on_load :active_record do
|
7
|
+
require 'wakari/models/active_record_extension'
|
8
|
+
include Wakari::ActiveRecordExtension
|
9
|
+
end
|
10
|
+
ActiveSupport.on_load :action_controller do
|
11
|
+
require 'wakari/controllers/action_controller_extension'
|
12
|
+
include Wakari::ActionControllerExtension
|
13
|
+
end
|
14
|
+
ActiveSupport.on_load :action_view do
|
15
|
+
require 'wakari/helpers/action_view_extension'
|
16
|
+
include Wakari::ActionViewExtension
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/wakari.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'wakari/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "wakari"
|
8
|
+
gem.version = Wakari::VERSION
|
9
|
+
gem.authors = ["Valery Kvon"]
|
10
|
+
gem.email = ["addagger@gmail.com"]
|
11
|
+
gem.homepage = %q{http://vkvon.ru/projects/wakari}
|
12
|
+
gem.description = %q{Add translations for models}
|
13
|
+
gem.summary = %q{Multilang assets for Rails}
|
14
|
+
|
15
|
+
gem.add_development_dependency "gaigo"
|
16
|
+
gem.add_development_dependency "acts_as_list"
|
17
|
+
gem.rubyforge_project = "wakari"
|
18
|
+
|
19
|
+
gem.files = `git ls-files`.split($/)
|
20
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
21
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
22
|
+
gem.require_paths = ["lib"]
|
23
|
+
gem.licenses = ['MIT']
|
24
|
+
end
|
metadata
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: wakari
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Valery Kvon
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-12-31 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: gaigo
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: acts_as_list
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
description: Add translations for models
|
47
|
+
email:
|
48
|
+
- addagger@gmail.com
|
49
|
+
executables: []
|
50
|
+
extensions: []
|
51
|
+
extra_rdoc_files: []
|
52
|
+
files:
|
53
|
+
- .gitignore
|
54
|
+
- CHANGELOG
|
55
|
+
- Gemfile
|
56
|
+
- LICENSE.txt
|
57
|
+
- README.md
|
58
|
+
- Rakefile
|
59
|
+
- app/controllers/wakari_controller.rb
|
60
|
+
- lib/wakari.rb
|
61
|
+
- lib/wakari/bundle.rb
|
62
|
+
- lib/wakari/controllers/action_controller_extension.rb
|
63
|
+
- lib/wakari/controllers/controller.rb
|
64
|
+
- lib/wakari/engine.rb
|
65
|
+
- lib/wakari/helpers/action_view_extension.rb
|
66
|
+
- lib/wakari/models/active_record_extension.rb
|
67
|
+
- lib/wakari/models/content.rb
|
68
|
+
- lib/wakari/models/content/model.rb
|
69
|
+
- lib/wakari/models/meta.rb
|
70
|
+
- lib/wakari/models/meta/model.rb
|
71
|
+
- lib/wakari/models/meta/reference.rb
|
72
|
+
- lib/wakari/models/proxy.rb
|
73
|
+
- lib/wakari/models/proxy/model.rb
|
74
|
+
- lib/wakari/models/support/errors.rb
|
75
|
+
- lib/wakari/models/support/naming.rb
|
76
|
+
- lib/wakari/models/translation.rb
|
77
|
+
- lib/wakari/models/translation/model.rb
|
78
|
+
- lib/wakari/multilang.rb
|
79
|
+
- lib/wakari/railtie.rb
|
80
|
+
- lib/wakari/version.rb
|
81
|
+
- wakari.gemspec
|
82
|
+
homepage: http://vkvon.ru/projects/wakari
|
83
|
+
licenses:
|
84
|
+
- MIT
|
85
|
+
post_install_message:
|
86
|
+
rdoc_options: []
|
87
|
+
require_paths:
|
88
|
+
- lib
|
89
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
90
|
+
none: false
|
91
|
+
requirements:
|
92
|
+
- - ! '>='
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '0'
|
95
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
96
|
+
none: false
|
97
|
+
requirements:
|
98
|
+
- - ! '>='
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
version: '0'
|
101
|
+
requirements: []
|
102
|
+
rubyforge_project: wakari
|
103
|
+
rubygems_version: 1.8.24
|
104
|
+
signing_key:
|
105
|
+
specification_version: 3
|
106
|
+
summary: Multilang assets for Rails
|
107
|
+
test_files: []
|