utusemi 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +61 -0
- data/Rakefile +25 -0
- data/lib/tasks/utusemi_tasks.rake +4 -0
- data/lib/utusemi.rb +62 -0
- data/lib/utusemi/configuration.rb +26 -0
- data/lib/utusemi/core.rb +343 -0
- data/lib/utusemi/definition.rb +39 -0
- data/lib/utusemi/railtie.rb +7 -0
- data/lib/utusemi/version.rb +3 -0
- metadata +152 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c3d9dc71891093baf91f418243e79cda7c2fde5e
|
4
|
+
data.tar.gz: 3c298928855391ac65f36f93b93c44226ba2b54a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f327281cc8a51c3f0c779bde5f78a0ad4f6f039102024df4dfc07d630a33161e7bba35a2cef9a0c444b78e46af2348a8d545d0df038ba08f4d6d2b641ff86e51
|
7
|
+
data.tar.gz: 721fb167c66338488ba3e136f981e8dddf4d699628a8fb0ad7d38117e41a2fce1562ae01209ffe848f843b44ee42bef328ffbde8ecbce6429a42dc605b277e0d
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2014 YOURNAME
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
# Utusemi
|
2
|
+
|
3
|
+
[![Build Status](https://secure.travis-ci.org/hyoshida/utusemi.png)](http://travis-ci.org/hyoshida/utusemi)
|
4
|
+
[![Code Climate](https://codeclimate.com/github/hyoshida/utusemi.png)](https://codeclimate.com/github/hyoshida/utusemi)
|
5
|
+
[![Coverage Status](https://coveralls.io/repos/hyoshida/utusemi/badge.png)](https://coveralls.io/r/hyoshida/utusemi)
|
6
|
+
[![Dependency Status](https://gemnasium.com/hyoshida/utusemi.svg)](https://gemnasium.com/hyoshida/utusemi)
|
7
|
+
|
8
|
+
Providing a flexible alias for column names in ActiveRecord.
|
9
|
+
|
10
|
+
|
11
|
+
## Installation
|
12
|
+
|
13
|
+
1. Add utusemi in the `Gemfile`:
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
gem 'utusemi'
|
17
|
+
```
|
18
|
+
|
19
|
+
2. Download and install by running:
|
20
|
+
|
21
|
+
```bash
|
22
|
+
bundle install
|
23
|
+
```
|
24
|
+
|
25
|
+
|
26
|
+
## Usage
|
27
|
+
|
28
|
+
1. Create a file named `utusemi.rb` in `config/initializers` and add column names in this file.
|
29
|
+
|
30
|
+
```ruby
|
31
|
+
Utusemi.configure do
|
32
|
+
map :sample do
|
33
|
+
name :first_name
|
34
|
+
end
|
35
|
+
end
|
36
|
+
```
|
37
|
+
|
38
|
+
2. Use `utusemi` method.
|
39
|
+
|
40
|
+
```ruby
|
41
|
+
irb> User.utusemi(:sample).where(name: 'John')
|
42
|
+
SELECT "users".* FROM "users" WHERE "users"."first_name" = 'John'
|
43
|
+
```
|
44
|
+
|
45
|
+
|
46
|
+
## Requirements
|
47
|
+
|
48
|
+
* Ruby on Rails 3.2, 4.1
|
49
|
+
* Ruby 2.1
|
50
|
+
|
51
|
+
|
52
|
+
## Development
|
53
|
+
|
54
|
+
To set up a development environment, simply do:
|
55
|
+
|
56
|
+
```bash
|
57
|
+
bundle install
|
58
|
+
bundle exec rake db:create
|
59
|
+
bundle exec rake db:migrate
|
60
|
+
bundle exec rake # run the test suite
|
61
|
+
```
|
data/Rakefile
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'rdoc/task'
|
8
|
+
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
11
|
+
rdoc.title = 'Utusemi'
|
12
|
+
rdoc.options << '--line-numbers'
|
13
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
14
|
+
end
|
15
|
+
|
16
|
+
APP_RAKEFILE = File.expand_path('../spec/dummy/Rakefile', __FILE__)
|
17
|
+
load 'rails/tasks/engine.rake'
|
18
|
+
|
19
|
+
Bundler::GemHelper.install_tasks
|
20
|
+
|
21
|
+
task :rubocop do
|
22
|
+
exec 'rubocop'
|
23
|
+
end
|
24
|
+
|
25
|
+
task default: ['app:spec', 'rubocop']
|
data/lib/utusemi.rb
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'rails'
|
2
|
+
|
3
|
+
require 'utusemi/definition'
|
4
|
+
require 'utusemi/configuration'
|
5
|
+
require 'utusemi/railtie'
|
6
|
+
|
7
|
+
module Utusemi
|
8
|
+
class << self
|
9
|
+
def enable
|
10
|
+
this = self
|
11
|
+
ActiveSupport.on_load(:active_record) do
|
12
|
+
require 'utusemi/core'
|
13
|
+
# for instance
|
14
|
+
this.include_to_activerecord_base
|
15
|
+
# for model and relation
|
16
|
+
this.prepend_to_activerecord_base
|
17
|
+
this.prepend_to_activerecord_relation
|
18
|
+
this.prepend_to_activerecord_eigenclass
|
19
|
+
this.prepend_to_activerecord_associations_hasmanyassociation
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def config
|
24
|
+
@configuration ||= Configuration.new
|
25
|
+
end
|
26
|
+
|
27
|
+
def configure(&block)
|
28
|
+
config.instance_eval(&block)
|
29
|
+
end
|
30
|
+
|
31
|
+
def include_to_activerecord_base
|
32
|
+
ActiveRecord::Base.send(:include, Core::InstanceMethods)
|
33
|
+
end
|
34
|
+
|
35
|
+
def prepend_to_activerecord_base
|
36
|
+
ActiveRecord::Base.send(:prepend, Core::ActiveRecord::Base)
|
37
|
+
end
|
38
|
+
|
39
|
+
def prepend_to_activerecord_relation
|
40
|
+
ActiveRecord::Relation.send(:prepend, Core::ActiveRecord::QueryMethods)
|
41
|
+
ActiveRecord::Relation.send(:prepend, Core::ActiveRecord::Relation)
|
42
|
+
end
|
43
|
+
|
44
|
+
def prepend_to_activerecord_eigenclass
|
45
|
+
activerecord_eigenclass.send(:prepend, Core::ActiveRecord::Querying)
|
46
|
+
# for rails 3.x
|
47
|
+
activerecord_eigenclass.send(:prepend, Core::ActiveRecord::RelationMethod) if Rails::VERSION::MAJOR == 3
|
48
|
+
# for association
|
49
|
+
activerecord_eigenclass.send(:prepend, Core::ActiveRecord::AssociationMethods)
|
50
|
+
end
|
51
|
+
|
52
|
+
def prepend_to_activerecord_associations_hasmanyassociation
|
53
|
+
ActiveRecord::Associations::HasManyAssociation.send(:prepend, Core::ActiveRecord::Associations)
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def activerecord_eigenclass
|
59
|
+
ActiveRecord::Base.class_eval { class << self; self; end }
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Utusemi
|
2
|
+
class Configuration
|
3
|
+
def initialize
|
4
|
+
@maps ||= {}
|
5
|
+
end
|
6
|
+
|
7
|
+
def map(name, *args, &block)
|
8
|
+
return map_get(name, *args) unless block_given?
|
9
|
+
options = args.shift if args.first.is_a? Hash
|
10
|
+
map_set(name, options, &block)
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def map_set(name, options, &block)
|
16
|
+
@maps[name.to_sym] = (options || {}).merge(block: block)
|
17
|
+
end
|
18
|
+
|
19
|
+
def map_get(name, *args)
|
20
|
+
map = @maps[name.to_sym].try(:dup) if name
|
21
|
+
block = map.delete(:block) if map
|
22
|
+
definition = Definition.new(map)
|
23
|
+
definition.exec(*args, &block)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/utusemi/core.rb
ADDED
@@ -0,0 +1,343 @@
|
|
1
|
+
module Utusemi
|
2
|
+
module Core
|
3
|
+
# 用途
|
4
|
+
# モデル向けカラムマッパとインスタンス向けカラムマッパの共通処理
|
5
|
+
#
|
6
|
+
# 役割
|
7
|
+
# モデル向けカラムマッパ => Utusemi::Core::ActiveRecord
|
8
|
+
# インスタンス向けカラムマッパ => Utusemi::Core::InstanceMethods
|
9
|
+
#
|
10
|
+
# 備考
|
11
|
+
# utusemiメソッドの第2引数は、任意のオプションをHashで指定する。
|
12
|
+
# ただしoptions[:times]は予約済みで、指定した回数分だけmapメソッドを
|
13
|
+
# 繰り返し、options[:index]にイテレート中のカウントを返す。
|
14
|
+
# また、その結果から複数のwhere条件を構築する。
|
15
|
+
#
|
16
|
+
module Base
|
17
|
+
def utusemi_values
|
18
|
+
@utusemi_values ||= {}
|
19
|
+
end
|
20
|
+
|
21
|
+
def utusemi(obj = nil, options = {})
|
22
|
+
clone.utusemi!(obj, options)
|
23
|
+
end
|
24
|
+
|
25
|
+
def utusemi!(obj = nil, options = {})
|
26
|
+
obj = true if obj.nil?
|
27
|
+
utusemi_values[:flag] = obj ? true : false
|
28
|
+
utusemi_values[:type] = obj.to_sym if obj.class.in? [Symbol, String]
|
29
|
+
utusemi_values[:options] = options
|
30
|
+
self
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def utusemi_column_names(index = nil)
|
36
|
+
return {} unless utusemi_values[:flag]
|
37
|
+
options = utusemi_values[:options] || {}
|
38
|
+
options.update(index: index)
|
39
|
+
Utusemi.config.map(utusemi_values[:type], options).attributes
|
40
|
+
end
|
41
|
+
|
42
|
+
def mapped_utusemi_column_name(column_name, index = nil)
|
43
|
+
utusemi_column_names(index)[column_name.to_sym] || column_name
|
44
|
+
end
|
45
|
+
|
46
|
+
def unmapped_utusemi_column_name(column_name, index = nil)
|
47
|
+
utusemi_column_names(index).invert[column_name.to_sym] || column_name
|
48
|
+
end
|
49
|
+
|
50
|
+
def eigenclass
|
51
|
+
class << self; self; end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# 用途
|
56
|
+
# Utusemi.config.mapに設定したマッピングを意識せずに実装できるよう、
|
57
|
+
# デフォルト名による各カラムへのアクセスを可能にする
|
58
|
+
#
|
59
|
+
# 使用例
|
60
|
+
# Utusemi.config do
|
61
|
+
# map :product do
|
62
|
+
# name :title
|
63
|
+
# end
|
64
|
+
# end
|
65
|
+
#
|
66
|
+
# product = Product.first
|
67
|
+
# product.name
|
68
|
+
# #=> NoMethodError: undefined method `name' for #<Product:...>
|
69
|
+
#
|
70
|
+
# product.utusemi(:product).name
|
71
|
+
# #=> 'test product'
|
72
|
+
#
|
73
|
+
module InstanceMethods
|
74
|
+
include Base
|
75
|
+
|
76
|
+
def utusemi!(obj = nil, options = {})
|
77
|
+
super.tap { utusemi_columns_mapper if obj.class.in? [Symbol, String] }
|
78
|
+
end
|
79
|
+
|
80
|
+
def utusemi_columns_mapper
|
81
|
+
utusemi_column_names.keys.each do |column_name|
|
82
|
+
# alias_attributeと同じことを、対象カラム名を動的に変更して行う
|
83
|
+
define_getter_method(column_name)
|
84
|
+
define_setter_method(column_name)
|
85
|
+
define_predicate_method(column_name)
|
86
|
+
define_was_method(column_name)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def changed
|
91
|
+
return super unless utusemi_values[:flag]
|
92
|
+
super + super.map { |column_name| unmapped_utusemi_column_name(column_name).to_s }
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
def define_getter_method(column_name)
|
98
|
+
target_column_name = mapped_utusemi_column_name(column_name)
|
99
|
+
define_singleton_method(column_name) { send target_column_name }
|
100
|
+
end
|
101
|
+
|
102
|
+
def define_setter_method(column_name)
|
103
|
+
target_column_name = mapped_utusemi_column_name(column_name)
|
104
|
+
define_singleton_method("#{column_name}=") { |value| send "#{target_column_name}=", value }
|
105
|
+
end
|
106
|
+
|
107
|
+
def define_predicate_method(column_name)
|
108
|
+
target_column_name = mapped_utusemi_column_name(column_name)
|
109
|
+
define_singleton_method("#{column_name}?") { send "#{target_column_name}?" }
|
110
|
+
end
|
111
|
+
|
112
|
+
def define_was_method(column_name)
|
113
|
+
target_column_name = mapped_utusemi_column_name(column_name)
|
114
|
+
define_singleton_method("#{column_name}_was") { send "#{target_column_name}_was" }
|
115
|
+
end
|
116
|
+
|
117
|
+
def utusemi_for_association(name, association, options = {})
|
118
|
+
utusemi_values = self.utusemi_values
|
119
|
+
utusemi_values = self.class.utusemi_values unless utusemi_values[:flag]
|
120
|
+
utusemi_flag = utusemi_values[:flag] || options[:force]
|
121
|
+
return association unless utusemi_flag
|
122
|
+
association.utusemi!(name.to_s.singularize, utusemi_values[:options])
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
# 用途
|
127
|
+
# whereなどのArelチェインにおいて、Utusemi.config.mapに設定したマッピングを
|
128
|
+
# 意識せずに実装できるよう、デフォルト名による各カラムへのアクセスを可能にする
|
129
|
+
#
|
130
|
+
# 使用例
|
131
|
+
# Product.utusemi(:product).where(name: "test")
|
132
|
+
# #=> [<products.titleが"test"であるレコード>]
|
133
|
+
#
|
134
|
+
module ActiveRecord
|
135
|
+
module Querying
|
136
|
+
include Base
|
137
|
+
|
138
|
+
case Rails::VERSION::MAJOR
|
139
|
+
when 4
|
140
|
+
delegate :utusemi, to: :all
|
141
|
+
when 3
|
142
|
+
delegate :utusemi, to: :scoped
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
module QueryMethods
|
147
|
+
include Base
|
148
|
+
|
149
|
+
def utusemi!(obj = nil, options = {})
|
150
|
+
super.tap { warning_checker unless Rails.env.production? }
|
151
|
+
end
|
152
|
+
|
153
|
+
def build_where(opts = :chain, *rest)
|
154
|
+
return super unless utusemi_values[:flag]
|
155
|
+
if utusemi_values[:options][:times]
|
156
|
+
opts_with_times(opts, utusemi_values[:options][:times]) { |opts_with_mapped| super(opts_with_mapped, *rest) }
|
157
|
+
else
|
158
|
+
super(opts_with_mapped_column_name(opts), *rest)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def order(opts = nil, *rest)
|
163
|
+
opts = opts_with_mapped_column_name(opts) if utusemi_values[:flag]
|
164
|
+
super
|
165
|
+
end
|
166
|
+
|
167
|
+
private
|
168
|
+
|
169
|
+
def opts_with_times(opts, times)
|
170
|
+
1.upto(times).map do |index|
|
171
|
+
yield opts_with_mapped_column_name(opts, index)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def opts_with_mapped_column_name(opts, index = nil)
|
176
|
+
case opts
|
177
|
+
when Hash
|
178
|
+
key_values = opts.map { |key, value| [mapped_utusemi_column_name(key.to_s, index), value] }.flatten(1)
|
179
|
+
Hash[*key_values]
|
180
|
+
when String, Symbol
|
181
|
+
mapped_column_names_for_string(opts.to_s, index)
|
182
|
+
else
|
183
|
+
opts
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
def mapped_column_names_for_string(string, index = nil)
|
188
|
+
utusemi_column_names(index).each do |old_column_name, new_column_name|
|
189
|
+
string.gsub!(/\b#{old_column_name}\b/, new_column_name.to_s)
|
190
|
+
end
|
191
|
+
string
|
192
|
+
end
|
193
|
+
|
194
|
+
def warning_checker
|
195
|
+
utusemi_column_names.each do |old_column_name, new_column_name|
|
196
|
+
return if old_column_name != new_column_name
|
197
|
+
Rails.logger.warn "[Utusemi:WARNING] #{old_column_name} is duplicated in Utusemi::Engine.config.#{utusemi_values[:type]}_columns."
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
# Rails 3.x で scope に対してのカラムマッピングが正常に動作するようにするためのもの
|
203
|
+
#
|
204
|
+
# 原因
|
205
|
+
# scope 内の条件が unscoped { ... } 内で実行されるため、カラムマッピングを実施する為のフラグが
|
206
|
+
# 引き継がれず、カラムマッピングが作動しない
|
207
|
+
#
|
208
|
+
# 対策
|
209
|
+
# scope メソッドでは unscoped { ... } の結果を Relation.new として再生成しているので
|
210
|
+
# relation メソッドを利用した際にカラムマッピング実施フラグがあればこれを継承するようにした
|
211
|
+
#
|
212
|
+
module RelationMethod
|
213
|
+
def relation(*args, &block)
|
214
|
+
return super unless current_scope
|
215
|
+
return super unless current_scope.utusemi_values
|
216
|
+
return super unless current_scope.utusemi_values[:flag]
|
217
|
+
super.utusemi(current_scope.utusemi_values[:type], utusemi_values[:options])
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
module Relation
|
222
|
+
# 用途
|
223
|
+
# utusemiメソッドを利用してレコードを検索した場合は
|
224
|
+
# Utusemi::Core#utusemiを個別呼び出さなくても済むようになる
|
225
|
+
#
|
226
|
+
# 使用例
|
227
|
+
# product = Product.utusemi(:product).where(name: 'test').first
|
228
|
+
# product.utusemi(:product).name
|
229
|
+
# #=> 'test' (= products.title)
|
230
|
+
#
|
231
|
+
# こうなっていたコードが以下のようになる
|
232
|
+
#
|
233
|
+
# product = Product.utusemi(:product).where(name: 'test').first
|
234
|
+
# product.name
|
235
|
+
# #=> true (= products.title)
|
236
|
+
#
|
237
|
+
def to_a
|
238
|
+
utusemi_values = self.utusemi_values
|
239
|
+
utusemi_values = @klass.utusemi_values unless utusemi_values[:flag]
|
240
|
+
return super unless utusemi_values[:flag]
|
241
|
+
super.each { |record| record.utusemi!(utusemi_values[:type], utusemi_values[:options]) }
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
module Base
|
246
|
+
# 用途
|
247
|
+
# utusemiメソッドを利用後にレコードを作成した場合は
|
248
|
+
# Utusemi::Core#utusemiを個別呼び出さなくても済むようになる
|
249
|
+
#
|
250
|
+
# 使用例
|
251
|
+
# product = Product.utusemi(:product).new(name: 'test')
|
252
|
+
# product.name
|
253
|
+
# #=> 'test' (= products.title)
|
254
|
+
#
|
255
|
+
def initialize(*args, &block)
|
256
|
+
case Rails::VERSION::MAJOR
|
257
|
+
when 4
|
258
|
+
current_scope = self.class.current_scope
|
259
|
+
when 3
|
260
|
+
current_scope = self.class.scoped
|
261
|
+
end
|
262
|
+
utusemi_values = current_scope.try(:utusemi_values) || {}
|
263
|
+
utusemi_values = self.class.utusemi_values unless utusemi_values[:flag]
|
264
|
+
utusemi!(utusemi_values[:type], utusemi_values[:options]) if utusemi_values[:flag]
|
265
|
+
super
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
# 用途
|
270
|
+
# 関連モデルにカラムマッパを継承する
|
271
|
+
#
|
272
|
+
# 使用例
|
273
|
+
# class Product
|
274
|
+
# has_many :stocks, utusemi: true
|
275
|
+
# ...
|
276
|
+
# end
|
277
|
+
# stock = Product.utusemi(:product).stocks.first
|
278
|
+
# stock.quantity
|
279
|
+
# #=> 10 (= stocks.units)
|
280
|
+
#
|
281
|
+
module Associations
|
282
|
+
def scope(*args)
|
283
|
+
utusemi_values = owner.utusemi_values
|
284
|
+
return super unless utusemi_values[:flag]
|
285
|
+
super.utusemi!(@reflection.name.to_s.singularize, utusemi_values[:options])
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
module AssociationMethods
|
290
|
+
def belongs_to(name, scope = nil, options = {})
|
291
|
+
check_deplicated_association_warning(:belongs_to, name, scope)
|
292
|
+
utusemi_flag = scope.try(:delete, :utusemi)
|
293
|
+
scope = utusemi_association_scope(:belongs_to, name, scope) if utusemi_flag
|
294
|
+
super if !utusemi_flag || !method_defined?(name)
|
295
|
+
define_utusemi_association_reader(name, utusemi_flag => true)
|
296
|
+
end
|
297
|
+
|
298
|
+
def has_one(name, scope = nil, options = {})
|
299
|
+
check_deplicated_association_warning(:has_one, name, scope)
|
300
|
+
utusemi_flag = scope.try(:delete, :utusemi)
|
301
|
+
scope = utusemi_association_scope(:has_one, name, scope) if utusemi_flag
|
302
|
+
super if !utusemi_flag || !method_defined?(name)
|
303
|
+
define_utusemi_association_reader(name, utusemi_flag => true)
|
304
|
+
end
|
305
|
+
|
306
|
+
def has_many(name, scope = nil, options = {}, &extension)
|
307
|
+
check_deplicated_association_warning(:has_many, name, scope)
|
308
|
+
utusemi_flag = scope.try(:delete, :utusemi)
|
309
|
+
scope = utusemi_association_scope(:has_many, name, scope) if utusemi_flag
|
310
|
+
super if !utusemi_flag || !method_defined?(name)
|
311
|
+
define_utusemi_association_reader(name, utusemi_flag => true)
|
312
|
+
end
|
313
|
+
|
314
|
+
private
|
315
|
+
|
316
|
+
def check_deplicated_association_warning(association_type, name, scope)
|
317
|
+
return unless method_defined?(name)
|
318
|
+
return unless method_defined?("#{name}_with_utusemi")
|
319
|
+
return if scope.try(:[], :utusemi)
|
320
|
+
Rails.logger.warn "[Utusemi:WARNING] \"#{association_type} :#{name}\" is duplicated in #{self.name}."
|
321
|
+
end
|
322
|
+
|
323
|
+
def utusemi_association_scope(method_name, name, scope = {})
|
324
|
+
utusemi_map = Utusemi.config.map(name.to_s.singularize)
|
325
|
+
default_scope = { class_name: utusemi_map.class_name }
|
326
|
+
default_scope[:foreign_key] = utusemi_map.foreign_key if method_name == :belongs_to
|
327
|
+
default_scope.merge(scope)
|
328
|
+
end
|
329
|
+
|
330
|
+
def define_utusemi_association_reader(name, options = {})
|
331
|
+
return if method_defined?("#{name}_with_utusemi")
|
332
|
+
define_method "#{name}_with_utusemi" do |*args|
|
333
|
+
association = send("#{name}_without_utusemi", *args)
|
334
|
+
return unless association
|
335
|
+
return association unless association.is_a? ActiveRecord::Base
|
336
|
+
utusemi_for_association(name, association, options)
|
337
|
+
end
|
338
|
+
alias_method_chain name, :utusemi
|
339
|
+
end
|
340
|
+
end
|
341
|
+
end
|
342
|
+
end
|
343
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Utusemi
|
2
|
+
class Definition
|
3
|
+
UNPROXIED_METHODS = %w(__send__ __id__ nil? send object_id extend instance_eval initialize block_given? raise caller method instance_exec)
|
4
|
+
OPTION_METHODS = %w(class_name foreign_key)
|
5
|
+
|
6
|
+
(instance_methods + private_instance_methods).each do |method|
|
7
|
+
undef_method(method) unless UNPROXIED_METHODS.include?(method.to_s)
|
8
|
+
end
|
9
|
+
|
10
|
+
OPTION_METHODS.each do |method|
|
11
|
+
define_method(method) { @options[method.to_sym] }
|
12
|
+
end
|
13
|
+
|
14
|
+
attr_reader :attributes
|
15
|
+
|
16
|
+
def initialize(options = {})
|
17
|
+
@attributes = {}
|
18
|
+
@options = options
|
19
|
+
end
|
20
|
+
|
21
|
+
def exec(*args, &block)
|
22
|
+
instance_exec(*args, &block) if block_given?
|
23
|
+
self
|
24
|
+
end
|
25
|
+
|
26
|
+
def method_missing(name, *args)
|
27
|
+
add_attribute(name, *args)
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def add_attribute(name, value = nil)
|
33
|
+
fail AttributeDefinitionError, 'Block given' if block_given?
|
34
|
+
@attributes[name.to_sym] = value.to_sym
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class AttributeDefinitionError; end
|
39
|
+
end
|
metadata
ADDED
@@ -0,0 +1,152 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: utusemi
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- YOSHIDA Hiroki
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-07-10 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activerecord
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 3.2.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 3.2.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: railties
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 3.2.0
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 3.2.0
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: pg
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec-rails
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: factory_girl_rails
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - '>='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rubocop
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - '>='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - '>='
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: coveralls
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - '>='
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - '>='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
description: Providing a flexible alias for column names in ActiveRecord.
|
112
|
+
email:
|
113
|
+
- hyoshida@appirits.com
|
114
|
+
executables: []
|
115
|
+
extensions: []
|
116
|
+
extra_rdoc_files: []
|
117
|
+
files:
|
118
|
+
- MIT-LICENSE
|
119
|
+
- README.md
|
120
|
+
- Rakefile
|
121
|
+
- lib/tasks/utusemi_tasks.rake
|
122
|
+
- lib/utusemi.rb
|
123
|
+
- lib/utusemi/configuration.rb
|
124
|
+
- lib/utusemi/core.rb
|
125
|
+
- lib/utusemi/definition.rb
|
126
|
+
- lib/utusemi/railtie.rb
|
127
|
+
- lib/utusemi/version.rb
|
128
|
+
homepage: https://github.com/hyoshida/utusemi#utusemi
|
129
|
+
licenses:
|
130
|
+
- MIT
|
131
|
+
metadata: {}
|
132
|
+
post_install_message:
|
133
|
+
rdoc_options: []
|
134
|
+
require_paths:
|
135
|
+
- lib
|
136
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
137
|
+
requirements:
|
138
|
+
- - '>='
|
139
|
+
- !ruby/object:Gem::Version
|
140
|
+
version: '0'
|
141
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - '>='
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
requirements: []
|
147
|
+
rubyforge_project:
|
148
|
+
rubygems_version: 2.2.2
|
149
|
+
signing_key:
|
150
|
+
specification_version: 4
|
151
|
+
summary: Providing a flexible alias for column names in ActiveRecord.
|
152
|
+
test_files: []
|