rails_dictionary 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/.gitignore +4 -0
- data/Gemfile +4 -0
- data/README.rdoc +12 -0
- data/Rakefile +8 -0
- data/dictionary.gemspec +21 -0
- data/lib/dictionary/acts_as_dict_slave.rb +59 -0
- data/lib/dictionary/acts_as_dict_type.rb +58 -0
- data/lib/dictionary/acts_as_dictionary.rb +46 -0
- data/lib/dictionary/array_core_ext.rb +13 -0
- data/lib/dictionary/version.rb +3 -0
- data/lib/dictionary.rb +8 -0
- data/test/dictionary_test.rb +129 -0
- metadata +68 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.rdoc
ADDED
data/Rakefile
ADDED
data/dictionary.gemspec
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "dictionary/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "rails_dictionary"
|
7
|
+
s.version = Dictionary::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["raykin"]
|
10
|
+
s.email = ["raykincoldxiao@campus.com"]
|
11
|
+
s.homepage = "https://github.com/raykin/dictionary"
|
12
|
+
s.summary = %q{dictionary data for application}
|
13
|
+
s.description = %q{mapping application static data to dictionary class and slave class}
|
14
|
+
|
15
|
+
s.rubyforge_project = "rails_dictionary"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module ActsAsDictSlave
|
2
|
+
def self.included(base)
|
3
|
+
base.extend(ClassMethods)
|
4
|
+
end
|
5
|
+
|
6
|
+
module ClassMethods
|
7
|
+
# argument except will remove dynamic column method
|
8
|
+
# argument add will add dynamic column method
|
9
|
+
def acts_as_dict_slave(ops={})
|
10
|
+
@@dict_columns = dict_columns(ops)
|
11
|
+
unless @@dict_columns.nil?
|
12
|
+
add_dynamic_column_method
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# return columns that exist in DictType#tab_and_column
|
17
|
+
def columns_in_dict_type
|
18
|
+
DictType.tab_and_column[self.name.downcase.to_sym]
|
19
|
+
end
|
20
|
+
|
21
|
+
# columns which map to dictionary
|
22
|
+
def dict_columns(ops={})
|
23
|
+
conf={except: nil,add: nil}
|
24
|
+
conf.update(ops)
|
25
|
+
cidt=self.columns_in_dict_type || []
|
26
|
+
cidt.delete(conf[:except])
|
27
|
+
case conf[:add]
|
28
|
+
when String
|
29
|
+
cidt.push(conf[:add])
|
30
|
+
when Array
|
31
|
+
cidt.push(*conf[:add])
|
32
|
+
else nil
|
33
|
+
end
|
34
|
+
cidt.uniq! || cidt
|
35
|
+
end
|
36
|
+
|
37
|
+
# add a belongs_to(Dictionary) association and a named_{column} method
|
38
|
+
def add_dynamic_column_method
|
39
|
+
self.extend(DynamicInsMethods)
|
40
|
+
@@dict_columns.each { |e| belongs_to "#{e.to_s}_dict".to_sym,class_name: "Dictionary",foreign_key: e }
|
41
|
+
@@dict_columns.each { |ele| named_dict_value ele.to_sym }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
module DynamicInsMethods
|
46
|
+
def named_dict_value(method_name)
|
47
|
+
belongs_to_name="#{method_name.to_s}_dict".to_sym
|
48
|
+
method_name="named_#{method_name.to_s}"
|
49
|
+
define_method(method_name) do | locale=:en |
|
50
|
+
# arg.first ? locale = "name_#{arg.first}" : locale ='name_en'
|
51
|
+
locale ="name_#{locale}"
|
52
|
+
self.send(belongs_to_name).try(:send,locale)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
ActiveRecord::Base.class_eval { include ActsAsDictSlave }
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require "rails"
|
2
|
+
module ActsAsDictType
|
3
|
+
def self.included(base)
|
4
|
+
base.extend(ClassMethods)
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def acts_as_dict_type(ops={})
|
9
|
+
self.class_eval do
|
10
|
+
has_many :dictionaries
|
11
|
+
include InstanceMethods
|
12
|
+
validates_uniqueness_of :name
|
13
|
+
after_save :delete_all_caches
|
14
|
+
after_destroy :delete_all_caches
|
15
|
+
|
16
|
+
def self.all_types
|
17
|
+
Rails.cache.fetch("DictType.all_types") { all.map(&:name) }.dup
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.cached_all
|
21
|
+
Rails.cache.fetch("DictType.cached_all") { all }.dup
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.revert(arg)
|
25
|
+
if arg.class == String
|
26
|
+
DictType.find_by_name(arg).id
|
27
|
+
elsif arg.class == Fixnum
|
28
|
+
DictType.find_by_id(arg).name
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
#TODO: get a more accurate method name
|
33
|
+
def self.tab_and_column
|
34
|
+
@tab_and_column={}
|
35
|
+
@all_types=all_types
|
36
|
+
#TODO: any better way to retrive the class name in app/model ?
|
37
|
+
# Here maybe a problem when class like Ckeditor::Asset(.name.underscore => "ckeditor/asset"
|
38
|
+
all_tabs=ActiveRecord::Base.connection.tables.sort.reject! do |t|
|
39
|
+
['schema_migrations', 'sessions'].include?(t)
|
40
|
+
end
|
41
|
+
all_class=all_tabs.map(&:singularize)
|
42
|
+
all_tabs=all_class
|
43
|
+
@tab_and_column=@all_types.extract_to_hash(all_tabs)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
module InstanceMethods
|
49
|
+
def delete_all_caches
|
50
|
+
Rails.cache.delete("DictType.all_types")
|
51
|
+
Rails.cache.delete("DictType.cached_all")
|
52
|
+
return true
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
58
|
+
ActiveRecord::Base.class_eval { include ActsAsDictType }
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module ActsAsDictionary
|
2
|
+
def self.included(base)
|
3
|
+
base.extend(ClassMethods)
|
4
|
+
end
|
5
|
+
|
6
|
+
module ClassMethods
|
7
|
+
def acts_as_dictionary
|
8
|
+
self.class_eval do
|
9
|
+
include InstanceMethods
|
10
|
+
belongs_to :dict_type
|
11
|
+
after_save :delete_dicts_cache
|
12
|
+
after_destroy :delete_dicts_cache
|
13
|
+
|
14
|
+
# TODO: need to add more function
|
15
|
+
def self.method_missing(method_id,options={},&block)
|
16
|
+
method_name=method_id.to_s.downcase
|
17
|
+
if DictType.all_types.include? method_id.to_s
|
18
|
+
Rails.cache.fetch("Dictionary.#{method_name}") { Dictionary.joins(:dict_type).where('dict_types.name'=>method_name).all }
|
19
|
+
listed_attr=Rails.cache.read("Dictionary.#{method_name}").dup
|
20
|
+
if options.keys.include? :locale
|
21
|
+
locale="name_#{options[:locale]}"
|
22
|
+
listed_attr.map! { |a| [a.send(locale),a.id] }
|
23
|
+
listed_attr.sort {|a,b| a.last <=> b.last } # maybe remove this one
|
24
|
+
else
|
25
|
+
listed_attr
|
26
|
+
end
|
27
|
+
else
|
28
|
+
super
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
module InstanceMethods
|
38
|
+
def delete_dicts_cache
|
39
|
+
method_name=DictType.revert(self.dict_type_id)
|
40
|
+
Rails.cache.delete("Dictionary.#{method_name}")
|
41
|
+
return true
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
ActiveRecord::Base.class_eval { include ActsAsDictionary }
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class Array
|
2
|
+
# returning a hash,not array
|
3
|
+
def extract_to_hash(keys_array)
|
4
|
+
ret_hash={}
|
5
|
+
keys_array.each {|ky| ret_hash[ky.to_sym]=[]}
|
6
|
+
self.each do |sf|
|
7
|
+
keys_array.each do |ky|
|
8
|
+
ret_hash[ky.to_sym] << sf.sub("#{ky}_","") if sf =~ Regexp.new("^#{ky}")
|
9
|
+
end
|
10
|
+
end
|
11
|
+
ret_hash.reject { |k,v| v.blank? }
|
12
|
+
end
|
13
|
+
end
|
data/lib/dictionary.rb
ADDED
@@ -0,0 +1,129 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
# min test
|
3
|
+
require "test/unit"
|
4
|
+
require "active_support"
|
5
|
+
require "active_record"
|
6
|
+
require "ruby-debug"
|
7
|
+
Object.const_set "RAILS_CACHE", ActiveSupport::Cache.lookup_store
|
8
|
+
require "active_support/cache"
|
9
|
+
require "rails"
|
10
|
+
# $: << "/home/raykin/studio/dictionary/lib" # tmply added for local testing
|
11
|
+
require "#{File.dirname(__FILE__)}/../lib/dictionary.rb"
|
12
|
+
|
13
|
+
ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:")
|
14
|
+
|
15
|
+
$stdout = StringIO.new
|
16
|
+
|
17
|
+
def setup_db
|
18
|
+
ActiveRecord::Base.logger
|
19
|
+
ActiveRecord::Schema.define(:version => 1) do
|
20
|
+
create_table :dict_types do |t|
|
21
|
+
t.string :name
|
22
|
+
t.string :comment
|
23
|
+
t.timestamps
|
24
|
+
end
|
25
|
+
create_table :dictionaries do |t|
|
26
|
+
t.string :name_en
|
27
|
+
t.string :name_zh
|
28
|
+
t.string :name_fr
|
29
|
+
t.integer :dict_type_id
|
30
|
+
t.timestamps
|
31
|
+
end
|
32
|
+
create_table :students do |t|
|
33
|
+
t.string :email
|
34
|
+
t.integer :city
|
35
|
+
t.integer :school
|
36
|
+
t.timestamps
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def teardown_db
|
42
|
+
ActiveRecord::Base.connection.tables.each do |table|
|
43
|
+
ActiveRecord::Base.connection.drop_table(table)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class DictType < ActiveRecord::Base
|
48
|
+
acts_as_dict_type
|
49
|
+
end
|
50
|
+
|
51
|
+
class Dictionary < ActiveRecord::Base
|
52
|
+
acts_as_dictionary
|
53
|
+
end
|
54
|
+
|
55
|
+
class Student < ActiveRecord::Base
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
class CoreExtTest < Test::Unit::TestCase
|
60
|
+
def test_array
|
61
|
+
expected_hash={:student => %w[school city],:admin => %w[role]}
|
62
|
+
assert_equal expected_hash, %w[student_school student_city admin_role].extract_to_hash(%w[student admin])
|
63
|
+
assert_equal expected_hash, %w[root student_school student_city admin_role].extract_to_hash(%w[student admin])
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
class DictTypeTest < Test::Unit::TestCase
|
68
|
+
def setup
|
69
|
+
setup_db
|
70
|
+
#Object.const_set('Student',Class.new(ActiveRecord::Base))
|
71
|
+
@dt_stu_city=DictType.create! :name => "student_city"
|
72
|
+
@dt_stu_school=DictType.create! :name => "student_school"
|
73
|
+
@dy_shanghai=Dictionary.create! name_en: "shanghai",name_zh: "上海",name_fr: "shanghai",dict_type_id: @dt_stu_city.id
|
74
|
+
@dy_beijing=Dictionary.create! name_en: "beijing",name_zh: "北京",name_fr: "Pékin",dict_type_id: @dt_stu_city.id
|
75
|
+
@stu_beijing=Student.create! email: "beijing@dict.com",city: @dy_beijing.id
|
76
|
+
@stu_shanghai=Student.create! email: "shanghai@dict.com",city: @dy_shanghai.id
|
77
|
+
Student.acts_as_dict_slave
|
78
|
+
# the acts_as_dict_slave need real data to generate dynamic method
|
79
|
+
end
|
80
|
+
|
81
|
+
def teardown
|
82
|
+
teardown_db
|
83
|
+
end
|
84
|
+
|
85
|
+
def test_tab_and_column
|
86
|
+
expected_hash={:student => %w[city school]}
|
87
|
+
assert_equal expected_hash,DictType.tab_and_column
|
88
|
+
end
|
89
|
+
|
90
|
+
def test_all_types
|
91
|
+
assert_equal %w[student_city student_school],DictType.all_types
|
92
|
+
end
|
93
|
+
|
94
|
+
# test revert method in acts_as_dict_type
|
95
|
+
def test_dt_revert
|
96
|
+
assert_equal "student_school",DictType.revert(@dt_stu_school.id)
|
97
|
+
end
|
98
|
+
|
99
|
+
def test_dictionary_method_missing
|
100
|
+
assert_equal [["shanghai",1],["beijing",2]],Dictionary.student_city(:locale => :en)
|
101
|
+
end
|
102
|
+
|
103
|
+
def test_dictionary_method_missing_with_locale
|
104
|
+
assert_equal [["上海", 1], ["北京", 2]],Dictionary.student_city(:locale => :zh)
|
105
|
+
end
|
106
|
+
|
107
|
+
# test dynamic instance methods in slave model
|
108
|
+
def test_named_city
|
109
|
+
assert_equal %w[city school],Student.columns_in_dict_type
|
110
|
+
assert_equal %w[city school],Student.dict_columns
|
111
|
+
assert_equal "shanghai",@stu_shanghai.named_city(:en)
|
112
|
+
assert_equal "Pékin",@stu_beijing.named_city(:fr)
|
113
|
+
end
|
114
|
+
|
115
|
+
def test_delete_dicts_cache
|
116
|
+
@dy_wuhan=Dictionary.create! name_en: "wuhan",name_zh: "武汉",name_fr: "wuhan",dict_type_id: @dt_stu_city.id
|
117
|
+
assert_equal [["shanghai", 1], ["beijing", 2], ["wuhan", 3]],Dictionary.student_city(:locale => :en)
|
118
|
+
@dy_wuhan.destroy
|
119
|
+
assert_equal [["shanghai", 1], ["beijing", 2]],Dictionary.student_city(:locale => :en)
|
120
|
+
assert_equal [@dy_shanghai,@dy_beijing],Dictionary.student_city
|
121
|
+
end
|
122
|
+
|
123
|
+
def test_delete_all_caches
|
124
|
+
assert_equal %w[student_city student_school],DictType.all_types
|
125
|
+
@dt_stu_school.destroy
|
126
|
+
assert_equal %w[student_city],DictType.all_types
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
metadata
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rails_dictionary
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 0.0.1
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- raykin
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2011-02-26 00:00:00 +08:00
|
14
|
+
default_executable:
|
15
|
+
dependencies: []
|
16
|
+
|
17
|
+
description: mapping application static data to dictionary class and slave class
|
18
|
+
email:
|
19
|
+
- raykincoldxiao@campus.com
|
20
|
+
executables: []
|
21
|
+
|
22
|
+
extensions: []
|
23
|
+
|
24
|
+
extra_rdoc_files: []
|
25
|
+
|
26
|
+
files:
|
27
|
+
- .gitignore
|
28
|
+
- Gemfile
|
29
|
+
- README.rdoc
|
30
|
+
- Rakefile
|
31
|
+
- dictionary.gemspec
|
32
|
+
- lib/dictionary.rb
|
33
|
+
- lib/dictionary/acts_as_dict_slave.rb
|
34
|
+
- lib/dictionary/acts_as_dict_type.rb
|
35
|
+
- lib/dictionary/acts_as_dictionary.rb
|
36
|
+
- lib/dictionary/array_core_ext.rb
|
37
|
+
- lib/dictionary/version.rb
|
38
|
+
- test/dictionary_test.rb
|
39
|
+
has_rdoc: true
|
40
|
+
homepage: https://github.com/raykin/dictionary
|
41
|
+
licenses: []
|
42
|
+
|
43
|
+
post_install_message:
|
44
|
+
rdoc_options: []
|
45
|
+
|
46
|
+
require_paths:
|
47
|
+
- lib
|
48
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: "0"
|
54
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
55
|
+
none: false
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: "0"
|
60
|
+
requirements: []
|
61
|
+
|
62
|
+
rubyforge_project: rails_dictionary
|
63
|
+
rubygems_version: 1.5.2
|
64
|
+
signing_key:
|
65
|
+
specification_version: 3
|
66
|
+
summary: dictionary data for application
|
67
|
+
test_files:
|
68
|
+
- test/dictionary_test.rb
|