smartdict-core 0.0.1
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/README.markdown +77 -0
- data/bin/smartdict +20 -0
- data/bin/smartdict-populator +50 -0
- data/config/default_config.yml +15 -0
- data/lib/smartdict/commands/abstract_command.rb +225 -0
- data/lib/smartdict/commands/has_format_list.rb +28 -0
- data/lib/smartdict/commands/help_command.rb +53 -0
- data/lib/smartdict/commands/list_command.rb +45 -0
- data/lib/smartdict/commands/translate_command.rb +35 -0
- data/lib/smartdict/commands.rb +10 -0
- data/lib/smartdict/core/command_manager.rb +27 -0
- data/lib/smartdict/core/driver_manager.rb +9 -0
- data/lib/smartdict/core/format_manager.rb +8 -0
- data/lib/smartdict/core/has_logger.rb +12 -0
- data/lib/smartdict/core/is_manager.rb +21 -0
- data/lib/smartdict/core/logger.rb +5 -0
- data/lib/smartdict/core/plugin_manager.rb +32 -0
- data/lib/smartdict/core.rb +12 -0
- data/lib/smartdict/drivers/abstract_driver.rb +61 -0
- data/lib/smartdict/drivers/google_translate_driver.rb +56 -0
- data/lib/smartdict/drivers.rb +6 -0
- data/lib/smartdict/errors.rb +11 -0
- data/lib/smartdict/formats/abstract_format.rb +21 -0
- data/lib/smartdict/formats/fb2_format.rb +40 -0
- data/lib/smartdict/formats/text_color_format.rb +52 -0
- data/lib/smartdict/formats/text_format.rb +9 -0
- data/lib/smartdict/formats.rb +8 -0
- data/lib/smartdict/list_builder.rb +51 -0
- data/lib/smartdict/models/driver.rb +14 -0
- data/lib/smartdict/models/language.rb +16 -0
- data/lib/smartdict/models/translated_word.rb +14 -0
- data/lib/smartdict/models/translation.rb +83 -0
- data/lib/smartdict/models/translation_query.rb +13 -0
- data/lib/smartdict/models/word.rb +13 -0
- data/lib/smartdict/models/word_class.rb +14 -0
- data/lib/smartdict/models.rb +11 -0
- data/lib/smartdict/plugin/initializer_context.rb +7 -0
- data/lib/smartdict/plugin.rb +9 -0
- data/lib/smartdict/runner.rb +5 -0
- data/lib/smartdict/storage/seeder.rb +32 -0
- data/lib/smartdict/storage/seeds/drivers.csv +2 -0
- data/lib/smartdict/storage/seeds/languages.csv +188 -0
- data/lib/smartdict/storage/seeds/word_classes.csv +10 -0
- data/lib/smartdict/storage.rb +44 -0
- data/lib/smartdict/translation.rb +19 -0
- data/lib/smartdict/translator.rb +38 -0
- data/lib/smartdict/version.rb +3 -0
- data/lib/smartdict-core.rb +1 -0
- data/lib/smartdict.rb +97 -0
- metadata +468 -0
@@ -0,0 +1,61 @@
|
|
1
|
+
# Translation driver provides an ability to translate words using external
|
2
|
+
# data sources. It can be data from local disk, database or remote services
|
3
|
+
# like Google Translate.
|
4
|
+
# Every driver must inherit {Smartdict::Driver} and have implementation of
|
5
|
+
# {#translate} method. This method should sets translated and transcription
|
6
|
+
# properties. For examples you can see #{Smartdict::Driver::GoogleTranslateDriver}.
|
7
|
+
#
|
8
|
+
# In your implementation of {#translate} you need to use methods:
|
9
|
+
# * word - returns a word what needs to be translated.
|
10
|
+
# * from_lang - code of source language("en", "ru", etc)
|
11
|
+
# * to_lang - code of target language("en, "ru, etc)
|
12
|
+
#
|
13
|
+
# Usage:
|
14
|
+
# class HelloWorldDriver < Smartdict::Driver
|
15
|
+
# # Set name of driver, so DriverManager can identify it.
|
16
|
+
# name "hello_world"
|
17
|
+
#
|
18
|
+
# # This trivial example will always return the same translation.
|
19
|
+
# def translate
|
20
|
+
# self.translated = {"noun" => ["hello", "hi"], "verb" => ["greet"]}
|
21
|
+
# self.transcription = "he'leu"
|
22
|
+
# end
|
23
|
+
# end
|
24
|
+
class Smartdict::Drivers::AbstractDriver
|
25
|
+
|
26
|
+
attr_reader :word, :from_lang, :to_lang
|
27
|
+
attr_accessor :translated, :transcription
|
28
|
+
|
29
|
+
# Is used to identify a driver
|
30
|
+
cattr_accessor :name
|
31
|
+
|
32
|
+
def self.translate(*args)
|
33
|
+
self.new(*args).build_translation
|
34
|
+
end
|
35
|
+
|
36
|
+
# Sets driver name
|
37
|
+
def self.set_name(name)
|
38
|
+
self.name = name.to_s
|
39
|
+
end
|
40
|
+
|
41
|
+
def initialize(word, from_lang, to_lang)
|
42
|
+
@word = word
|
43
|
+
@from_lang = from_lang
|
44
|
+
@to_lang = to_lang
|
45
|
+
translate
|
46
|
+
end
|
47
|
+
|
48
|
+
def build_translation
|
49
|
+
translation = Smartdict::Translation.new(
|
50
|
+
:word => word,
|
51
|
+
:from_lang => from_lang.to_s,
|
52
|
+
:to_lang => to_lang.to_s,
|
53
|
+
:transcription => transcription,
|
54
|
+
:driver => self.name
|
55
|
+
)
|
56
|
+
translated.each do |word_class, words|
|
57
|
+
translation.translated[word_class] = words
|
58
|
+
end
|
59
|
+
translation
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Smartdict::Drivers
|
2
|
+
# The translation driver for Google Translate service.
|
3
|
+
class GoogleTranslateDriver < AbstractDriver
|
4
|
+
|
5
|
+
# Pretend being Firefox :)
|
6
|
+
USER_AGENT = "Mozilla/5.0 (X11; U; Linux x86_64; ru; rv:1.9.1.16) Gecko/20110429 Iceweasel/3.5.16 (like Firefox/3.5.1623123)"
|
7
|
+
|
8
|
+
# Host of Google Translate service.
|
9
|
+
HOST = "translate.google.com"
|
10
|
+
|
11
|
+
set_name "google_translate"
|
12
|
+
|
13
|
+
# Sets translation and transcription. GoogleTranslate doesn't provide
|
14
|
+
# transcription, so it's nil.
|
15
|
+
def translate
|
16
|
+
self.translated = response_to_hash(get_response)
|
17
|
+
self.transcription = nil
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
# Parses string return by GoogleTranslate
|
24
|
+
# Returns hash similar to this:
|
25
|
+
# { "noun" => ["try", "attempt"],
|
26
|
+
# "verb" => ["try", "offer"] }
|
27
|
+
# If no translation was found than returns nil.
|
28
|
+
# @return [Hash] every key is word class and every value is array of translated words.
|
29
|
+
def response_to_hash(response)
|
30
|
+
result = {}
|
31
|
+
array = YAML.load(response.gsub(/,+/, ', '))
|
32
|
+
array[1].each do |trans|
|
33
|
+
word_class = trans[0].empty? ? 'undefined' : trans[0]
|
34
|
+
result[word_class] = trans[1]
|
35
|
+
end
|
36
|
+
result
|
37
|
+
rescue NoMethodError
|
38
|
+
raise Smartdict::TranslationNotFound
|
39
|
+
end
|
40
|
+
|
41
|
+
# Sends remote request to GooogleTranslate service.
|
42
|
+
# @return [String] body of response.
|
43
|
+
def get_response
|
44
|
+
http = Net::HTTP.new(HOST, 80)
|
45
|
+
request = Net::HTTP::Get.new(http_path, {"User-Agent" => USER_AGENT})
|
46
|
+
http.request(request).read_body
|
47
|
+
end
|
48
|
+
|
49
|
+
# @return [String] http path for request to translate word.
|
50
|
+
def http_path
|
51
|
+
w = word.gsub(' ', '+')
|
52
|
+
"/translate_a/t?client=t&text=#{w}&hl=en&sl=#{from_lang}&tl=#{to_lang}&multires=1&otf=1&rom=1&ssel=0&tsel=0&sc=1"
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class Smartdict::Formats::AbstractFormat
|
2
|
+
include Singleton
|
3
|
+
|
4
|
+
class_attribute :description
|
5
|
+
|
6
|
+
def self.set_description(description)
|
7
|
+
self.description = description
|
8
|
+
end
|
9
|
+
|
10
|
+
# @param [Models::Translation] translation
|
11
|
+
# @return [String]
|
12
|
+
def self.format_translation(translation)
|
13
|
+
instance.format_translation(translation)
|
14
|
+
end
|
15
|
+
|
16
|
+
# @param [Array] array of {Models::Translation}.
|
17
|
+
# @return [String]
|
18
|
+
def self.format_list(translations)
|
19
|
+
instance.format_list(translations)
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'builder'
|
2
|
+
|
3
|
+
class Smartdict::Formats::Fb2Format < Smartdict::Formats::AbstractFormat
|
4
|
+
set_description "Displays translation in fiction book format."
|
5
|
+
|
6
|
+
|
7
|
+
def format_list(translations)
|
8
|
+
xml = Builder::XmlMarkup.new(:indent => 2)
|
9
|
+
xml.instruct!
|
10
|
+
xml.FictionBook(:xmlns => "http://www.gribuser.ru/xml/fictionbook/2.0") do |book|
|
11
|
+
book.description do |desc|
|
12
|
+
desc.tag!("document-info") do |doc_info|
|
13
|
+
doc_info.tag!("program-used", "Smartdict version #{Smartdict::VERSION}")
|
14
|
+
end
|
15
|
+
desc.tag!('title-info') do |title_info|
|
16
|
+
title_info.tag!('book-title', "#{Time.now.strftime('%F')} - English words")
|
17
|
+
title_info.genre 'sci_linguistic'
|
18
|
+
title_info.annotation do |annotation|
|
19
|
+
annotation.p "English words to learn"
|
20
|
+
annotation.p "The content generate by program Smardict v#{Smartdict::VERSION}"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
book.body do |body|
|
25
|
+
body.title "TITLE"
|
26
|
+
translations.each do |translation|
|
27
|
+
body.section do |word_section|
|
28
|
+
word_section.title {|title| title.p "#{translation.word} [#{translation.transcription}]"}
|
29
|
+
translation.translated.each do |word_class, translations|
|
30
|
+
word_section.subtitle word_class
|
31
|
+
word_section.p translations.join("; ")
|
32
|
+
word_section.tag!("empty-line")
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
class Smartdict::Formats::TextColorFormat < Smartdict::Formats::AbstractFormat
|
2
|
+
set_description "Displays translation with ASCII highlight"
|
3
|
+
|
4
|
+
# :nodoc:
|
5
|
+
def format_translation(translation)
|
6
|
+
result = "#{bold_green(translation.word)}"
|
7
|
+
result << green(" [#{translation.transcription}]") if translation.transcription
|
8
|
+
result << "\n"
|
9
|
+
|
10
|
+
translation.translated.each do |word_class, words|
|
11
|
+
result << " #{bold(word_class)}\n"
|
12
|
+
words.each do |word|
|
13
|
+
result << " #{word}\n"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
result
|
18
|
+
end
|
19
|
+
|
20
|
+
# :nodoc:
|
21
|
+
def format_list(translations)
|
22
|
+
result = ""
|
23
|
+
translations.each do |translation|
|
24
|
+
translated_words = translation.translated.values.map{|words| words.first(3)}.flatten
|
25
|
+
|
26
|
+
result << green(translation.word)
|
27
|
+
result << " - "
|
28
|
+
result << translated_words.join(", ")
|
29
|
+
result << "\n"
|
30
|
+
end
|
31
|
+
result
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def bold_green(text)
|
38
|
+
colorize(text, "1;32")
|
39
|
+
end
|
40
|
+
|
41
|
+
def bold(text)
|
42
|
+
colorize(text, 1)
|
43
|
+
end
|
44
|
+
|
45
|
+
def green(text)
|
46
|
+
colorize(text, 32)
|
47
|
+
end
|
48
|
+
|
49
|
+
def colorize(text, color_code)
|
50
|
+
"\e[#{color_code}m#{text}\e[0m"
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# TODO: tests
|
2
|
+
|
3
|
+
class Smartdict::ListBuilder
|
4
|
+
include Smartdict::Models
|
5
|
+
|
6
|
+
# Options:
|
7
|
+
# * +:since+
|
8
|
+
# * +:till+
|
9
|
+
# * +:from_lang+
|
10
|
+
# * +:to_lang+
|
11
|
+
# * +:driver+
|
12
|
+
#
|
13
|
+
# @return [Array] array of {Smartdict::Translation}.
|
14
|
+
def self.build(options = {})
|
15
|
+
new(options).send(:build)
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def initialize(options)
|
22
|
+
@options = options
|
23
|
+
@options[:till] ||= Time.now
|
24
|
+
end
|
25
|
+
|
26
|
+
def build
|
27
|
+
fetch_translations.map(&:to_struct)
|
28
|
+
end
|
29
|
+
|
30
|
+
def fetch_translations
|
31
|
+
query = Translation.all(Translation.translation_queries.created_at.lte => @options[:till])
|
32
|
+
query = query.all(Translation.translation_queries.created_at.gte => @options[:since]) if @options[:since]
|
33
|
+
query = query.all(Translation.to_lang_id => to_lang.id) if to_lang
|
34
|
+
query = query.all(Translation.from_lang_id => from_lang.id) if from_lang
|
35
|
+
query = query.all(Translation.driver_id => driver.id) if driver
|
36
|
+
query
|
37
|
+
end
|
38
|
+
|
39
|
+
def from_lang
|
40
|
+
Language[@options[:from_lang]]
|
41
|
+
end
|
42
|
+
|
43
|
+
def to_lang
|
44
|
+
Language[@options[:to_lang]]
|
45
|
+
end
|
46
|
+
|
47
|
+
def driver
|
48
|
+
Driver[@options[:driver]]
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class Smartdict::Models::Driver
|
2
|
+
include DataMapper::Resource
|
3
|
+
include DataMapper::Validations
|
4
|
+
include DataMapper::Enum
|
5
|
+
|
6
|
+
acts_as_enumerated
|
7
|
+
|
8
|
+
property :id , Serial
|
9
|
+
property :name, String, :unique_index => true
|
10
|
+
|
11
|
+
has n, :translations
|
12
|
+
|
13
|
+
validates_presence_of :name
|
14
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class Smartdict::Models::Language
|
2
|
+
include DataMapper::Resource
|
3
|
+
include DataMapper::Validations
|
4
|
+
include DataMapper::Enum
|
5
|
+
|
6
|
+
acts_as_enumerated :name_property => :code
|
7
|
+
|
8
|
+
property :id , Serial
|
9
|
+
property :name, String
|
10
|
+
property :code, String, :unique_index => true, :length => 2
|
11
|
+
|
12
|
+
has n, :words
|
13
|
+
|
14
|
+
validates_presence_of :name, :code
|
15
|
+
validates_uniqueness_of :name, :code
|
16
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class Smartdict::Models::TranslatedWord
|
2
|
+
include DataMapper::Resource
|
3
|
+
|
4
|
+
property :id, Serial
|
5
|
+
property :translation_id, Integer, :unique_index => :index_translated_word
|
6
|
+
property :word_class_id , Integer, :unique_index => :index_translated_word
|
7
|
+
property :word_id , Integer, :unique_index => :index_translated_word
|
8
|
+
|
9
|
+
belongs_to :translation
|
10
|
+
belongs_to :word_class
|
11
|
+
belongs_to :word
|
12
|
+
|
13
|
+
validates_presence_of :translation_id, :word_class_id, :word_id
|
14
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
class Smartdict::Models::Translation
|
2
|
+
include DataMapper::Resource
|
3
|
+
include Smartdict::Models
|
4
|
+
|
5
|
+
property :id , Serial
|
6
|
+
property :word_id , Integer, :unique_index => :index_translation
|
7
|
+
property :from_lang_id, Integer, :unique_index => :index_translation
|
8
|
+
property :to_lang_id , Integer, :unique_index => :index_translation
|
9
|
+
property :driver_id , Integer, :unique_index => :index_translation
|
10
|
+
|
11
|
+
belongs_to :word
|
12
|
+
belongs_to :driver
|
13
|
+
belongs_to :from_lang, 'Language'
|
14
|
+
belongs_to :to_lang , 'Language'
|
15
|
+
has n, :translated_words
|
16
|
+
has n, :translation_queries
|
17
|
+
|
18
|
+
validates_presence_of :word
|
19
|
+
validates_presence_of :driver
|
20
|
+
validates_presence_of :from_lang_id
|
21
|
+
validates_presence_of :to_lang_id
|
22
|
+
|
23
|
+
|
24
|
+
# Create {Smartdict::Models::Translation} from {Smartdict::Translation}.
|
25
|
+
def self.create_from_struct(struct)
|
26
|
+
from_lang = Language[struct.from_lang]
|
27
|
+
to_lang = Language[struct.to_lang]
|
28
|
+
driver = Driver[struct.driver]
|
29
|
+
|
30
|
+
word = Word.first_or_create(:name => struct.word, :language_id => from_lang.id)
|
31
|
+
word.transcription = struct.transcription if word.transcription.blank?
|
32
|
+
word.save!
|
33
|
+
|
34
|
+
translation = self.create(
|
35
|
+
:word => word,
|
36
|
+
:driver => driver,
|
37
|
+
:from_lang_id => from_lang.id,
|
38
|
+
:to_lang_id => to_lang.id
|
39
|
+
)
|
40
|
+
|
41
|
+
struct.translated.each do |word_class_name, meanings|
|
42
|
+
meanings.each do |meaning|
|
43
|
+
w = Word.first_or_create(:name => meaning, :language_id => to_lang.id)
|
44
|
+
word_class = WordClass[word_class_name]
|
45
|
+
TranslatedWord.create(:word => w, :word_class => word_class, :translation => translation)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
translation
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.find(word, from_lang_code, to_lang_code, driver_name)
|
53
|
+
from_lang = Language[from_lang_code]
|
54
|
+
to_lang = Language[to_lang_code]
|
55
|
+
driver = Driver[driver_name]
|
56
|
+
|
57
|
+
word = Word.first(:name => word, :language_id => from_lang.id)
|
58
|
+
self.first(:from_lang => from_lang, :to_lang => to_lang, :word => word, :driver => driver)
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
# TODO: it's a hack. Remove.
|
63
|
+
def initialize(*args)
|
64
|
+
self.class.finalize
|
65
|
+
self.word_class_id = 1
|
66
|
+
super(*args)
|
67
|
+
end
|
68
|
+
|
69
|
+
def to_struct
|
70
|
+
struct = Smartdict::Translation.new(
|
71
|
+
:word => word.name,
|
72
|
+
:transcription => word.transcription,
|
73
|
+
:from_lang => from_lang.code,
|
74
|
+
:to_lang => to_lang.code,
|
75
|
+
:driver => driver.name
|
76
|
+
)
|
77
|
+
translated_words.each do |tw|
|
78
|
+
struct[tw.word_class.name] << tw.word.name
|
79
|
+
end
|
80
|
+
struct
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class Smartdict::Models::TranslationQuery
|
2
|
+
include DataMapper::Resource
|
3
|
+
|
4
|
+
property :id , Serial
|
5
|
+
property :created_at , DateTime
|
6
|
+
property :translation_id, Integer
|
7
|
+
|
8
|
+
belongs_to :translation
|
9
|
+
|
10
|
+
before :save do
|
11
|
+
self.created_at = Time.now
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class Smartdict::Models::Word
|
2
|
+
include DataMapper::Resource
|
3
|
+
|
4
|
+
property :id , Serial
|
5
|
+
property :name , String, :unique_index => :index_word
|
6
|
+
property :language_id , Integer, :unique_index => :index_word
|
7
|
+
property :transcription, String
|
8
|
+
|
9
|
+
belongs_to :language
|
10
|
+
has n, :translations
|
11
|
+
|
12
|
+
validates_presence_of :language_id, :name
|
13
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# Word class is the same what type of speech is (noun, verb, etc.)
|
2
|
+
class Smartdict::Models::WordClass
|
3
|
+
include DataMapper::Resource
|
4
|
+
include DataMapper::Enum
|
5
|
+
|
6
|
+
acts_as_enumerated
|
7
|
+
|
8
|
+
property :id, Serial
|
9
|
+
property :name, String
|
10
|
+
|
11
|
+
has n, :translations
|
12
|
+
|
13
|
+
validates_presence_of :name
|
14
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Smartdict
|
2
|
+
module Models; end
|
3
|
+
end
|
4
|
+
|
5
|
+
require 'smartdict/models/language'
|
6
|
+
require 'smartdict/models/translation'
|
7
|
+
require 'smartdict/models/translation_query'
|
8
|
+
require 'smartdict/models/word'
|
9
|
+
require 'smartdict/models/word_class'
|
10
|
+
require 'smartdict/models/translated_word'
|
11
|
+
require 'smartdict/models/driver'
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'csv'
|
2
|
+
|
3
|
+
module Smartdict::Storage::Seeder
|
4
|
+
extend self
|
5
|
+
|
6
|
+
def seed!
|
7
|
+
seed_files.each do |csv_file|
|
8
|
+
seed_from(csv_file)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def seed_files
|
16
|
+
seeds_dir = File.expand_path('../seeds', __FILE__)
|
17
|
+
Dir["#{seeds_dir}/*.csv"]
|
18
|
+
end
|
19
|
+
|
20
|
+
def seed_from(csv_file)
|
21
|
+
model = csv_file_to_model(csv_file)
|
22
|
+
CSV.read(csv_file, :headers => true).each do |row|
|
23
|
+
model.create(row.to_hash)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def csv_file_to_model(csv_file)
|
28
|
+
plural_name = File.basename(csv_file).gsub(/\.csv$/, "")
|
29
|
+
model_name = plural_name.classify
|
30
|
+
Smartdict::Models.const_get(model_name)
|
31
|
+
end
|
32
|
+
end
|