rails_dictionary 0.0.8 → 0.0.9
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 +3 -1
- data/CHANGELOG +6 -0
- data/Gemfile +7 -1
- data/README.rdoc +19 -7
- data/Rakefile +2 -1
- data/lib/rails_dictionary/engine.rb +4 -0
- data/lib/rails_dictionary/models/active_record_extension.rb +45 -0
- data/lib/rails_dictionary/models/acts_as_dict_slave.rb +55 -0
- data/lib/rails_dictionary/models/acts_as_dict_type.rb +54 -0
- data/lib/rails_dictionary/models/acts_as_dictionary.rb +56 -0
- data/lib/rails_dictionary/railtie.rb +17 -0
- data/lib/rails_dictionary/version.rb +1 -1
- data/lib/rails_dictionary.rb +2 -4
- data/log/development.log +2 -0
- data/rails_dictionary.gemspec +1 -1
- data/spec/fake_app.rb +36 -0
- data/spec/rails_dictionary_spec.rb +90 -0
- data/spec/spec_helper.rb +19 -0
- data/test/rails_dictionary_test.rb +8 -6
- metadata +19 -9
- data/lib/rails_dictionary/acts_as_dict_slave.rb +0 -69
- data/lib/rails_dictionary/acts_as_dict_type.rb +0 -56
- data/lib/rails_dictionary/acts_as_dictionary.rb +0 -57
data/.gitignore
CHANGED
data/CHANGELOG
ADDED
data/Gemfile
CHANGED
@@ -1,4 +1,10 @@
|
|
1
1
|
source "http://rubygems.org"
|
2
2
|
|
3
|
-
# Specify your gem's dependencies in
|
3
|
+
# Specify your gem's dependencies in rails_dictionary.gemspec
|
4
4
|
gemspec
|
5
|
+
|
6
|
+
group :development,:test do
|
7
|
+
gem 'rspec', '>= 2.5.0'
|
8
|
+
gem 'rails', '>= 3.0.0'
|
9
|
+
gem 'sqlite3'
|
10
|
+
end
|
data/README.rdoc
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
= Intro
|
2
|
-
There are
|
3
|
-
|
2
|
+
There are always some static data(not static page) in application.For example product type or diploma kind of student.
|
3
|
+
|
4
|
+
This gem can map these static data to a Dictionary#method like Dictionary#student_diploma_kind and generate a list of instance method on Student like @student.named_city which return city name (with locale if you need).
|
4
5
|
|
5
6
|
= Usage
|
6
7
|
== Installation
|
@@ -43,10 +44,11 @@ These task are just generate table dictionaries,dict_types,students and some sam
|
|
43
44
|
| 2 | shanghai@dict.com | 3 | |
|
44
45
|
+----+-------------------+------+--------+
|
45
46
|
2 rows in set
|
46
|
-
There is one
|
47
|
+
There is one convention of DictType.name value.All value of DictType.name is "model_method" : student is model and city is method of student model.
|
47
48
|
|
48
49
|
=== Table Definition
|
49
50
|
Make sure you have two tables which named as dict_types and dictionaries.
|
51
|
+
|
50
52
|
Table dictionaries has one convention of naming column : name_locale.So the name_fr means this column have a french value,you can see more usage later.
|
51
53
|
The students table is not required and variable by your application.
|
52
54
|
|
@@ -65,13 +67,15 @@ Here is what should be like.Student model can be other models.
|
|
65
67
|
acts_as_dict_slave
|
66
68
|
end
|
67
69
|
|
68
|
-
== Features
|
70
|
+
== Features (relies on the above data) :
|
69
71
|
DictType.all_cached #=> return cache of DictType.all
|
70
72
|
DictType.all_types = %w[student_city student_school] # also cached
|
71
73
|
Dictionary.student_city #=> [Dictionary.find(5),Dictionary.find(6)]
|
72
|
-
student_city is a dynamic method which returns a list of dictionary object which dict_type is "student_city".
|
74
|
+
student_city is a dynamic method which returns a list of dictionary object which dict_type is "student_city".
|
75
|
+
Actually Dictionary will have as many dynamic methods as DictType.count and each dynamic method name is DictType.name.
|
76
|
+
Before version 0.1.0 or 0.2.0 ,all dynamic methods return an array,not ActiveRelation.So
|
73
77
|
Dictionary.student_school = []
|
74
|
-
Dictionary.student_city :locale => :en
|
78
|
+
Dictionary.student_city :locale => :en #=> [["shanghai",1],["beijing",2]]
|
75
79
|
You can use it in form select method like
|
76
80
|
collection_select :student,:city,Dictionary.student_city,:id,:name_en
|
77
81
|
select :student,:city,Dictionary.student_city(params)
|
@@ -80,6 +84,9 @@ If params contains :locale => :fr,it returns a list of french name of student ci
|
|
80
84
|
Here is an other solution for international translation.
|
81
85
|
Student.find(1).named_city(:zh) = "北京"
|
82
86
|
Student.find(1).named_city(:fr) = "Pékin"
|
87
|
+
Student.find(1).named_city(:en) = "beijing"
|
88
|
+
Make sure your locale is en,not en-US.
|
89
|
+
|
83
90
|
Student has two belongs_to assocition which named as city_dict and school_dict,the naming convention is method_dict.
|
84
91
|
Student.find(1).city_dict #=> Dictionary.find(6)
|
85
92
|
|
@@ -97,5 +104,10 @@ The rails cache would not refresh.
|
|
97
104
|
In short,when you confused with the debug data,try running "rails tmp:clear" first.
|
98
105
|
|
99
106
|
= TODO & Problems
|
100
|
-
There are no convention and implemention to map Class like Ckeditor::Asset to a method.
|
107
|
+
There are no convention and implemention to map Class like Ckeditor::Asset to a legal method name.
|
108
|
+
|
109
|
+
Rebuild core code.Using Rails engine.
|
110
|
+
|
111
|
+
Make cached type of DictType to an array of symbols
|
112
|
+
|
101
113
|
Rewrite Testing,make it more maintainable.
|
data/Rakefile
CHANGED
@@ -3,6 +3,7 @@ Bundler::GemHelper.install_tasks
|
|
3
3
|
|
4
4
|
desc "Running Test"
|
5
5
|
task :test do
|
6
|
-
system "ruby -I . test/rails_dictionary_test.rb "
|
6
|
+
# system "ruby -I . test/rails_dictionary_test.rb " # used with version 0.0.8 or before it
|
7
|
+
system "rspec spec/rails_dictionary_spec.rb"
|
7
8
|
end
|
8
9
|
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module RailsDictionary
|
2
|
+
module ActiveRecordExtension
|
3
|
+
def self.included(base)
|
4
|
+
base.extend(ClassMethods)
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
# TODO: move macro define in each module file
|
9
|
+
# See Usage in readme.doc.
|
10
|
+
def acts_as_dict_type
|
11
|
+
self.class_eval do
|
12
|
+
has_many :dictionaries
|
13
|
+
validates_uniqueness_of :name
|
14
|
+
after_save :delete_all_caches
|
15
|
+
after_destroy :delete_all_caches
|
16
|
+
end
|
17
|
+
include RailsDictionary::ActsAsDictType
|
18
|
+
end
|
19
|
+
|
20
|
+
def acts_as_dictionary
|
21
|
+
self.class_eval do
|
22
|
+
belongs_to :dict_type
|
23
|
+
after_save :delete_dicts_cache
|
24
|
+
after_destroy :delete_dicts_cache
|
25
|
+
end
|
26
|
+
include RailsDictionary::ActsAsDictionary
|
27
|
+
end
|
28
|
+
|
29
|
+
# :except - remove dict mapping column
|
30
|
+
# :add - add dict mapping column
|
31
|
+
# :locale - add and initialize class attribute default_dict_locale
|
32
|
+
def acts_as_dict_slave(ops={})
|
33
|
+
include RailsDictionary::ActsAsDictSlave
|
34
|
+
class_attribute :default_dict_locale,:instance_writer => false
|
35
|
+
cattr_accessor :dict_mapping_columns,:instance_writes => false
|
36
|
+
self.default_dict_locale = ops[:locale] if ops[:locale]
|
37
|
+
self.dict_mapping_columns = dict_columns(ops)
|
38
|
+
unless dict_mapping_columns.nil?
|
39
|
+
add_dynamic_column_method
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module RailsDictionary
|
2
|
+
module ActsAsDictSlave
|
3
|
+
def self.included(base)
|
4
|
+
base.extend(ClassMethods)
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
|
9
|
+
# return columns that exist in DictType#tab_and_column
|
10
|
+
def columns_in_dict_type
|
11
|
+
DictType.tab_and_column[self.name.underscore.to_sym]
|
12
|
+
end
|
13
|
+
|
14
|
+
# columns which map to dictionary
|
15
|
+
def dict_columns(ops={})
|
16
|
+
conf={except: nil,add: nil}
|
17
|
+
conf.update(ops)
|
18
|
+
cidt=self.columns_in_dict_type || []
|
19
|
+
cidt.delete(conf[:except])
|
20
|
+
case conf[:add]
|
21
|
+
when String
|
22
|
+
cidt.push(conf[:add])
|
23
|
+
when Array
|
24
|
+
cidt.push(*conf[:add])
|
25
|
+
else nil
|
26
|
+
end
|
27
|
+
cidt.uniq! || cidt
|
28
|
+
end
|
29
|
+
|
30
|
+
# add a belongs_to(Dictionary) association and a named_{column} method
|
31
|
+
def add_dynamic_column_method
|
32
|
+
dict_mapping_columns.each { |e| belongs_to "#{e.to_s}_dict".to_sym,class_name: "Dictionary",foreign_key: e }
|
33
|
+
dict_mapping_columns.each { |ele| named_dict_value ele.to_sym }
|
34
|
+
end
|
35
|
+
|
36
|
+
# Generate dynamic instance method named_column to slave model
|
37
|
+
# def named_city(locale=nil)
|
38
|
+
# locale = locale.presence || default_dict_locale.presence || :en
|
39
|
+
# locale = "name_#{locale}"
|
40
|
+
# self.send(city_dict).try(:send,locale)
|
41
|
+
# end
|
42
|
+
def named_dict_value(method_name)
|
43
|
+
belongs_to_name="#{method_name.to_s}_dict".to_sym
|
44
|
+
method_name="named_#{method_name.to_s}"
|
45
|
+
define_method(method_name) do | locale=nil |
|
46
|
+
locale = locale.presence || default_dict_locale.presence || :en
|
47
|
+
locale = "name_#{locale}"
|
48
|
+
self.send(belongs_to_name).try(:send,locale)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module RailsDictionary
|
2
|
+
module ActsAsDictType
|
3
|
+
def self.included(base)
|
4
|
+
base.extend(ClassMethods)
|
5
|
+
base.send :include,InstanceMethods
|
6
|
+
end
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
|
10
|
+
def all_types
|
11
|
+
Rails.cache.fetch("DictType.all_types") { all.map(&:name).map(&:to_sym) }.dup
|
12
|
+
end
|
13
|
+
|
14
|
+
def cached_all
|
15
|
+
Rails.cache.fetch("DictType.cached_all") { all }.dup
|
16
|
+
end
|
17
|
+
|
18
|
+
# short method to transfer id to name or name to id
|
19
|
+
def revert(arg)
|
20
|
+
if arg.is_a?(String)
|
21
|
+
DictType.where(name: arg).first.try(:id)
|
22
|
+
elsif arg.is_a?(Fixnum)
|
23
|
+
DictType.where(id: arg).first.try(:name)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# TODO: get a more accurate method name
|
28
|
+
#
|
29
|
+
# Parse the name value to get which column and model are listed in DictType
|
30
|
+
#
|
31
|
+
# Programmer DOC:
|
32
|
+
# There are two chooses to get subclass,one is subclasses the other is descendants,
|
33
|
+
# I don't know which is better,but descendants contains subclass of subclass,it contains more.
|
34
|
+
# Class like +Ckeditor::Asset+ transfer to "ckeditor/asset",but we can not naming method like that,
|
35
|
+
# So it still not support, the solution may be simple,just make another convention to escape "/"
|
36
|
+
def tab_and_column
|
37
|
+
Rails.cache.fetch("DictType.tab_and_column") do
|
38
|
+
all_model_class=ActiveRecord::Base.descendants.map(&:name).map(&:underscore)
|
39
|
+
all_types.map(&:to_s).extract_to_hash(all_model_class)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
module InstanceMethods
|
45
|
+
def delete_all_caches
|
46
|
+
Rails.cache.delete("DictType.all_types")
|
47
|
+
Rails.cache.delete("DictType.cached_all")
|
48
|
+
Rails.cache.delete("DictType.tab_and_column")
|
49
|
+
return true
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module RailsDictionary
|
2
|
+
module ActsAsDictionary
|
3
|
+
def self.included(base)
|
4
|
+
base.extend(ClassMethods)
|
5
|
+
base.send :include,InstanceMethods
|
6
|
+
end
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
|
10
|
+
# Return an instance of array
|
11
|
+
# Programmer DOC:
|
12
|
+
# Following design is failed with sqlite3 in test
|
13
|
+
# scope :dict_type_name_eq,lambda {|method_name| joins(:dict_type).where("dict_types.name" => method_name)}
|
14
|
+
# remove the all seems not pass test,return following errors
|
15
|
+
# TypeError: no marshal_dump is defined for class SQLite3::Database
|
16
|
+
def dict_type_name_eq(method_name)
|
17
|
+
joins(:dict_type).where("dict_types.name" => method_name).all
|
18
|
+
end
|
19
|
+
|
20
|
+
# Generate methods like Dictionary.student_city
|
21
|
+
# Dictionary.student_city - a list of dictionary object which dict type is student_city
|
22
|
+
# Dictionary.student_city(:locale => :zh) - a select format array which can be used
|
23
|
+
# in view method select as choice params
|
24
|
+
# Programmer DOC && TODO:
|
25
|
+
# rethink about the cache.
|
26
|
+
# cache methods like Dictionary.student_city(:locale => :zh,:sort => :name_fr)
|
27
|
+
# but not cache Dictionary.student_city,return it as relation
|
28
|
+
def method_missing(method_id,options={},&block)
|
29
|
+
method_name=method_id.to_s.downcase
|
30
|
+
if DictType.all_types.include? method_id
|
31
|
+
Rails.cache.fetch("Dictionary.#{method_name}") { dict_type_name_eq(method_name) }
|
32
|
+
listed_attr=Rails.cache.read("Dictionary.#{method_name}").dup
|
33
|
+
# Instance of activerelation can not be dup?
|
34
|
+
if options.keys.include? :locale or options.keys.include? "locale"
|
35
|
+
locale="name_#{ options[:locale] }"
|
36
|
+
listed_attr.map! { |a| [a.send(locale),a.id] }
|
37
|
+
listed_attr.sort { |a,b| a.first <=> b.first }
|
38
|
+
# maybe remove the above line,or change some sorting and caching design
|
39
|
+
else
|
40
|
+
listed_attr
|
41
|
+
end
|
42
|
+
else
|
43
|
+
super
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
module InstanceMethods
|
49
|
+
def delete_dicts_cache
|
50
|
+
method_name=DictType.revert(self.dict_type_id)
|
51
|
+
Rails.cache.delete("Dictionary.#{method_name}")
|
52
|
+
return true
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'rails'
|
2
|
+
|
3
|
+
require File.join(File.dirname(__FILE__),"array_core_ext")
|
4
|
+
require File.join(File.dirname(__FILE__),"models/acts_as_dict_type")
|
5
|
+
require File.join(File.dirname(__FILE__),"models/acts_as_dictionary")
|
6
|
+
require File.join(File.dirname(__FILE__),"models/acts_as_dict_slave")
|
7
|
+
|
8
|
+
module RailsDictionary
|
9
|
+
class Railtie < ::Rails::Railtie
|
10
|
+
initializer 'rails_dictionary' do |app|
|
11
|
+
ActiveSupport.on_load(:active_record) do
|
12
|
+
require File.join(File.dirname(__FILE__), 'models/active_record_extension')
|
13
|
+
::ActiveRecord::Base.send :include, RailsDictionary::ActiveRecordExtension
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/rails_dictionary.rb
CHANGED
@@ -1,4 +1,2 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require "rails_dictionary/acts_as_dict_type"
|
4
|
-
require "rails_dictionary/array_core_ext"
|
1
|
+
require File.join(File.dirname(__FILE__), 'rails_dictionary/railtie')
|
2
|
+
require File.join(File.dirname(__FILE__), 'rails_dictionary/engine')
|
data/log/development.log
ADDED
data/rails_dictionary.gemspec
CHANGED
@@ -6,7 +6,7 @@ Gem::Specification.new do |s|
|
|
6
6
|
s.name = "rails_dictionary"
|
7
7
|
s.version = RailsDictionary::VERSION
|
8
8
|
s.platform = Gem::Platform::RUBY
|
9
|
-
s.authors = ["
|
9
|
+
s.authors = ["Raykin Lee"]
|
10
10
|
s.email = ["raykincoldxiao@campus.com"]
|
11
11
|
s.homepage = "https://github.com/raykin/rails_dictionary"
|
12
12
|
s.summary = %q{dictionary data for web application}
|
data/spec/fake_app.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
|
2
|
+
# database
|
3
|
+
ActiveRecord::Base.configurations = {'test' => {:adapter => 'sqlite3', :database => ':memory:'}}
|
4
|
+
ActiveRecord::Base.establish_connection('test')
|
5
|
+
|
6
|
+
# config
|
7
|
+
app = Class.new(Rails::Application)
|
8
|
+
app.config.active_support.deprecation = :log
|
9
|
+
app.initialize!
|
10
|
+
|
11
|
+
# models
|
12
|
+
class DictType < ActiveRecord::Base
|
13
|
+
acts_as_dict_type
|
14
|
+
end
|
15
|
+
|
16
|
+
class Dictionary < ActiveRecord::Base
|
17
|
+
acts_as_dictionary
|
18
|
+
end
|
19
|
+
|
20
|
+
class Student < ActiveRecord::Base
|
21
|
+
end
|
22
|
+
|
23
|
+
#migrations
|
24
|
+
class CreateAllTables < ActiveRecord::Migration
|
25
|
+
def self.up
|
26
|
+
create_table(:dict_types) {|t| t.string :name}
|
27
|
+
create_table(:dictionaries) {|t| t.string :name_en; t.string :name_zh ; t.string :name_fr ; t.integer :dict_type_id}
|
28
|
+
create_table(:students) {|t| t.string :email; t.integer :city; t.integer :school}
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.down
|
32
|
+
drop_table :dict_types
|
33
|
+
drop_table :dictionaries
|
34
|
+
drop_table :students
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require File.expand_path('spec_helper', File.dirname(__FILE__))
|
3
|
+
|
4
|
+
|
5
|
+
describe RailsDictionary::ActiveRecordExtension do
|
6
|
+
%w[acts_as_dict_type acts_as_dictionary acts_as_dict_slave].each do |method_name|
|
7
|
+
it "contains #{method_name}" do
|
8
|
+
ActiveRecord::Base.methods.include?(method_name.to_sym).should be_true
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe RailsDictionary do
|
14
|
+
let!(:dt_stu_city) { DictType.create! :name => "student_city" }
|
15
|
+
let!(:dt_stu_school) { DictType.create! :name => "student_school" }
|
16
|
+
let!(:dy_shanghai) { Dictionary.create! name_en: "shanghai",name_zh: "上海",name_fr: "shanghai",dict_type_id: dt_stu_city.id }
|
17
|
+
let!(:dy_beijing) { Dictionary.create! name_en: "beijing",name_zh: "北京",name_fr: "Pékin",dict_type_id: dt_stu_city.id }
|
18
|
+
let!(:stu_beijing) { Student.create! email: "beijing@dict.com",city: dy_beijing.id }
|
19
|
+
let!(:stu_shanghai) { Student.create! email: "shanghai@dict.com",city: dy_shanghai.id }
|
20
|
+
|
21
|
+
describe DictType do
|
22
|
+
it "parse model and method" do
|
23
|
+
expected_hash={:student => %w[city school]}
|
24
|
+
DictType.tab_and_column.should == expected_hash
|
25
|
+
end
|
26
|
+
|
27
|
+
it "all types" do
|
28
|
+
DictType.all_types.should == [:student_city, :student_school]
|
29
|
+
end
|
30
|
+
|
31
|
+
it "reverts id to name or name to id" do
|
32
|
+
DictType.revert(dt_stu_school.id).should == "student_school"
|
33
|
+
DictType.revert(100).should be_nil
|
34
|
+
end
|
35
|
+
|
36
|
+
it "after one record removed" do
|
37
|
+
dt_stu_school.destroy
|
38
|
+
DictType.all_types.should == [:student_city]
|
39
|
+
DictType.tab_and_column.should == Hash[:student,["city"]]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe Dictionary do
|
44
|
+
it "generate student_city method" do
|
45
|
+
Dictionary.student_city.should == [dy_shanghai,dy_beijing]
|
46
|
+
end
|
47
|
+
|
48
|
+
it "generate student_city method with locale" do
|
49
|
+
Dictionary.student_city(:locale => :zh).should == [["上海", 1],["北京", 2]]
|
50
|
+
end
|
51
|
+
|
52
|
+
it "after record added or removed" do
|
53
|
+
@dy_wuhan=Dictionary.create! name_en: "wuhan",name_zh: "武汉",name_fr: "wuhan",dict_type_id: dt_stu_city.id
|
54
|
+
Dictionary.student_city(:locale => :en).should == [["beijing", 2],["shanghai",1],["wuhan", 3]]
|
55
|
+
@dy_wuhan.destroy
|
56
|
+
Dictionary.student_city(:locale => :en).should == [["beijing", 2],["shanghai",1]]
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe Student do
|
61
|
+
before :each do
|
62
|
+
Student.acts_as_dict_slave
|
63
|
+
end
|
64
|
+
it "method of columns_in_dict_type and dict_columns" do
|
65
|
+
Student.columns_in_dict_type.should == %w[city school]
|
66
|
+
Student.dict_columns == %w[city school]
|
67
|
+
end
|
68
|
+
|
69
|
+
it "named_city with different locale" do
|
70
|
+
stu_shanghai.named_city(:en).should == "shanghai"
|
71
|
+
stu_shanghai.named_city("").should == "shanghai"
|
72
|
+
stu_beijing.named_city(:fr).should == "Pékin"
|
73
|
+
end
|
74
|
+
|
75
|
+
it "override default locale" do
|
76
|
+
Student.acts_as_dict_slave :locale => :fr
|
77
|
+
stu_beijing.named_city.should == "Pékin"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe Array do
|
83
|
+
let(:an_array) { %w[root student_school student_city admin_role] }
|
84
|
+
let(:expected_hash) { {:student => %w[school city],:admin => %w[role]} }
|
85
|
+
let(:blank_hash) { Hash.new }
|
86
|
+
it "extract to hash" do
|
87
|
+
an_array.extract_to_hash(%w[student admin]).should == expected_hash
|
88
|
+
an_array.extract_to_hash(%w[students]).should == blank_hash
|
89
|
+
end
|
90
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# copy and modify the test design of kaminari(a paginate gem for Rails3)
|
2
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
3
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
4
|
+
require 'rails'
|
5
|
+
require 'active_record'
|
6
|
+
require 'action_controller/railtie'
|
7
|
+
require 'action_view/railtie'
|
8
|
+
require 'rails_dictionary'
|
9
|
+
|
10
|
+
require File.join(File.dirname(__FILE__), 'fake_app')
|
11
|
+
|
12
|
+
require 'rspec/rails'
|
13
|
+
|
14
|
+
$stdout = StringIO.new # remove the noise output
|
15
|
+
|
16
|
+
RSpec.configure do |config|
|
17
|
+
config.use_transactional_fixtures = true
|
18
|
+
CreateAllTables.up unless ActiveRecord::Base.connection.table_exists? 'dict_types'
|
19
|
+
end
|
@@ -1,4 +1,7 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
|
+
# Testing for version 0.0.8 or lower.Copy the test design from acts_as_tree
|
3
|
+
# Usage: run "ruby -I . test/rails_dictionary_test.rb " in root dir
|
4
|
+
#
|
2
5
|
require "test/unit"
|
3
6
|
require "active_support"
|
4
7
|
require "active_record"
|
@@ -6,7 +9,6 @@ require "ruby-debug" # coupled with debugger to debug code
|
|
6
9
|
Object.const_set "RAILS_CACHE", ActiveSupport::Cache.lookup_store
|
7
10
|
require "active_support/cache"
|
8
11
|
require "rails"
|
9
|
-
# $: << "/home/raykin/studio/rails_dictionary/lib" # tmply added for local testing
|
10
12
|
require "#{File.dirname(__FILE__)}/../lib/rails_dictionary.rb"
|
11
13
|
|
12
14
|
ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:")
|
@@ -53,7 +55,6 @@ end
|
|
53
55
|
class Student < ActiveRecord::Base
|
54
56
|
end
|
55
57
|
|
56
|
-
|
57
58
|
class CoreExtTest < Test::Unit::TestCase
|
58
59
|
def test_array
|
59
60
|
expected_hash={:student => %w[school city],:admin => %w[role]}
|
@@ -92,6 +93,7 @@ class DictTypeTest < Test::Unit::TestCase
|
|
92
93
|
# test revert method in acts_as_dict_type
|
93
94
|
def test_dt_revert
|
94
95
|
assert_equal "student_school",DictType.revert(@dt_stu_school.id)
|
96
|
+
assert_equal nil,DictType.revert(100)
|
95
97
|
end
|
96
98
|
|
97
99
|
# test dynamic instance methods in slave model
|
@@ -111,18 +113,18 @@ class DictTypeTest < Test::Unit::TestCase
|
|
111
113
|
end
|
112
114
|
|
113
115
|
def test_dictionary_method_missing
|
114
|
-
assert_equal [["
|
116
|
+
assert_equal [["beijing",2],["shanghai",1]],Dictionary.student_city(:locale => :en)
|
115
117
|
end
|
116
118
|
|
117
119
|
def test_dictionary_method_missing_with_locale
|
118
|
-
assert_equal [["上海", 1],
|
120
|
+
assert_equal [["上海", 1],["北京", 2]],Dictionary.student_city(:locale => :zh)
|
119
121
|
end
|
120
122
|
|
121
123
|
def test_delete_dicts_cache
|
122
124
|
@dy_wuhan=Dictionary.create! name_en: "wuhan",name_zh: "武汉",name_fr: "wuhan",dict_type_id: @dt_stu_city.id
|
123
|
-
assert_equal [["
|
125
|
+
assert_equal [["beijing", 2],["shanghai",1],["wuhan", 3]],Dictionary.student_city(:locale => :en)
|
124
126
|
@dy_wuhan.destroy
|
125
|
-
assert_equal [["
|
127
|
+
assert_equal [["beijing", 2],["shanghai",1]],Dictionary.student_city(:locale => :en)
|
126
128
|
assert_equal [@dy_shanghai,@dy_beijing],Dictionary.student_city
|
127
129
|
end
|
128
130
|
|
metadata
CHANGED
@@ -2,16 +2,15 @@
|
|
2
2
|
name: rails_dictionary
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.0.
|
5
|
+
version: 0.0.9
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
|
-
-
|
8
|
+
- Raykin Lee
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2011-
|
14
|
-
default_executable:
|
13
|
+
date: 2011-04-14 00:00:00 Z
|
15
14
|
dependencies: []
|
16
15
|
|
17
16
|
description: Rails plugin for mapping static data of web application to Dictionary class
|
@@ -25,19 +24,26 @@ extra_rdoc_files: []
|
|
25
24
|
|
26
25
|
files:
|
27
26
|
- .gitignore
|
27
|
+
- CHANGELOG
|
28
28
|
- Gemfile
|
29
29
|
- README.rdoc
|
30
30
|
- Rakefile
|
31
31
|
- lib/rails_dictionary.rb
|
32
|
-
- lib/rails_dictionary/acts_as_dict_slave.rb
|
33
|
-
- lib/rails_dictionary/acts_as_dict_type.rb
|
34
|
-
- lib/rails_dictionary/acts_as_dictionary.rb
|
35
32
|
- lib/rails_dictionary/array_core_ext.rb
|
33
|
+
- lib/rails_dictionary/engine.rb
|
34
|
+
- lib/rails_dictionary/models/active_record_extension.rb
|
35
|
+
- lib/rails_dictionary/models/acts_as_dict_slave.rb
|
36
|
+
- lib/rails_dictionary/models/acts_as_dict_type.rb
|
37
|
+
- lib/rails_dictionary/models/acts_as_dictionary.rb
|
38
|
+
- lib/rails_dictionary/railtie.rb
|
36
39
|
- lib/rails_dictionary/version.rb
|
37
40
|
- lib/tasks/dicts.rake
|
41
|
+
- log/development.log
|
38
42
|
- rails_dictionary.gemspec
|
43
|
+
- spec/fake_app.rb
|
44
|
+
- spec/rails_dictionary_spec.rb
|
45
|
+
- spec/spec_helper.rb
|
39
46
|
- test/rails_dictionary_test.rb
|
40
|
-
has_rdoc: true
|
41
47
|
homepage: https://github.com/raykin/rails_dictionary
|
42
48
|
licenses: []
|
43
49
|
|
@@ -61,9 +67,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
61
67
|
requirements: []
|
62
68
|
|
63
69
|
rubyforge_project: rails_dictionary
|
64
|
-
rubygems_version: 1.
|
70
|
+
rubygems_version: 1.7.2
|
65
71
|
signing_key:
|
66
72
|
specification_version: 3
|
67
73
|
summary: dictionary data for web application
|
68
74
|
test_files:
|
75
|
+
- spec/fake_app.rb
|
76
|
+
- spec/rails_dictionary_spec.rb
|
77
|
+
- spec/spec_helper.rb
|
69
78
|
- test/rails_dictionary_test.rb
|
79
|
+
has_rdoc:
|
@@ -1,69 +0,0 @@
|
|
1
|
-
module ActsAsDictSlave
|
2
|
-
def self.included(base)
|
3
|
-
base.extend(ClassMethods)
|
4
|
-
end
|
5
|
-
|
6
|
-
module ClassMethods
|
7
|
-
# :except - remove dict mapping column
|
8
|
-
# :add - add dict mapping column
|
9
|
-
# :locale - add and initialize class attribute default_dict_locale
|
10
|
-
def acts_as_dict_slave(ops={})
|
11
|
-
class_attribute :default_dict_locale,:instance_writer => false
|
12
|
-
cattr_accessor :dict_mapping_columns,:instance_writes => false
|
13
|
-
self.default_dict_locale = ops[:locale] if ops[:locale]
|
14
|
-
self.dict_mapping_columns = dict_columns(ops)
|
15
|
-
unless dict_mapping_columns.nil?
|
16
|
-
add_dynamic_column_method
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
# return columns that exist in DictType#tab_and_column
|
21
|
-
def columns_in_dict_type
|
22
|
-
DictType.tab_and_column[self.name.underscore.to_sym]
|
23
|
-
end
|
24
|
-
|
25
|
-
# columns which map to dictionary
|
26
|
-
def dict_columns(ops={})
|
27
|
-
conf={except: nil,add: nil}
|
28
|
-
conf.update(ops)
|
29
|
-
cidt=self.columns_in_dict_type || []
|
30
|
-
cidt.delete(conf[:except])
|
31
|
-
case conf[:add]
|
32
|
-
when String
|
33
|
-
cidt.push(conf[:add])
|
34
|
-
when Array
|
35
|
-
cidt.push(*conf[:add])
|
36
|
-
else nil
|
37
|
-
end
|
38
|
-
cidt.uniq! || cidt
|
39
|
-
end
|
40
|
-
|
41
|
-
# add a belongs_to(Dictionary) association and a named_{column} method
|
42
|
-
def add_dynamic_column_method
|
43
|
-
self.extend(DynamicInsMethods)
|
44
|
-
dict_mapping_columns.each { |e| belongs_to "#{e.to_s}_dict".to_sym,class_name: "Dictionary",foreign_key: e }
|
45
|
-
dict_mapping_columns.each { |ele| named_dict_value ele.to_sym }
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
module DynamicInsMethods
|
50
|
-
# generate dynamic instance method named_column to slave model
|
51
|
-
# def named_city(locale=nil)
|
52
|
-
# locale = locale.presence || default_dict_locale.presence || :en
|
53
|
-
# locale = "name_#{locale}"
|
54
|
-
# self.send(city_dict).try(:send,locale)
|
55
|
-
# end
|
56
|
-
def named_dict_value(method_name)
|
57
|
-
belongs_to_name="#{method_name.to_s}_dict".to_sym
|
58
|
-
method_name="named_#{method_name.to_s}"
|
59
|
-
define_method(method_name) do | locale=nil |
|
60
|
-
locale = locale.presence || default_dict_locale.presence || :en
|
61
|
-
locale = "name_#{locale}"
|
62
|
-
self.send(belongs_to_name).try(:send,locale)
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
end
|
67
|
-
|
68
|
-
end
|
69
|
-
ActiveRecord::Base.class_eval { include ActsAsDictSlave }
|
@@ -1,56 +0,0 @@
|
|
1
|
-
module ActsAsDictType
|
2
|
-
def self.included(base)
|
3
|
-
base.extend(ClassMethods)
|
4
|
-
end
|
5
|
-
|
6
|
-
module ClassMethods
|
7
|
-
def acts_as_dict_type(ops={})
|
8
|
-
self.class_eval do
|
9
|
-
has_many :dictionaries
|
10
|
-
include InstanceMethods
|
11
|
-
validates_uniqueness_of :name
|
12
|
-
after_save :delete_all_caches
|
13
|
-
after_destroy :delete_all_caches
|
14
|
-
|
15
|
-
def self.all_types
|
16
|
-
Rails.cache.fetch("DictType.all_types") { all.map(&:name) }.dup
|
17
|
-
end
|
18
|
-
|
19
|
-
def self.cached_all
|
20
|
-
Rails.cache.fetch("DictType.cached_all") { all }.dup
|
21
|
-
end
|
22
|
-
|
23
|
-
# short method to transfer id to name or name to id
|
24
|
-
def self.revert(arg)
|
25
|
-
if arg.is_a?(String)
|
26
|
-
DictType.where(name: arg).try(:first).id
|
27
|
-
elsif arg.is_a?(Fixnum)
|
28
|
-
DictType.where(id: arg).try(:first).name
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
# TODO: get a more accurate method name
|
33
|
-
# Parse the name value to get which column and model are listed in DictType
|
34
|
-
def self.tab_and_column
|
35
|
-
tab_and_column={}
|
36
|
-
# There are two chooses,one is subclasses the other is descendants,
|
37
|
-
# I don't know which is better,but descendants contains subclass of subclass,it contains more.
|
38
|
-
# Class like +Ckeditor::Asset+ transfer to "ckeditor/asset",but we can not naming method like that,
|
39
|
-
# So it still not support, the solution may be simple,just make another convention to escape "/"
|
40
|
-
all_model_class=ActiveRecord::Base.descendants.map(&:name).map(&:underscore)
|
41
|
-
tab_and_column=all_types.extract_to_hash(all_model_class)
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
module InstanceMethods
|
47
|
-
def delete_all_caches
|
48
|
-
Rails.cache.delete("DictType.all_types")
|
49
|
-
Rails.cache.delete("DictType.cached_all")
|
50
|
-
return true
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
end
|
55
|
-
end
|
56
|
-
ActiveRecord::Base.class_eval { include ActsAsDictType }
|
@@ -1,57 +0,0 @@
|
|
1
|
-
module ActsAsDictionary
|
2
|
-
def self.included(base)
|
3
|
-
base.extend(ClassMethods)
|
4
|
-
end
|
5
|
-
|
6
|
-
module ClassMethods
|
7
|
-
|
8
|
-
def acts_as_dictionary
|
9
|
-
belongs_to :dict_type
|
10
|
-
after_save :delete_dicts_cache
|
11
|
-
after_destroy :delete_dicts_cache
|
12
|
-
|
13
|
-
# Return an instance of array
|
14
|
-
def dict_type_name_eq(method_name)
|
15
|
-
joins(:dict_type).where("dict_types.name" => method_name).all
|
16
|
-
end
|
17
|
-
#Following design is not successed with sqlite3 in test
|
18
|
-
# scope :dict_type_name_eq,lambda {|method_name| joins(:dict_type).where("dict_types.name" => method_name)}
|
19
|
-
# remove the all seems not pass test,return following errors
|
20
|
-
# TypeError: no marshal_dump is defined for class SQLite3::Database
|
21
|
-
include InstanceMethods
|
22
|
-
# Generate methods like Dictionary.student_city
|
23
|
-
# Dictionary.student_city - a list of dictionary object which dict type is student_city
|
24
|
-
# Dictionary.student_city(:locale => :zh) - a select format array which can be used
|
25
|
-
# in view method select as choice params
|
26
|
-
# Is this design good?
|
27
|
-
def self.method_missing(method_id,options={},&block)
|
28
|
-
method_name=method_id.to_s.downcase
|
29
|
-
if DictType.all_types.include? method_id.to_s
|
30
|
-
Rails.cache.fetch("Dictionary.#{method_name}") { dict_type_name_eq(method_name) }
|
31
|
-
listed_attr=Rails.cache.read("Dictionary.#{method_name}").dup
|
32
|
-
# Instance of activerelation can not be dup?
|
33
|
-
if options.keys.include? :locale or options.keys.include? "locale"
|
34
|
-
locale="name_#{ options[:locale] }"
|
35
|
-
listed_attr.map! { |a| [a.send(locale),a.id] }
|
36
|
-
listed_attr.sort {|a,b| a.last <=> b.last } # maybe remove this line
|
37
|
-
else
|
38
|
-
listed_attr
|
39
|
-
end
|
40
|
-
else
|
41
|
-
super
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
module InstanceMethods
|
49
|
-
def delete_dicts_cache
|
50
|
-
method_name=DictType.revert(self.dict_type_id)
|
51
|
-
Rails.cache.delete("Dictionary.#{method_name}")
|
52
|
-
return true
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
end
|
57
|
-
ActiveRecord::Base.class_eval { include ActsAsDictionary }
|