rails_dictionary 0.2.3 → 0.3.pre.rc1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA256:
3
- metadata.gz: a3cc435df2a7cdf214af4e1843755b3c56b56edc2680fe644a8f38a991236567
4
- data.tar.gz: 573815dc3115fc14232cbf0cca60c2fd3721f4676ff68a87541abf465a68f1f1
2
+ SHA1:
3
+ metadata.gz: 309d2e3ca49c9e564523d61307014d44987b7561
4
+ data.tar.gz: e6ea71c9fc377893c23883a0a0f08d001bc2f328
5
5
  SHA512:
6
- metadata.gz: cb51afd93704440a426dee3e366ca80912c578be74836466afe7d3d567fd5880fff2fa99de4d9dd0f9fc988566e96b34be0e0ef2498ac173ae85232fa71f835f
7
- data.tar.gz: 04c1f6992583f8b9d76675d912a4fb9cc5068b1254d928fc07ad92bb77df616d212d8d4041fc3563b313da1abbe6a9f52a2343e411992e3d595987525d9f560b
6
+ metadata.gz: 0daca999ef3ad0ff8888e5e2ca7ae85fe81bbc62c5e1a7c696f51f7d904eeb5bb985200a471306efb75f5c4ce3e599b5cd2e57513f07ef7d1a95f26294b0acef
7
+ data.tar.gz: 987fdd01ad2d98bb9a211eda4aed92cedbe9469f4cfaf7a1d6b6803a52f41315ba9c4239e43826a620fb7d289fcc9c53dd206942db0f6f60ccd25ebe206f9d54
data/CHANGELOG CHANGED
@@ -1,3 +1,7 @@
1
+ 0.3.1
2
+ Replace rspec with minitest.
3
+ Replace dict_types with STI.
4
+
1
5
  0.2
2
6
  Add Support For Rails4
3
7
 
data/Gemfile CHANGED
@@ -4,8 +4,7 @@ source "http://rubygems.org"
4
4
  gemspec
5
5
 
6
6
  group :development,:test do
7
- gem 'rspec'
7
+ gem 'minitest'
8
8
  gem 'byebug'
9
- gem "rspec-rails"
10
9
  gem 'sqlite3'
11
10
  end
@@ -1,6 +1,3 @@
1
- = Plan
2
- Working on sti branch will target to 0.3 version which will not compatible with 0.1 and 0.2
3
-
4
1
  = Intro
5
2
  There are always some static data(not static page) in application.For example product type or student diploma type.
6
3
 
@@ -29,7 +26,7 @@ See change log for brief info.
29
26
  == Sample
30
27
  Run following task will give you a simple start.Maybe you should try it in a new application first.
31
28
  rake dicts:generate
32
- rake dicts:sample_slave
29
+ rake dicts:sample_consumer
33
30
  rake db:migrate
34
31
  rake dicts:sample_data
35
32
  These task are just generate table dictionaries,dict_types,students and some sample data.The data should be
@@ -80,7 +77,7 @@ Here is what should be like.Student model can be other models.
80
77
  end
81
78
 
82
79
  class Student < ActiveRecord::Base
83
- # use acts_as_dict_slave when your rails_dictionary version < 0.2
80
+ # use acts_as_dict_consumer when your rails_dictionary version < 0.2
84
81
  acts_as_dict_consumer
85
82
  end
86
83
 
data/Rakefile CHANGED
@@ -5,6 +5,7 @@ task :default => [:test]
5
5
 
6
6
  desc "Running Test"
7
7
  task :test do
8
- # system "ruby -I . test/rails_dictionary_test.rb " # used with version 0.0.8 or before it
9
- system "rspec spec/rails_dictionary_spec.rb"
8
+ system 'ruby test/rails_dictionary_test.rb'
9
+ system 'ruby test/acts_as_consumer_test.rb'
10
+ system 'ruby test/lookup_test.rb'
10
11
  end
data/Readme.md ADDED
@@ -0,0 +1,10 @@
1
+ ## Usage
2
+
3
+ Version 0.3 is not backward compatible!!
4
+ Only for version 0.3.
5
+
6
+ See [README.rdoc](https://github.com/raykin/rails_dictionary/blob/sti/README.v2.0.rdoc) for version 0.2.
7
+
8
+ ### Changes from 0.2 to 0.3
9
+
10
+ Locale feature removed. Please use i18n.
@@ -5,44 +5,34 @@ module RailsDictionary
5
5
  end
6
6
 
7
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
-
12
- has_many :dictionaries
13
- validates_uniqueness_of :name
14
- after_save :delete_all_caches
15
- after_destroy :delete_all_caches
16
-
17
- include RailsDictionary::ActsAsDictType
18
- end
19
8
 
20
9
  def acts_as_dictionary
21
-
22
- belongs_to :dict_type
23
- after_save :delete_dicts_cache
24
- after_destroy :delete_dicts_cache
25
- scope :dict_type_name_eq, lambda { |name| joins(:dict_type).where("dict_types.name" => name) }
26
-
27
- include RailsDictionary::ActsAsDictionary
10
+ include ActsAsDictionary
11
+ validates_uniqueness_of :name, scope: [inheritance_column]
28
12
  end
29
13
 
30
- # Ex: acts_as_dict_slave :add => :category
31
- # :except - remove dict mapping column
32
- # :add - add dict mapping column
33
- # :locale - add and initialize class attribute default_dict_locale
34
- def acts_as_dict_slave(ops={})
35
- include RailsDictionary::ActsAsDictSlave
36
- class_attribute :default_dict_locale, :instance_writer => false
37
- cattr_accessor :dict_mapping_columns, :instance_writer => false
38
- self.default_dict_locale = ops[:locale] if ops[:locale]
39
- self.dict_mapping_columns = dict_columns(ops)
40
- unless dict_mapping_columns.nil?
41
- add_dynamic_column_method
14
+ # Ex: acts_as_dict_consumer on: :city
15
+ #
16
+ # on: - add dict mapping columns, can be string or array of string
17
+ # relation_type: - belongs_to/many_to_many, default is belongs_to. has_many Not supported yet
18
+ # class_name: - Dictionary class name
19
+ # locale: - add and initialize class attribute default_dict_locale
20
+ def acts_as_dict_consumer(opts={})
21
+ include ActsAsDictConsumer
22
+ case opts[:on]
23
+ when Array
24
+ opts[:on].each do |on_value|
25
+ build_dict_relation(opts.merge(on: on_value))
26
+ end
27
+ when String, Symbol
28
+ build_dict_relation(opts)
29
+ else
30
+ raise TypeError, 'Wrong value of params on'
42
31
  end
43
32
  end
44
33
 
45
- alias_method :acts_as_dict_consumer, :acts_as_dict_slave
46
34
  end
47
35
  end
48
36
  end
37
+
38
+ ::ActiveRecord::Base.send :include, RailsDictionary::ActiveRecordExtension
@@ -0,0 +1,77 @@
1
+ module RailsDictionary
2
+ module ActsAsDictConsumer
3
+ def self.included(base)
4
+ base.extend(ClassMethods)
5
+ end
6
+
7
+ module ClassMethods
8
+
9
+ # Generate dynamic instance method named_column to consumer model
10
+ # def named_city(locale=nil)
11
+ # locale = locale.presence || default_dict_locale.presence || :en
12
+ # locale = "name_#{locale}"
13
+ # self.send(city_dict).try(:send,locale)
14
+ # end
15
+ # alias_method :city_name, :named_city
16
+ # def named_dict_value(method_name)
17
+ # belongs_to_name="#{method_name}_dict".to_sym
18
+ # origin_method_name = method_name
19
+ # method_name="named_#{method_name}"
20
+ # define_method(method_name) do | locale=nil |
21
+ # locale = locale.presence || default_dict_locale.presence || :en
22
+ # locale = "name_#{locale}"
23
+ # self.send(belongs_to_name).try(:send,locale)
24
+ # end
25
+ # alias_method "#{origin_method_name}_name".to_sym, method_name.to_sym
26
+ # end
27
+
28
+ # Build dynamic method column_name= to the consumer model
29
+ #
30
+ # ex:
31
+ # def city_name=(value, options = {})
32
+ # send "city=", dictionary_obj
33
+ # end
34
+ def dict_name_equal
35
+ relation_name = @dict_relation_name
36
+ relation_type = @dict_relation_type
37
+ method_name = "#{relation_name}_name="
38
+ class_opt = @opt
39
+ define_method(method_name) do |value, options={}|
40
+ dicts = RailsDictionary.dclass.where(name: Array(value), type: class_opt[:class_name])
41
+ if dicts
42
+ if relation_type == :belongs_to
43
+ send "#{relation_name}=", dicts.first
44
+ elsif relation_type == :many_to_many
45
+ send "#{relation_name}=", dicts.map(&:id)
46
+ else
47
+ raise "Wrong relation method name: #{relation_type}"
48
+ end
49
+ else
50
+ # do nothing ?
51
+ end
52
+ end
53
+ end
54
+
55
+ # dont think instance var is a good sollution
56
+ # cause the consumer class will include other lib too
57
+ def build_dict_relation(opt)
58
+ @opt = opt
59
+ @dict_relation_name = @opt.delete :on
60
+ raise 'params on cant be nil' if @dict_relation_name.nil?
61
+ @dict_relation_type = @opt.delete(:relation_type) || :belongs_to
62
+ # @opt[:foreign_key] ||= "#{@dict_relation_name}_id"
63
+ @opt[:class_name] ||= "#{RailsDictionary.config.dictionary_klass}::#{@dict_relation_name.to_s.singularize.camelize}"
64
+ ::RailsDictionary.init_dict_sti_class(@opt[:class_name])
65
+ if @dict_relation_type.to_sym == :belongs_to
66
+ send @dict_relation_type, @dict_relation_name, @opt
67
+ elsif @dict_relation_type.to_sym == :many_to_many
68
+ # no code required?
69
+ # build_many_to_many_dict_relation
70
+ end
71
+ dict_name_equal
72
+ end
73
+
74
+ end # END ClassMethods
75
+
76
+ end
77
+ end
@@ -2,83 +2,24 @@ module RailsDictionary
2
2
  module ActsAsDictionary
3
3
  def self.included(base)
4
4
  base.extend(ClassMethods)
5
- base.send :include,InstanceMethods
5
+ base.send :include, InstanceMethods
6
6
  end
7
7
 
8
8
  module ClassMethods
9
9
 
10
- # For rails3
11
- # I thought it would be better to define a method in method_missing, Not just generate cache.
12
- # Cause cache can not store ActiveRecord
13
- # Generate methods like Dictionary.student_city
14
- # Dictionary.student_city - a list of dictionary object which dict type is student_city
15
- # Dictionary.student_city(:locale => :zh) - a select format array which can be used
16
- # in view method select as choice params
17
- # Programmer DOC && TODO:
18
- # rethink about the cache.
19
- # cache methods like Dictionary.student_city(:locale => :zh,:sort => :name_fr)
20
- # but not cache Dictionary.student_city, return it as relation
21
- #
22
- # Remove nil noise,if listed_attr =[[nil, 201], [nil, 203], [nil, 202], ["Sciences", 200]]
23
- # the sort would be failed of ArgumentError: comparison of Array with Array failed
24
- # split this method ,make it more short and maintainance
25
- def method_missing(method_id,options={})
26
- if DictType.all_types.include? method_id
27
- method_name=method_id.to_s.downcase
28
- # TODO: If cache engine is failed, then the code will failed with null cant dup
29
- Rails.cache.fetch("Dictionary.#{method_name}") { dict_type_name_eq(method_name).to_a }
30
- listed_attr=Rails.cache.read("Dictionary.#{method_name}").dup # Instance of ActiveRecord::Relation can not be dup?
31
- build_scope_method(method_id)
32
- if options.keys.include? :locale or options.keys.include? "locale"
33
- locale="name_#{ options[:locale] }"
34
- sort_block=sort_dicts(options)
35
- listed_attr.sort!(&sort_block) if sort_block
36
- listed_attr.map! { |a| [a.send(locale),a.id] }.reject! {|ele| ele.first.nil?}
37
- end
38
- listed_attr
39
- else
40
- super
41
- end
42
- end
43
-
44
- # Override this method to get customed sort block
45
- def sort_dicts(options)
46
- if options.keys.include? :locale or options.keys.include? "locale"
47
- locale="name_#{ options[:locale] }"
48
- if options[:locale].to_sym == :zh
49
- Proc.new { |a,b| a.send(locale).encode('GBK') <=> b.send(locale).encode('GBK') }
50
- else
51
- Proc.new { |a,b| a.send(locale).downcase <=> b.send(locale).downcase }
52
- end
53
- else
54
- false
55
- end
56
- end
57
-
58
- def respond_to?(name, include_private=false)
59
- DictType.all_types.include?(name) || super
60
- end
61
-
62
- private
63
-
64
- def build_scope_method(name)
65
- scope_method_name = "scoped_#{name}".to_sym
66
- unless respond_to? scope_method_name
67
- define_singleton_method scope_method_name do
68
- # see http://stackoverflow.com/questions/18198963/with-rails-4-model-scoped-is-deprecated-but-model-all-cant-replace-it
69
- # for usage of where(nil)
70
- dict_type_name_eq(name).where(nil)
71
- end
72
- end
10
+ # override to make sure STI class init first
11
+ def new(opts)
12
+ type_opt = opts.with_indifferent_access[inheritance_column]
13
+ RailsDictionary.init_dict_sti_class(type_opt) if type_opt
14
+ super
73
15
  end
74
16
 
75
17
  end # End ClassMethods
76
18
 
77
19
  module InstanceMethods
78
- def delete_dicts_cache
79
- method_name=DictType.revert(self.dict_type_id)
80
- Rails.cache.delete("Dictionary.#{method_name}")
81
- return true
20
+ def type=(name)
21
+ ::RailsDictionary.init_dict_sti_class(name)
22
+ super
82
23
  end
83
24
  end # End InstanceMethods
84
25
 
@@ -1,3 +1,3 @@
1
1
  module RailsDictionary
2
- VERSION = "0.2.3"
2
+ VERSION = "0.3-rc1"
3
3
  end
@@ -1,10 +1,33 @@
1
- require File.join(File.dirname(__FILE__), "rails_dictionary/array_core_ext")
2
1
  require File.join(File.dirname(__FILE__), "rails_dictionary/models/active_record_extension")
3
- require File.join(File.dirname(__FILE__), "rails_dictionary/models/acts_as_dict_type")
4
2
  require File.join(File.dirname(__FILE__), "rails_dictionary/models/acts_as_dictionary")
5
- require File.join(File.dirname(__FILE__), "rails_dictionary/models/acts_as_dict_slave")
3
+ require File.join(File.dirname(__FILE__), "rails_dictionary/models/acts_as_dict_consumer")
6
4
 
7
5
  # rake tasks not autoload in Rails4
8
- Dir[File.expand_path('../tasks/**/*.rake',__FILE__)].each { |ext| load ext } if defined?(Rake)
6
+ # todo: may add migration(from 0.2 to 0.3) task
7
+ # Dir[File.expand_path('../tasks/**/*.rake',__FILE__)].each { |ext| load ext } if defined?(Rake)
9
8
 
10
- ::ActiveRecord::Base.send :include, RailsDictionary::ActiveRecordExtension
9
+ module RailsDictionary
10
+
11
+ def self.config
12
+ Config.instance
13
+ end
14
+
15
+ class Config < Struct.new(:dictionary_klass, :defined_sti_klass)
16
+ include Singleton
17
+ end
18
+
19
+ config.dictionary_klass = :Dictionary
20
+ config.defined_sti_klass = []
21
+
22
+ def self.dclass
23
+ @dclass ||= config.dictionary_klass.to_s.constantize
24
+ end
25
+
26
+ def self.init_dict_sti_class(klass)
27
+ unless config.defined_sti_klass.include?(klass) || Module.const_defined?(klass)
28
+ subklass = klass.sub "#{config.dictionary_klass}::", ''
29
+ dclass.const_set subklass, Class.new(dclass)
30
+ config.defined_sti_klass.push(klass)
31
+ end
32
+ end
33
+ end
data/lib/tasks/dicts.rake CHANGED
@@ -2,20 +2,22 @@
2
2
  namespace :dicts do
3
3
  desc "Generate dictionary and dict_type model"
4
4
  task :generate do
5
- system "rails g model dictionary name_en:string name_zh:string name_fr:string dict_type_id:integer"
6
- system "rails g model dict_type name:string"
5
+ system "rails g model dictionary name_en name_zh name_fr type"
7
6
  end
7
+
8
8
  desc "Generate student model"
9
- task :sample_slave do
9
+ task :sample_consumer do
10
10
  system "rails g model student email:string city:integer school:integer"
11
11
  end
12
+
12
13
  desc "Generate sample data for rails_dictionary gem"
13
14
  task :sample_data => [:environment] do
14
- @dt_stu_city=DictType.create! :name => "student_city"
15
- @dt_stu_school=DictType.create! :name => "student_school"
16
- @dy_shanghai=Dictionary.create! name_en: "shanghai",name_zh: "上海",name_fr: "shanghai",dict_type_id: @dt_stu_city.id
17
- @dy_beijing=Dictionary.create! name_en: "beijing",name_zh: "北京",name_fr: "Pékin",dict_type_id: @dt_stu_city.id
18
- @stu_beijing=Student.create! email: "beijing@dict.com",city: @dy_beijing.id
19
- @stu_shanghai=Student.create! email: "shanghai@dict.com",city: @dy_shanghai.id
15
+ # @dt_stu_city=DictType.create! name: "student_city"
16
+ # @dt_stu_school=DictType.create! name: "student_school"
17
+ @dy_shanghai=Dictionary.create! name_en: "shanghai", name_zh: "上海", name_fr: "shanghai", dict_type_id: @dt_stu_city.id
18
+ @dy_beijing=Dictionary.create! name_en: "beijing", name_zh: "北京", name_fr: "Pékin", dict_type_id: @dt_stu_city.id
19
+ @stu_beijing=Student.create! email: "beijing@dict.com", city: @dy_beijing.id
20
+ @stu_shanghai=Student.create! email: "shanghai@dict.com", city: @dy_shanghai.id
20
21
  end
22
+
21
23
  end
@@ -14,9 +14,10 @@ Gem::Specification.new do |s|
14
14
  s.rubyforge_project = "rails_dictionary"
15
15
 
16
16
  s.add_runtime_dependency 'rails', '> 4.0'
17
+ s.add_runtime_dependency 'database_cleaner'
17
18
 
18
19
  s.files = `git ls-files`.split("\n")
19
- s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
20
+ s.test_files = `git ls-files -- {test}/*`.split("\n")
20
21
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
21
22
  s.require_paths = ["lib"]
22
23
  end
@@ -0,0 +1,44 @@
1
+ require File.expand_path('test_helper', File.dirname(__FILE__))
2
+
3
+ class TestConsumer < TestSupporter
4
+
5
+ end
6
+
7
+ class TestConsumeOneColumn < TestConsumer
8
+ def setup
9
+ super
10
+ Student.acts_as_dict_consumer on: :city
11
+ end
12
+
13
+ def test_student_should_has_method_city
14
+ assert Student.new.respond_to?(:city), 'student should has method city'
15
+ assert Student.new.respond_to?(:create_city), 'student should has method create_city'
16
+ assert_includes RailsDictionary.config.defined_sti_klass, 'Dictionary::City'
17
+ end
18
+
19
+ def test_dictionary_city_class_exist
20
+ assert Dictionary.const_defined?('City'), 'Dictionary::City should be defined'
21
+ assert_includes RailsDictionary.config.defined_sti_klass, 'Dictionary::City'
22
+ end
23
+ end
24
+
25
+ class TestConsumeMultipleColumns < TestConsumer
26
+
27
+ def setup
28
+ super
29
+ Student.acts_as_dict_consumer on: [:city, :school]
30
+ end
31
+
32
+ def test_student_should_has_method_city
33
+ assert Student.new.respond_to?(:city), 'student should has method city'
34
+ assert Student.new.respond_to?(:create_city), 'student should has method create_city'
35
+ assert Student.new.respond_to?(:school), 'student should has method city'
36
+ assert Student.new.respond_to?(:create_school), 'student should has method city'
37
+ end
38
+
39
+ def test_dictionary_city_and_schoold_exist
40
+ assert Dictionary.const_defined?('City'), 'Dictionary::City should be defined'
41
+ assert Dictionary.const_defined?('School'), 'Dictionary::City should be defined'
42
+ assert_equal(['Dictionary::City', 'Dictionary::School'], RailsDictionary.config.defined_sti_klass)
43
+ end
44
+ end
data/test/fake_app.rb ADDED
@@ -0,0 +1,40 @@
1
+ # database
2
+ ActiveRecord::Base.configurations = {'test' => {:adapter => 'sqlite3', :database => ':memory:'}}
3
+ ActiveRecord::Base.establish_connection(:test)
4
+
5
+ # config
6
+ app = Class.new(Rails::Application)
7
+ app.config.active_support.deprecation = :log
8
+ app.config.eager_load = false
9
+ app.initialize!
10
+
11
+ class Dictionary < ActiveRecord::Base
12
+ acts_as_dictionary
13
+ end
14
+
15
+ class Student < ActiveRecord::Base
16
+ end
17
+
18
+ class Lookup < ActiveRecord::Base
19
+ end
20
+
21
+ #migrations
22
+ class CreateAllTables < ActiveRecord::Migration
23
+ def self.up
24
+ create_table(:dictionaries) {|t| t.string :name; t.string :type}
25
+ create_table(:students) do |t|
26
+ t.string :email
27
+ t.integer :city_id
28
+ t.integer :school_id
29
+ t.text :major_array
30
+ t.text :majors
31
+ end
32
+ create_table(:lookups) { |t| t.string :name; t.string :type }
33
+ end
34
+
35
+ def self.down
36
+ drop_table :dictionaries
37
+ drop_table :students
38
+ drop_table :lookups
39
+ end
40
+ end
@@ -0,0 +1,39 @@
1
+ require File.expand_path('test_helper', File.dirname(__FILE__))
2
+
3
+ module TestLookup
4
+
5
+ class TestAsDictionary < TestSupporter
6
+
7
+ def prepare_data
8
+ @art = Lookup.create!(name: 'art', type: 'Lookup::Major')
9
+ @math = Lookup.create!(name: 'math', type: 'Lookup::Major')
10
+ end
11
+
12
+ def setup
13
+ RailsDictionary.config.dictionary_klass = :Lookup
14
+ Lookup.acts_as_dictionary
15
+ super
16
+ end
17
+
18
+ def test_lookup
19
+ prepare_data
20
+ assert Lookup, Lookup::Major.superclass
21
+ end
22
+
23
+ def test_student_major_array
24
+ Student.acts_as_dict_consumer on: :major_array, relation_type: :many_to_many, class_name: 'Lookup::Major'
25
+ prepare_data
26
+ s = Student.new(major_array_name: ['art', 'math'])
27
+ s.save!; s.reload
28
+ assert_equal([@art.id, @math.id], s.major_array)
29
+ end
30
+
31
+ def test_student_with_majors
32
+ Student.acts_as_dict_consumer on: :majors, relation_type: :many_to_many
33
+ prepare_data
34
+ s = Student.new(majors_name: ['art', 'math'])
35
+ s.save!; s.reload
36
+ assert_equal([@art.id, @math.id], s.majors)
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,81 @@
1
+ require File.expand_path('test_helper', File.dirname(__FILE__))
2
+
3
+ class TestRailsDictionary < TestSupporter
4
+
5
+ class TestActiveRecordExtension < TestRailsDictionary
6
+ [:acts_as_dictionary, :acts_as_dict_consumer].each do |method_name|
7
+ define_method "test_#{method_name}_exists_ar" do
8
+ assert_includes ActiveRecord::Base.methods, method_name
9
+ end
10
+ end
11
+ end
12
+
13
+ class PreTestDatabase < TestRailsDictionary
14
+
15
+ def test_no_dictionary_data_exist_before
16
+ assert_equal 0, Dictionary.count, 'dicionaries table should be blank'
17
+ end
18
+ end
19
+
20
+ class TestInitSubDictClass < TestRailsDictionary
21
+ def setup
22
+ super
23
+ if Dictionary.const_defined? 'City'
24
+ Dictionary.send :remove_const, 'City'
25
+ RailsDictionary.config.defined_sti_klass.delete('Dictionary::City')
26
+ end
27
+ end
28
+
29
+ def test_dictionary_city_class_when_create_dictionary
30
+ @shanghai = Dictionary.create!(name: "shanghai", type: 'Dictionary::City')
31
+ assert Dictionary, Dictionary::City.superclass
32
+ end
33
+
34
+ def test_dictionary_city_class_when_assign_type
35
+ beijing = Dictionary.new(name: 'beijing')
36
+ beijing.type = 'Dictionary::City'
37
+ beijing.save!
38
+ assert Dictionary, Dictionary::City.superclass
39
+ end
40
+
41
+ def test_dictionary_city_class_when_assign_type_in_new
42
+ beijing = Dictionary.new(name: 'beijing', type: 'Dictionary::City')
43
+ beijing.save!
44
+ assert Dictionary, Dictionary::City.superclass
45
+ end
46
+
47
+ end
48
+ end
49
+
50
+ class TestWithDB < TestRailsDictionary
51
+ def setup
52
+ super
53
+ Dictionary.acts_as_dictionary
54
+ @beijing, @shanghai = prepare_city_data
55
+
56
+ @stu_beijing = Student.create! email: "beijing@dict.com", city_id: @shanghai.id
57
+ @stu_shanghai = Student.create! email: "shanghai@dict.com", city_id: @shanghai.id
58
+ end
59
+ end
60
+
61
+ class TestStudent < TestWithDB
62
+ def setup
63
+ super
64
+ Student.acts_as_dict_consumer on: [:city]
65
+ end
66
+
67
+ def test_city_name_equal_to_exist_dictionary_name
68
+ assert_equal 1, Dictionary.where(name: "beijing").count
69
+ @stu_shanghai.update_attributes city_name: "beijing"
70
+ assert_equal 'beijing', @stu_shanghai.reload.city.name
71
+ assert_equal 1, Dictionary.where(name: "beijing").count
72
+ end
73
+
74
+ def create_a_student_with_shanghai_city
75
+ s = Student.create(city_name: "shanghai")
76
+ s.reload
77
+ s.city.name.should == "shanghai"
78
+ assert_equal Dictionary.find_by(name: 'shanghai').id, s.city_id
79
+ assert_equal 1, Dictionary.where(name: "shanghai").count
80
+ end
81
+ end
@@ -0,0 +1,36 @@
1
+ # copy and modify the test design of kaminari(a paginate gem for Rails3)
2
+ RAILS_ENV = 'test'
3
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
4
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
5
+ require 'byebug'
6
+ require 'rack/test'
7
+ require 'rails'
8
+ require 'active_record'
9
+ require 'rails_dictionary'
10
+
11
+ ActiveRecord::Migration.verbose = false
12
+
13
+ require File.join(File.dirname(__FILE__), 'fake_app')
14
+
15
+ CreateAllTables.up unless ActiveRecord::Base.connection.table_exists? 'dictionaries'
16
+
17
+ Student.serialize :major_array, Array
18
+ Student.serialize :majors, Array
19
+
20
+ require 'minitest/autorun'
21
+ require 'database_cleaner'
22
+
23
+ DatabaseCleaner.strategy = :truncation
24
+
25
+ class TestSupporter < Minitest::Test
26
+ def setup
27
+ DatabaseCleaner.clean
28
+ end
29
+
30
+ def prepare_city_data
31
+ [
32
+ Dictionary.create!(name: 'beijing', type: 'Dictionary::City'),
33
+ Dictionary.create!(name: 'shanghai', type: 'Dictionary::City')
34
+ ]
35
+ end
36
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails_dictionary
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.3
4
+ version: 0.3.pre.rc1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Raykin Lee
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-07-24 00:00:00.000000000 Z
11
+ date: 2015-03-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - ">"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '4.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: database_cleaner
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
27
41
  description: Rails plugin for mapping static data of web application to Dictionary
28
42
  class
29
43
  email:
@@ -35,20 +49,21 @@ files:
35
49
  - ".gitignore"
36
50
  - CHANGELOG
37
51
  - Gemfile
38
- - README.rdoc
52
+ - README.v2.0.rdoc
39
53
  - Rakefile
54
+ - Readme.md
40
55
  - lib/rails_dictionary.rb
41
- - lib/rails_dictionary/array_core_ext.rb
42
56
  - lib/rails_dictionary/models/active_record_extension.rb
43
- - lib/rails_dictionary/models/acts_as_dict_slave.rb
44
- - lib/rails_dictionary/models/acts_as_dict_type.rb
57
+ - lib/rails_dictionary/models/acts_as_dict_consumer.rb
45
58
  - lib/rails_dictionary/models/acts_as_dictionary.rb
46
59
  - lib/rails_dictionary/version.rb
47
60
  - lib/tasks/dicts.rake
48
61
  - rails_dictionary.gemspec
49
- - spec/fake_app.rb
50
- - spec/rails_dictionary_spec.rb
51
- - spec/spec_helper.rb
62
+ - test/acts_as_consumer_test.rb
63
+ - test/fake_app.rb
64
+ - test/lookup_test.rb
65
+ - test/rails_dictionary_test.rb
66
+ - test/test_helper.rb
52
67
  homepage: https://github.com/raykin/rails_dictionary
53
68
  licenses: []
54
69
  metadata: {}
@@ -63,15 +78,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
63
78
  version: '0'
64
79
  required_rubygems_version: !ruby/object:Gem::Requirement
65
80
  requirements:
66
- - - ">="
81
+ - - ">"
67
82
  - !ruby/object:Gem::Version
68
- version: '0'
83
+ version: 1.3.1
69
84
  requirements: []
70
- rubygems_version: 3.0.1
85
+ rubyforge_project: rails_dictionary
86
+ rubygems_version: 2.2.2
71
87
  signing_key:
72
88
  specification_version: 4
73
89
  summary: dictionary data for web application
74
- test_files:
75
- - spec/fake_app.rb
76
- - spec/rails_dictionary_spec.rb
77
- - spec/spec_helper.rb
90
+ test_files: []
91
+ has_rdoc:
@@ -1,13 +0,0 @@
1
- class Array
2
- # Return a hash by compare two arrays
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
@@ -1,88 +0,0 @@
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
- if ActiveRecord::VERSION::STRING < '3.1'
12
- DictType.tab_and_column[self.name.underscore.to_sym]
13
- elsif DictType.table_exists?
14
- DictType.tab_and_column[self.name.underscore.to_sym]
15
- else
16
- []
17
- end
18
- end
19
-
20
- # columns which map to dictionary
21
- def dict_columns(ops={})
22
- conf = { except: nil, add: nil}
23
- conf.update(ops)
24
- cidt = columns_in_dict_type || []
25
- cidt.delete(conf[:except])
26
- case conf[:add]
27
- when String
28
- cidt.push(conf[:add])
29
- when Array
30
- cidt.push(*conf[:add])
31
- else nil
32
- end
33
- cidt.uniq! || cidt
34
- end
35
-
36
- # add a belongs_to(Dictionary) association and a named_{column} method
37
- def add_dynamic_column_method
38
- dict_mapping_columns.each { |e| belongs_to "#{e}_dict".to_sym, class_name: "Dictionary", foreign_key: e.to_sym }
39
- dict_mapping_columns.each { |ele| named_dict_value ele.to_sym }
40
- dict_mapping_columns.each { |ele| dict_name_equal ele.to_sym }
41
- end
42
-
43
- # Generate dynamic instance method named_column to consumer model
44
- # def named_city(locale=nil)
45
- # locale = locale.presence || default_dict_locale.presence || :en
46
- # locale = "name_#{locale}"
47
- # self.send(city_dict).try(:send,locale)
48
- # end
49
- # alias_method :city_name, :named_city
50
- def named_dict_value(method_name)
51
- belongs_to_name="#{method_name}_dict".to_sym
52
- origin_method_name = method_name
53
- method_name="named_#{method_name}"
54
- define_method(method_name) do | locale=nil |
55
- locale = locale.presence || default_dict_locale.presence || :en
56
- locale = "name_#{locale}"
57
- self.send(belongs_to_name).try(:send,locale)
58
- end
59
- alias_method "#{origin_method_name}_name".to_sym, method_name.to_sym
60
- end
61
-
62
- # Build dynamic method column_name= to the consumer model
63
- #
64
- # def city_name=(value, options = {})
65
- #
66
- #
67
- # end
68
- def dict_name_equal(colname)
69
- method_name = "#{colname}_name="
70
- belongs_to_name="#{colname}_dict".to_sym
71
- define_method(method_name) do |value, options={}|
72
- options.merge!(name_en: value)
73
- dict_type_id = DictType.revert("#{self.class.table_name.singularize}_#{colname}")
74
- options.merge!(dict_type_id: dict_type_id)
75
- exist_dictionary = Dictionary.where(options)
76
- if exist_dictionary.present?
77
- exist_id = exist_dictionary.first.id
78
- else
79
- exist_id = send("create_#{belongs_to_name}!", options).id
80
- end
81
- send "#{colname}=", exist_id
82
- end
83
- end
84
-
85
- end # END ClassMethods
86
-
87
- end
88
- end
@@ -1,56 +0,0 @@
1
- module RailsDictionary
2
- module ActsAsDictType
3
- def self.included(base)
4
- base.extend(ClassMethods)
5
- base.send :include, InstanceMethods
6
- base.mattr_accessor :whole_types
7
- end
8
-
9
- module ClassMethods
10
-
11
- def all_types
12
- whole_types ||
13
- self.whole_types = all.map(&:name).map(&:to_sym)
14
- end
15
-
16
- # short method to transfer id to name or name to id
17
- # TODO: cache it
18
- def revert(arg)
19
- if arg.is_a?(String)
20
- DictType.where(name: arg).first.try(:id)
21
- elsif arg.is_a?(Integer)
22
- DictType.where(id: arg).first.try(:name)
23
- end
24
- end
25
-
26
- #
27
- # Parse the name value to get which column and model(or table) are listed in DictType
28
- #
29
- # Programmer DOC:
30
- # There are two chooses to get subclass,one is subclasses the other is descendants,
31
- # I don't know which is better,but descendants contains subclass of subclass,it contains more.
32
- #
33
- # Class like +Ckeditor::Asset+ transfer to "ckeditor/asset",but we can not naming method like that,
34
- # So it still not support, the solution may be simple,just make another convention to escape "/"
35
- #
36
- # Seems this method did not need to be cached in production.
37
- # Because everyclass was cached before application was run.So after application was run, it never be run again.
38
- # TODO:
39
- # To cache this method output need more skills on how to caculate ActiveRecord::Base.descendants
40
- # Temply remove the cache
41
- # And add test for this situation
42
- def tab_and_column
43
- all_model_class=ActiveRecord::Base.descendants.map(&:name).map(&:underscore)
44
- all_types.map(&:to_s).extract_to_hash(all_model_class)
45
- end
46
- end
47
-
48
- module InstanceMethods
49
- def delete_all_caches
50
- self.whole_types = nil
51
- return true
52
- end
53
- end
54
-
55
- end
56
- end
data/spec/fake_app.rb DELETED
@@ -1,41 +0,0 @@
1
- # database
2
- ActiveRecord::Base.configurations = {'test' => {:adapter => 'sqlite3', :database => ':memory:'}}
3
-
4
- if ActiveRecord::VERSION::MAJOR >= 5
5
- ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => ':memory:')
6
- else
7
- ActiveRecord::Base.establish_connection(:test)
8
- end
9
-
10
- # config
11
- app = Class.new(Rails::Application)
12
- app.config.active_support.deprecation = :log
13
- app.config.eager_load = false
14
- app.initialize!
15
-
16
- # models
17
- class DictType < ActiveRecord::Base
18
- acts_as_dict_type
19
- end
20
-
21
- class Dictionary < ActiveRecord::Base
22
- acts_as_dictionary
23
- end
24
-
25
- class Student < ActiveRecord::Base
26
- end
27
-
28
- #migrations
29
- class CreateAllTables < ActiveRecord::VERSION::MAJOR >= 5 ? ActiveRecord::Migration[5.0] : ActiveRecord::Migration
30
- def self.up
31
- create_table(:dict_types) {|t| t.string :name}
32
- create_table(:dictionaries) {|t| t.string :name_en; t.string :name_zh ; t.string :name_fr ; t.integer :dict_type_id}
33
- create_table(:students) {|t| t.string :email; t.integer :city; t.integer :school}
34
- end
35
-
36
- def self.down
37
- drop_table :dict_types
38
- drop_table :dictionaries
39
- drop_table :students
40
- end
41
- end
@@ -1,122 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- require File.expand_path('spec_helper', File.dirname(__FILE__))
3
-
4
- describe RailsDictionary::ActiveRecordExtension do
5
- %w[acts_as_dict_type acts_as_dictionary acts_as_dict_slave].each do |method_name|
6
- it "contains #{method_name}" do
7
- expect(ActiveRecord::Base.methods).to include(method_name.to_sym)
8
- end
9
- end
10
- end
11
-
12
- describe RailsDictionary do
13
- let!(:dt_stu_city) { DictType.create! :name => "student_city" }
14
- let!(:dt_stu_school) { DictType.create! :name => "student_school" }
15
- let!(:dy_shanghai) { Dictionary.create! name_en: "shanghai",name_zh: "上海",name_fr: "shanghai",dict_type_id: dt_stu_city.id }
16
- let!(:dy_beijing) { Dictionary.create! name_en: "beijing",name_zh: "北京",name_fr: "Pékin",dict_type_id: dt_stu_city.id }
17
- let!(:stu_beijing) { Student.create! email: "beijing@dict.com",city: dy_beijing.id }
18
- let!(:stu_shanghai) { Student.create! email: "shanghai@dict.com",city: dy_shanghai.id }
19
-
20
- describe DictType do
21
- it "parse model and method" do
22
- expected_hash={:student => %w[city school]}
23
- DictType.tab_and_column.should == expected_hash
24
- end
25
-
26
- it "all types" do
27
- DictType.all_types.should == [:student_city, :student_school]
28
- end
29
-
30
- it "reverts id to name or name to id" do
31
- DictType.revert(dt_stu_school.id).should == "student_school"
32
- DictType.revert(100).should be_nil
33
- end
34
-
35
- it "after one record removed" do
36
- DictType.all_types
37
- DictType.whole_types.should == [:student_city, :student_school]
38
- dt_stu_school.destroy
39
- DictType.all_types.should == [:student_city]
40
- DictType.tab_and_column.should == Hash[:student,["city"]]
41
- end
42
- end
43
-
44
- describe Dictionary do
45
- it "should respond to student_city" do
46
- Dictionary.should respond_to(:student_city)
47
- end
48
-
49
- it "generate student_city method" do
50
- Dictionary.student_city.should == [dy_shanghai,dy_beijing]
51
- end
52
-
53
- it "generate student_city method with locale" do
54
- Dictionary.student_city(:locale => :zh).should == [["北京", 2],["上海",1]]
55
- end
56
-
57
- it "build scope method scoped_student_city" do
58
- Dictionary.scoped_student_city.class.name.should == "ActiveRecord::Relation"
59
- Dictionary.scoped_student_city.where(:id => 1).should == [dy_shanghai]
60
- end
61
-
62
- it "after record added or removed" do
63
- @dy_wuhan=Dictionary.create! name_en: "wuhan",name_zh: "武汉",name_fr: "wuhan",dict_type_id: dt_stu_city.id
64
- Dictionary.student_city(:locale => :en).should == [["beijing", 2],["shanghai",1],["wuhan", 3]]
65
- @dy_wuhan.destroy
66
- Dictionary.student_city(:locale => :en).should == [["beijing", 2],["shanghai",1]]
67
- end
68
-
69
- end
70
-
71
- describe Student do
72
- before :each do
73
- Student.acts_as_dict_slave
74
- end
75
- it "method of columns_in_dict_type and dict_columns" do
76
- Student.columns_in_dict_type.should == %w[city school]
77
- Student.dict_columns == %w[city school]
78
- end
79
-
80
- it "named_city with different locale" do
81
- stu_shanghai.named_city(:en).should == "shanghai"
82
- stu_shanghai.city_name(:en).should == "shanghai"
83
- stu_shanghai.city_name.should == "shanghai"
84
- stu_shanghai.named_city("").should == "shanghai"
85
- stu_beijing.named_city(:fr).should == "Pékin"
86
- end
87
-
88
- it "update city by set city_name to a value" do
89
- stu_shanghai.update_attributes city_name: "wuhan"
90
- stu_shanghai.reload.city_name.should == "wuhan"
91
- Dictionary.student_city.map(&:name_en).include?("wuhan").should be_truthy
92
- end
93
-
94
- it "update city by set city_name to an exist dictionary name" do
95
- Dictionary.where(name_en: "beijing").count.should eq(1)
96
- stu_shanghai.update_attributes city_name: "beijing"
97
- stu_shanghai.reload.city_name.should eq('beijing')
98
- Dictionary.where(name_en: "beijing").count.should eq(1)
99
- end
100
-
101
- it "create a student with shanghai city" do
102
- s = Student.create(city_name: "shanghai")
103
- s.reload
104
- s.city_name.should == "shanghai"
105
- end
106
-
107
- it "override default locale" do
108
- Student.acts_as_dict_slave :locale => :fr
109
- stu_beijing.named_city.should == "Pékin"
110
- end
111
- end
112
- end
113
-
114
- describe Array do
115
- let(:an_array) { %w[root student_school student_city admin_role] }
116
- let(:expected_hash) { {:student => %w[school city],:admin => %w[role]} }
117
- let(:blank_hash) { Hash.new }
118
- it "extract to hash" do
119
- an_array.extract_to_hash(%w[student admin]).should == expected_hash
120
- an_array.extract_to_hash(%w[students]).should == blank_hash
121
- end
122
- end
data/spec/spec_helper.rb DELETED
@@ -1,24 +0,0 @@
1
- # copy and modify the test design of kaminari(a paginate gem for Rails3)
2
- RAILS_ENV = 'test'
3
- $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
4
- $LOAD_PATH.unshift(File.dirname(__FILE__))
5
- require 'rails'
6
- require 'active_record'
7
- require 'action_controller/railtie'
8
- require 'action_view/railtie'
9
- require 'rails_dictionary'
10
-
11
- require File.join(File.dirname(__FILE__), 'fake_app')
12
-
13
- require 'rspec/rails'
14
-
15
- $stdout = StringIO.new # remove the noise output
16
-
17
- RSpec.configure do |config|
18
- config.use_transactional_fixtures = true
19
- # not work, must syntax error
20
- # config.expect_with(:rspec) { |c| c.syntax = :should }
21
- CreateAllTables.up unless ActiveRecord::Base.connection.table_exists? 'dict_types'
22
- end
23
-
24
- require 'byebug'