rails_dictionary 0.2.3 → 0.3.pre.rc1

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