rails_dictionary 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|