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.
- checksums.yaml +5 -5
- data/.github/workflows/ci.yml +33 -0
- data/.gitignore +5 -3
- data/CHANGELOG +19 -3
- data/Gemfile +10 -3
- data/Gemfile.lock +350 -0
- data/{README.v2.0.rdoc → README.rdoc} +33 -15
- data/Rakefile +10 -4
- data/docs/load-order.md +118 -0
- data/lib/rails_dictionary/active_record_extension.rb +48 -0
- data/lib/rails_dictionary/acts_as_dict_slave.rb +85 -0
- data/lib/rails_dictionary/acts_as_dict_type.rb +53 -0
- data/lib/rails_dictionary/acts_as_dictionary.rb +118 -0
- data/lib/rails_dictionary/array_core_ext.rb +10 -0
- data/lib/rails_dictionary/railtie.rb +11 -0
- data/lib/rails_dictionary/version.rb +1 -1
- data/lib/rails_dictionary.rb +64 -20
- data/lib/tasks/dicts.rake +9 -11
- data/pkg/rails_dictionary-0.2.2.gem +0 -0
- data/pkg/rails_dictionary-0.2.3.gem +0 -0
- data/rails_dictionary.gemspec +4 -4
- data/spec/fake_app.rb +29 -0
- data/spec/init_models.rb +12 -0
- data/spec/load_order/empty_boot_recovers.rb +19 -0
- data/spec/load_order/lazy_slave_model_boot.rb +25 -0
- data/spec/load_order/preseeded_boot.rb +22 -0
- data/spec/load_order/support.rb +41 -0
- data/spec/rails_dictionary_spec.rb +172 -0
- data/spec/spec_helper.rb +22 -0
- data/v1.0-roadmap.md +31 -0
- metadata +37 -38
- data/Readme.md +0 -10
- data/lib/rails_dictionary/models/active_record_extension.rb +0 -38
- data/lib/rails_dictionary/models/acts_as_dict_consumer.rb +0 -77
- data/lib/rails_dictionary/models/acts_as_dictionary.rb +0 -27
- data/test/acts_as_consumer_test.rb +0 -44
- data/test/fake_app.rb +0 -40
- data/test/lookup_test.rb +0 -39
- data/test/rails_dictionary_test.rb +0 -81
- 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
|
data/spec/spec_helper.rb
ADDED
|
@@ -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.
|
|
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:
|
|
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: '
|
|
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: '
|
|
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
|
-
-
|
|
44
|
+
- Gemfile.lock
|
|
45
|
+
- README.rdoc
|
|
53
46
|
- Rakefile
|
|
54
|
-
-
|
|
47
|
+
- docs/load-order.md
|
|
55
48
|
- lib/rails_dictionary.rb
|
|
56
|
-
- lib/rails_dictionary/
|
|
57
|
-
- lib/rails_dictionary/
|
|
58
|
-
- lib/rails_dictionary/
|
|
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
|
-
-
|
|
63
|
-
-
|
|
64
|
-
-
|
|
65
|
-
-
|
|
66
|
-
-
|
|
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:
|
|
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:
|
|
85
|
+
version: '0'
|
|
84
86
|
requirements: []
|
|
85
|
-
|
|
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
|