rails_dictionary 0.3.pre.rc1 → 0.4.0

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.
Files changed (40) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/ci.yml +33 -0
  3. data/.gitignore +5 -3
  4. data/CHANGELOG +19 -3
  5. data/Gemfile +10 -3
  6. data/Gemfile.lock +350 -0
  7. data/{README.v2.0.rdoc → README.rdoc} +33 -15
  8. data/Rakefile +10 -4
  9. data/docs/load-order.md +118 -0
  10. data/lib/rails_dictionary/active_record_extension.rb +48 -0
  11. data/lib/rails_dictionary/acts_as_dict_slave.rb +85 -0
  12. data/lib/rails_dictionary/acts_as_dict_type.rb +53 -0
  13. data/lib/rails_dictionary/acts_as_dictionary.rb +118 -0
  14. data/lib/rails_dictionary/array_core_ext.rb +10 -0
  15. data/lib/rails_dictionary/railtie.rb +11 -0
  16. data/lib/rails_dictionary/version.rb +1 -1
  17. data/lib/rails_dictionary.rb +64 -20
  18. data/lib/tasks/dicts.rake +9 -11
  19. data/pkg/rails_dictionary-0.2.2.gem +0 -0
  20. data/pkg/rails_dictionary-0.2.3.gem +0 -0
  21. data/rails_dictionary.gemspec +4 -4
  22. data/spec/fake_app.rb +29 -0
  23. data/spec/init_models.rb +12 -0
  24. data/spec/load_order/empty_boot_recovers.rb +19 -0
  25. data/spec/load_order/lazy_slave_model_boot.rb +25 -0
  26. data/spec/load_order/preseeded_boot.rb +22 -0
  27. data/spec/load_order/support.rb +41 -0
  28. data/spec/rails_dictionary_spec.rb +172 -0
  29. data/spec/spec_helper.rb +22 -0
  30. data/v1.0-roadmap.md +31 -0
  31. metadata +37 -38
  32. data/Readme.md +0 -10
  33. data/lib/rails_dictionary/models/active_record_extension.rb +0 -38
  34. data/lib/rails_dictionary/models/acts_as_dict_consumer.rb +0 -77
  35. data/lib/rails_dictionary/models/acts_as_dictionary.rb +0 -27
  36. data/test/acts_as_consumer_test.rb +0 -44
  37. data/test/fake_app.rb +0 -40
  38. data/test/lookup_test.rb +0 -39
  39. data/test/rails_dictionary_test.rb +0 -81
  40. data/test/test_helper.rb +0 -36
@@ -0,0 +1,19 @@
1
+ # Scenario: the app boots while the dict_types table does NOT exist yet
2
+ # (e.g. before the first migration). This used to leave the gem permanently
3
+ # broken. It must boot without raising, and recover once data appears.
4
+
5
+ require_relative "support"
6
+
7
+ define_models
8
+ build_and_initialize_app # to_prepare runs against an empty DB -> must not raise
9
+
10
+ assert !Dictionary.respond_to?(:country),
11
+ "no lookup methods exist when the table is missing at boot"
12
+
13
+ create_dict_tables
14
+ DictType.create!(name: "country") # runtime write -> after_save hook regenerates
15
+
16
+ assert Dictionary.respond_to?(:country),
17
+ "lookup method appears after a DictType is created at runtime"
18
+
19
+ puts "OK empty_boot_recovers"
@@ -0,0 +1,25 @@
1
+ # Scenario: DictType is loaded and seeded BEFORE the Dictionary model is ever
2
+ # referenced (mirrors a host app whose seed/fixture file writes dict_types
3
+ # first, with eager_load off so the slave model autoloads lazily). The
4
+ # after_save hook fires reload_dict_methods while the Dictionary model is not
5
+ # yet registered, so it must still work once Dictionary is finally loaded.
6
+
7
+ require_relative "support"
8
+
9
+ # Only DictType exists at boot; Dictionary is NOT defined/registered yet.
10
+ Object.const_set(:DictType, Class.new(ActiveRecord::Base))
11
+ DictType.class_eval { acts_as_dict_type }
12
+
13
+ create_dict_tables
14
+ build_and_initialize_app # to_prepare runs; Dictionary not registered yet
15
+
16
+ DictType.create!(name: "country") # after_save -> reload_dict_methods, empty registry
17
+
18
+ # Host app references the slave model for the first time here.
19
+ Object.const_set(:Dictionary, Class.new(ActiveRecord::Base))
20
+ Dictionary.class_eval { acts_as_dictionary }
21
+
22
+ assert Dictionary.respond_to?(:country),
23
+ "lookup method exists when the slave model is loaded after seeding"
24
+
25
+ puts "OK lazy_slave_model_boot"
@@ -0,0 +1,22 @@
1
+ # Scenario: the dict_types are already seeded BEFORE the app boots (the normal
2
+ # production case). Seeding here uses insert, which skips callbacks, so the
3
+ # after_save hook never fires. The lookup methods must therefore be generated
4
+ # by the Railtie's boot-time hook, with zero runtime writes.
5
+
6
+ require_relative "support"
7
+
8
+ define_models
9
+ create_dict_tables
10
+ DictType.insert({ name: "country" }) # seeded without firing callbacks
11
+
12
+ build_and_initialize_app # to_prepare -> RailsDictionary.load_dict_methods
13
+
14
+ assert Dictionary.respond_to?(:country),
15
+ "lookup method is generated at boot from pre-seeded data"
16
+
17
+ Dictionary.create!(name_en: "France", dict_type_id: DictType.first.id)
18
+
19
+ assert Dictionary.country.map(&:name_en) == ["France"],
20
+ "lookup returns seeded rows without any DictType runtime write"
21
+
22
+ puts "OK preseeded_boot"
@@ -0,0 +1,41 @@
1
+ # Shared setup for the isolated load-order scripts. Each scenario runs in its
2
+ # OWN process so it can control what exists in the database at boot time —
3
+ # something the shared rspec suite (single process, tables already created)
4
+ # cannot exercise.
5
+
6
+ $LOAD_PATH.unshift(File.expand_path("../../../lib", __FILE__))
7
+
8
+ require "rails"
9
+ require "active_record"
10
+ require "action_controller/railtie"
11
+ require "rails_dictionary"
12
+
13
+ ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
14
+
15
+ def define_models
16
+ # Assign the constant BEFORE running the macro so the class already has a
17
+ # name when it registers itself (mirrors `class Dictionary < AR::Base`).
18
+ Object.const_set(:DictType, Class.new(ActiveRecord::Base))
19
+ DictType.class_eval { acts_as_dict_type }
20
+ Object.const_set(:Dictionary, Class.new(ActiveRecord::Base))
21
+ Dictionary.class_eval { acts_as_dictionary }
22
+ end
23
+
24
+ def create_dict_tables
25
+ ActiveRecord::Schema.verbose = false
26
+ ActiveRecord::Schema.define do
27
+ create_table(:dict_types) { |t| t.string :name }
28
+ create_table(:dictionaries) { |t| t.string :name_en; t.integer :dict_type_id }
29
+ end
30
+ end
31
+
32
+ def build_and_initialize_app
33
+ app = Class.new(Rails::Application)
34
+ app.config.active_support.deprecation = :log
35
+ app.config.eager_load = false
36
+ app.initialize!
37
+ end
38
+
39
+ def assert(condition, message)
40
+ raise "FAIL: #{message}" unless condition
41
+ end
@@ -0,0 +1,172 @@
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
+
43
+ # Regression: an empty result must not be memoized as a permanent [].
44
+ # insert bypasses callbacks so whole_types is not reset for us.
45
+ it "recomputes after being first read while empty (load-order bug)" do
46
+ DictType.whole_types = []
47
+ DictType.insert({ name: "country" })
48
+ DictType.all_types.should include(:country)
49
+ end
50
+ end
51
+
52
+ describe Dictionary do
53
+ it "should respond to student_city" do
54
+ Dictionary.should respond_to(:student_city)
55
+ end
56
+
57
+ it "generate student_city method" do
58
+ Dictionary.student_city.should == [dy_shanghai,dy_beijing]
59
+ end
60
+
61
+ it "generate student_city method with locale" do
62
+ Dictionary.student_city(:locale => :zh).should == [["北京", 2],["上海",1]]
63
+ end
64
+
65
+ it "build scope method scoped_student_city" do
66
+ Dictionary.scoped_student_city.class.name.should == "ActiveRecord::Relation"
67
+ Dictionary.scoped_student_city.where(:id => 1).should == [dy_shanghai]
68
+ end
69
+
70
+ it "after record added or removed" do
71
+ @dy_wuhan=Dictionary.create! name_en: "wuhan",name_zh: "武汉",name_fr: "wuhan",dict_type_id: dt_stu_city.id
72
+ Dictionary.student_city(:locale => :en).should == [["beijing", 2],["shanghai",1],["wuhan", 3]]
73
+ @dy_wuhan.destroy
74
+ Dictionary.student_city(:locale => :en).should == [["beijing", 2],["shanghai",1]]
75
+ end
76
+
77
+ it "lists categories" do
78
+ Dictionary.categories.should == [:student_city, :student_school]
79
+ end
80
+
81
+ it "query: true bypasses a stale cache and hits the DB" do
82
+ Dictionary.student_city.map(&:name_en) # warm the cache
83
+ Dictionary.insert({ name_en: "wuhan", dict_type_id: dt_stu_city.id }) # no callbacks -> cache stays stale
84
+ Dictionary.student_city.map(&:name_en).should_not include("wuhan")
85
+ Dictionary.student_city(query: true).map(&:name_en).should include("wuhan")
86
+ end
87
+
88
+ it "adds and removes a dictionary entry" do
89
+ Dictionary.add(:student_city, "wuhan")
90
+ Dictionary.student_city(query: true).map(&:name_en).should include("wuhan")
91
+ Dictionary.remove(:student_city, "wuhan")
92
+ Dictionary.student_city(query: true).map(&:name_en).should_not include("wuhan")
93
+ end
94
+
95
+ it "returns form-ready options without the deprecated locale arg" do
96
+ Dictionary.options_for(:student_city).should == [["shanghai", 1], ["beijing", 2]]
97
+ Dictionary.options_for(:student_city, locale: :fr).should == [["shanghai", 1], ["Pékin", 2]]
98
+ end
99
+
100
+ it "deprecates the :locale option on the lookup method" do
101
+ expect(RailsDictionary.deprecator).to receive(:warn)
102
+ Dictionary.student_city(locale: :en)
103
+ end
104
+
105
+ end
106
+
107
+ describe Student do
108
+ # Run it because dict type value has cached at runtime but data inserted after cache created
109
+ before :each do
110
+ Student.acts_as_dict_slave
111
+ end
112
+ it "method of columns_in_dict_type and dict_columns" do
113
+ Student.columns_in_dict_type.should == %w[city school]
114
+ Student.dict_columns == %w[city school]
115
+ end
116
+
117
+ it "named_city with different locale" do
118
+ stu_shanghai.named_city(:en).should == "shanghai"
119
+ stu_shanghai.city_name(:en).should == "shanghai"
120
+ stu_shanghai.city_name.should == "shanghai"
121
+ stu_shanghai.named_city("").should == "shanghai"
122
+ stu_beijing.named_city(:fr).should == "Pékin"
123
+ end
124
+
125
+ it "update city by set city_name to a value" do
126
+ stu_shanghai.update city_name: "wuhan"
127
+ stu_shanghai.reload.city_name.should == "wuhan"
128
+ Dictionary.student_city.map(&:name_en).include?("wuhan").should be_truthy
129
+ end
130
+
131
+ it "update city by set city_name to an exist dictionary name" do
132
+ Dictionary.where(name_en: "beijing").count.should eq(1)
133
+ stu_shanghai.update city_name: "beijing"
134
+ stu_shanghai.reload.city_name.should eq('beijing')
135
+ Dictionary.where(name_en: "beijing").count.should eq(1)
136
+ end
137
+
138
+ it "create a student with shanghai city" do
139
+ s = Student.create(city_name: "shanghai")
140
+ s.reload
141
+ s.city_name.should == "shanghai"
142
+ end
143
+
144
+ it "override default locale" do
145
+ Student.acts_as_dict_consumer :locale => :fr
146
+ stu_beijing.named_city.should == "Pékin"
147
+ end
148
+
149
+ it "create a student with new city(reproduce conflict error with Rails5.2.0)" do
150
+ s = Student.new
151
+ s.city_name = 'Sedan'
152
+ s.save
153
+ s.reload
154
+ s.city_name.should == 'Sedan'
155
+ end
156
+ end
157
+ end
158
+
159
+ describe Array do
160
+ let(:an_array) { %w[root student_school student_city admin_role] }
161
+ let(:expected_hash) { {:student => %w[school city],:admin => %w[role]} }
162
+ let(:blank_hash) { Hash.new }
163
+ it "extract to hash" do
164
+ an_array.extract_to_hash(%w[student admin]).should == expected_hash
165
+ an_array.extract_to_hash(%w[students]).should == blank_hash
166
+ end
167
+
168
+ it "is deprecated in favor of RailsDictionary.extract_to_hash" do
169
+ expect(RailsDictionary.deprecator).to receive(:warn)
170
+ an_array.extract_to_hash(%w[student admin])
171
+ end
172
+ end
@@ -0,0 +1,22 @@
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 outputo
16
+
17
+ RSpec.configure do |config|
18
+ config.use_transactional_fixtures = true
19
+ config.expect_with(:rspec) { |c| c.syntax = [:should, :expect] }
20
+ CreateAllTables.up unless ActiveRecord::Base.connection.table_exists? 'dict_types'
21
+ require File.join(File.dirname(__FILE__), 'init_models')
22
+ end
data/v1.0-roadmap.md ADDED
@@ -0,0 +1,31 @@
1
+ # rails_dictionary Roadmap
2
+
3
+ **Core principle: intuitive API. Use naturally, learn nothing.**
4
+
5
+ ## v1.0 (breaking)
6
+
7
+ <!-- dev note: not suggested, should not add it -->
8
+ ### 1. One table, simple schema
9
+
10
+ Drop `dict_types`/`DictType`. Single `dictionaries` table: `id`, `name`, `category`.
11
+
12
+ ### 2. Clean API
13
+
14
+ Finish what v0.3 started: **remove** the deprecated `locale:` option and the
15
+ `Array#extract_to_hash` monkey-patch, and add the `uses_dictionary :city, :degree`
16
+ consumer macro.
17
+
18
+ <!-- how to do it? -->
19
+ ### 3. Auto caching
20
+
21
+ Cache primitive data (ids, names) by default — a derived, rebuildable index rather
22
+ than id-bearing AR records. No manual cache management.
23
+
24
+ ---
25
+
26
+ ## v1.1 (undecided)
27
+
28
+ <!-- delay it until dict_type table has really decided to remove -->
29
+ ### Generator
30
+
31
+ `rails g dictionary city degree major` to create the migration and seed categories.
metadata CHANGED
@@ -1,43 +1,34 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails_dictionary
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.pre.rc1
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Raykin Lee
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2015-03-21 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: rails
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - ">"
18
- - !ruby/object:Gem::Version
19
- version: '4.0'
20
- type: :runtime
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - ">"
25
- - !ruby/object:Gem::Version
26
- version: '4.0'
27
- - !ruby/object:Gem::Dependency
28
- name: database_cleaner
29
14
  requirement: !ruby/object:Gem::Requirement
30
15
  requirements:
31
16
  - - ">="
32
17
  - !ruby/object:Gem::Version
33
- version: '0'
18
+ version: '7.1'
19
+ - - "<"
20
+ - !ruby/object:Gem::Version
21
+ version: '9'
34
22
  type: :runtime
35
23
  prerelease: false
36
24
  version_requirements: !ruby/object:Gem::Requirement
37
25
  requirements:
38
26
  - - ">="
39
27
  - !ruby/object:Gem::Version
40
- version: '0'
28
+ version: '7.1'
29
+ - - "<"
30
+ - !ruby/object:Gem::Version
31
+ version: '9'
41
32
  description: Rails plugin for mapping static data of web application to Dictionary
42
33
  class
43
34
  email:
@@ -46,28 +37,39 @@ executables: []
46
37
  extensions: []
47
38
  extra_rdoc_files: []
48
39
  files:
40
+ - ".github/workflows/ci.yml"
49
41
  - ".gitignore"
50
42
  - CHANGELOG
51
43
  - Gemfile
52
- - README.v2.0.rdoc
44
+ - Gemfile.lock
45
+ - README.rdoc
53
46
  - Rakefile
54
- - Readme.md
47
+ - docs/load-order.md
55
48
  - lib/rails_dictionary.rb
56
- - lib/rails_dictionary/models/active_record_extension.rb
57
- - lib/rails_dictionary/models/acts_as_dict_consumer.rb
58
- - lib/rails_dictionary/models/acts_as_dictionary.rb
49
+ - lib/rails_dictionary/active_record_extension.rb
50
+ - lib/rails_dictionary/acts_as_dict_slave.rb
51
+ - lib/rails_dictionary/acts_as_dict_type.rb
52
+ - lib/rails_dictionary/acts_as_dictionary.rb
53
+ - lib/rails_dictionary/array_core_ext.rb
54
+ - lib/rails_dictionary/railtie.rb
59
55
  - lib/rails_dictionary/version.rb
60
56
  - lib/tasks/dicts.rake
57
+ - pkg/rails_dictionary-0.2.2.gem
58
+ - pkg/rails_dictionary-0.2.3.gem
61
59
  - rails_dictionary.gemspec
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
60
+ - spec/fake_app.rb
61
+ - spec/init_models.rb
62
+ - spec/load_order/empty_boot_recovers.rb
63
+ - spec/load_order/lazy_slave_model_boot.rb
64
+ - spec/load_order/preseeded_boot.rb
65
+ - spec/load_order/support.rb
66
+ - spec/rails_dictionary_spec.rb
67
+ - spec/spec_helper.rb
68
+ - v1.0-roadmap.md
67
69
  homepage: https://github.com/raykin/rails_dictionary
68
- licenses: []
70
+ licenses:
71
+ - MIT
69
72
  metadata: {}
70
- post_install_message:
71
73
  rdoc_options: []
72
74
  require_paths:
73
75
  - lib
@@ -75,17 +77,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
75
77
  requirements:
76
78
  - - ">="
77
79
  - !ruby/object:Gem::Version
78
- version: '0'
80
+ version: 3.3.0
79
81
  required_rubygems_version: !ruby/object:Gem::Requirement
80
82
  requirements:
81
- - - ">"
83
+ - - ">="
82
84
  - !ruby/object:Gem::Version
83
- version: 1.3.1
85
+ version: '0'
84
86
  requirements: []
85
- rubyforge_project: rails_dictionary
86
- rubygems_version: 2.2.2
87
- signing_key:
87
+ rubygems_version: 4.0.10
88
88
  specification_version: 4
89
89
  summary: dictionary data for web application
90
90
  test_files: []
91
- has_rdoc:
data/Readme.md DELETED
@@ -1,10 +0,0 @@
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.
@@ -1,38 +0,0 @@
1
- module RailsDictionary
2
- module ActiveRecordExtension
3
- def self.included(base)
4
- base.extend(ClassMethods)
5
- end
6
-
7
- module ClassMethods
8
-
9
- def acts_as_dictionary
10
- include ActsAsDictionary
11
- validates_uniqueness_of :name, scope: [inheritance_column]
12
- end
13
-
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'
31
- end
32
- end
33
-
34
- end
35
- end
36
- end
37
-
38
- ::ActiveRecord::Base.send :include, RailsDictionary::ActiveRecordExtension
@@ -1,77 +0,0 @@
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
@@ -1,27 +0,0 @@
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
- # 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
15
- end
16
-
17
- end # End ClassMethods
18
-
19
- module InstanceMethods
20
- def type=(name)
21
- ::RailsDictionary.init_dict_sti_class(name)
22
- super
23
- end
24
- end # End InstanceMethods
25
-
26
- end
27
- end
@@ -1,44 +0,0 @@
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