wakari 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|