memory_record 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/.gitignore +18 -0
- data/.rspec +2 -0
- data/.travis.yml +3 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +22 -0
- data/README.org +123 -0
- data/Rakefile +4 -0
- data/examples/0110_basic.rb +16 -0
- data/examples/0120_attr_reader_auto_option.rb +14 -0
- data/examples/0130_for_legacy_code.rb +14 -0
- data/examples/0140_key_join_underscore_if_array.rb +11 -0
- data/examples/0150_behavior_like_hash.rb +11 -0
- data/examples/0160_practice.rb +27 -0
- data/examples/0170_reset.rb +21 -0
- data/examples/0180_key_is_anything_ok.rb +11 -0
- data/examples/0190_tips_on_handling_boolean_type.rb +14 -0
- data/examples/0200_freeze_makes_it_impossible_to_memorize.rb +24 -0
- data/examples/0210_super_usable.rb +20 -0
- data/examples/0230_validation.rb +23 -0
- data/examples/0240_key_duplicate.rb +12 -0
- data/examples/0250_when_key_like_numeric.rb +17 -0
- data/examples/0260_with_use_activerecord_enum.rb +33 -0
- data/lib/memory_record/memory_record.rb +153 -0
- data/lib/memory_record/version.rb +3 -0
- data/lib/memory_record.rb +2 -0
- data/memory_record.gemspec +26 -0
- data/spec/memory_record_spec.rb +164 -0
- data/spec/spec_helper.rb +9 -0
- metadata +144 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 0a0d02478e038db4812bab5276b78fea07776ab4
|
4
|
+
data.tar.gz: 98d8b5e8afc8d4953dc71e240112ea96deb13bcc
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 57018d07d34f7087d497ed1e31df045590b3ba664607a2990c65ac5aa1cf0e6f3af45c72790a0dc576da73dc1412b66f6ed9de2773e0c0de41485b3371f6a4a5
|
7
|
+
data.tar.gz: 37dc4b8c4ff4a17d5a70e1681993b42d65186a7b3cfdd4b04b1de74dd3e8fd31a26013a275a4226422130ae3a857b22a3c9246cd57f55de6ea9b4ac1c22092d3
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 akicho8
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.org
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
* A small number of records Easy handling library
|
2
|
+
|
3
|
+
#+BEGIN_SRC ruby
|
4
|
+
class Direction
|
5
|
+
include MemoryRecord
|
6
|
+
memory_record [
|
7
|
+
{key: :left, name: "←", vector: [-1, 0]},
|
8
|
+
{key: :right, name: "→", vector: [ 1, 0]},
|
9
|
+
], attr_reader_auto: true
|
10
|
+
|
11
|
+
def long_name
|
12
|
+
"#{key}:#{name}"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
Direction.collect(&:name) # => ["←", "→"]
|
17
|
+
Direction.keys # => [:left, :right]
|
18
|
+
|
19
|
+
Direction[:right].key # => :right
|
20
|
+
Direction[:right].code # => 1
|
21
|
+
Direction[:right].vector # => [1, 0]
|
22
|
+
Direction[:right].long_name # => "right:→"
|
23
|
+
|
24
|
+
Direction[1].key # => :right
|
25
|
+
|
26
|
+
Direction[:up] # => nil
|
27
|
+
Direction.fetch(:up) rescue $! # => #<KeyError: Direction.fetch(:up) does not match anything。
|
28
|
+
#+END_SRC
|
29
|
+
|
30
|
+
*** How to decide =code= yourself?
|
31
|
+
|
32
|
+
#+BEGIN_SRC ruby
|
33
|
+
class Foo
|
34
|
+
include MemoryRecord
|
35
|
+
memory_record [
|
36
|
+
{code: 1, key: :a, name: "A"},
|
37
|
+
{code: 2, key: :b, name: "B"},
|
38
|
+
{code: 3, key: :c, name: "C"},
|
39
|
+
], attr_reader: :name
|
40
|
+
end
|
41
|
+
|
42
|
+
Foo.collect(&:code) # => [1, 2, 3]
|
43
|
+
#+END_SRC
|
44
|
+
|
45
|
+
It is used only when refactoring legacy code, when compatibility is required.
|
46
|
+
|
47
|
+
*** How to turn as an array?
|
48
|
+
|
49
|
+
=Enumerable= extended, so that =each= method is available
|
50
|
+
|
51
|
+
#+BEGIN_SRC ruby
|
52
|
+
Foo.each {|v| ... }
|
53
|
+
Foo.collect {|v| ... }
|
54
|
+
#+END_SRC
|
55
|
+
|
56
|
+
*** How do I submit a form to select in Rails?
|
57
|
+
|
58
|
+
#+BEGIN_SRC ruby
|
59
|
+
form.collection_select(:selection_code, Foo, :code, :name)
|
60
|
+
#+END_SRC
|
61
|
+
|
62
|
+
*** Is the reference in subscripts slow?
|
63
|
+
|
64
|
+
Since it has a hash internally using the key value as a key, it can be acquired with O (1).
|
65
|
+
|
66
|
+
#+BEGIN_SRC ruby
|
67
|
+
Foo[1].name # => "A"
|
68
|
+
Foo[:a].name # => "A"
|
69
|
+
#+END_SRC
|
70
|
+
|
71
|
+
*** Instances always react to =code= and =key=
|
72
|
+
|
73
|
+
#+BEGIN_SRC ruby
|
74
|
+
object = Foo.first
|
75
|
+
object.key # => :a
|
76
|
+
object.code # => 1
|
77
|
+
#+END_SRC
|
78
|
+
|
79
|
+
*** =attr_reader= option
|
80
|
+
|
81
|
+
The attribute value can be referenced with =@attributes[:xxx]=, but it is troublesome to refer frequently, so it is methodized with =attr_reader: xxx=.
|
82
|
+
|
83
|
+
*** =attr_reader_auto= option
|
84
|
+
|
85
|
+
Automatically all attributes =attr_reader=
|
86
|
+
|
87
|
+
*** What if I do not want you to feel free to access the attributes of a record?
|
88
|
+
|
89
|
+
=attr_reader= with no method definition and reference =object.attributes[:xxx]=
|
90
|
+
|
91
|
+
*** How do I add a method to an instance?
|
92
|
+
|
93
|
+
For that, I am creating a new class so I need to define it normally
|
94
|
+
|
95
|
+
*** =name= method is special?
|
96
|
+
|
97
|
+
If =name= is not defined, it defines a =name= method that returns a translation of =key=
|
98
|
+
|
99
|
+
*** =to_s= method is defined?
|
100
|
+
|
101
|
+
= Alias of =name=, =to_s= is defined.
|
102
|
+
|
103
|
+
*** If there is no key, use fetch to get an error
|
104
|
+
|
105
|
+
#+BEGIN_SRC ruby
|
106
|
+
Foo.fetch(:xxx) # => <KeyError: ...>
|
107
|
+
#+END_SRC
|
108
|
+
|
109
|
+
The following are all the same
|
110
|
+
|
111
|
+
#+BEGIN_SRC ruby
|
112
|
+
Foo[:xxx] || :default # => :default
|
113
|
+
Foo.fetch(:xxx, :default} # => :default
|
114
|
+
Foo.fetch(:xxx) { :default } # => :default
|
115
|
+
#+END_SRC
|
116
|
+
|
117
|
+
*** Use fetch_if to ignore if the key is nil
|
118
|
+
|
119
|
+
#+BEGIN_SRC ruby
|
120
|
+
Foo.fetch_if(nil) # => nil
|
121
|
+
Foo.fetch_if(:a) # => #<Foo:... @attributes={...}>
|
122
|
+
Foo.fetch_if(:xxx) # => <KeyError: ...>
|
123
|
+
#+END_SRC
|
data/Rakefile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
$LOAD_PATH.unshift '../lib'
|
2
|
+
require 'memory_record'
|
3
|
+
|
4
|
+
class Foo
|
5
|
+
include MemoryRecord
|
6
|
+
memory_record [
|
7
|
+
{key: :male, name: 'otoko'},
|
8
|
+
{key: :female, name: 'onna'},
|
9
|
+
], attr_reader: :name
|
10
|
+
end
|
11
|
+
|
12
|
+
Foo[:male] # => #<Foo:0x007ff459a494a8 @attributes={:key=>:male, :name=>"otoko", :code=>0}>
|
13
|
+
Foo[:female] # => #<Foo:0x007ff459a49390 @attributes={:key=>:female, :name=>"onna", :code=>1}>
|
14
|
+
|
15
|
+
Foo[:male].name # => "otoko"
|
16
|
+
Foo[:female].name # => "onna"
|
@@ -0,0 +1,14 @@
|
|
1
|
+
$LOAD_PATH.unshift '../lib'
|
2
|
+
require 'memory_record'
|
3
|
+
|
4
|
+
model = Class.new do
|
5
|
+
include MemoryRecord
|
6
|
+
memory_record [
|
7
|
+
{a: 1},
|
8
|
+
{b: 2},
|
9
|
+
], attr_reader_auto: true
|
10
|
+
end
|
11
|
+
|
12
|
+
model.first.name # => nil
|
13
|
+
model.first.a # => 1
|
14
|
+
model.first.b # => nil
|
@@ -0,0 +1,14 @@
|
|
1
|
+
$LOAD_PATH.unshift '../lib'
|
2
|
+
require 'memory_record'
|
3
|
+
|
4
|
+
class Foo
|
5
|
+
include MemoryRecord
|
6
|
+
memory_record [
|
7
|
+
{code: 1, key: :a, name: 'A'},
|
8
|
+
{code: 2, key: :b, name: 'B'},
|
9
|
+
{code: 3, key: :c, name: 'C'},
|
10
|
+
], :attr_reader => :name
|
11
|
+
end
|
12
|
+
|
13
|
+
Foo[2].name # => 'B'
|
14
|
+
Foo[:b].name # => 'B'
|
@@ -0,0 +1,27 @@
|
|
1
|
+
$LOAD_PATH.unshift '../lib'
|
2
|
+
require 'memory_record'
|
3
|
+
|
4
|
+
class Direction
|
5
|
+
include MemoryRecord
|
6
|
+
memory_record [
|
7
|
+
{key: :left, name: '←', vector: [-1, 0]},
|
8
|
+
{key: :right, name: '→', vector: [ 1, 0]},
|
9
|
+
], attr_reader_auto: true
|
10
|
+
|
11
|
+
def long_name
|
12
|
+
'#{name}方向'
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
Direction.collect(&:name) # => ['←', '→']
|
17
|
+
Direction.keys # => [:left, :right]
|
18
|
+
|
19
|
+
Direction[:right].key # => :right
|
20
|
+
Direction[:right].code # => 1
|
21
|
+
Direction[:right].vector # => [1, 0]
|
22
|
+
Direction[:right].long_name # => '→方向'
|
23
|
+
|
24
|
+
Direction[1].key # => :right
|
25
|
+
|
26
|
+
Direction[:up] # => nil
|
27
|
+
Direction.fetch(:up) rescue $! # => #<KeyError: Direction.fetch(:up) does not match anything
|
@@ -0,0 +1,21 @@
|
|
1
|
+
$LOAD_PATH.unshift '../lib'
|
2
|
+
require 'memory_record'
|
3
|
+
|
4
|
+
class C
|
5
|
+
include MemoryRecord
|
6
|
+
memory_record [{key: :a}], attr_reader: :name
|
7
|
+
end
|
8
|
+
|
9
|
+
C.keys # => [:a]
|
10
|
+
|
11
|
+
# memory_record では更新できない
|
12
|
+
class C
|
13
|
+
memory_record [{key: :b}], attr_reader: :name
|
14
|
+
end
|
15
|
+
|
16
|
+
C.keys # => [:a]
|
17
|
+
|
18
|
+
# memory_record_list_set を使うこと
|
19
|
+
C.memory_record_list_set [{key: :c}]
|
20
|
+
|
21
|
+
C.keys # => [:c]
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# freeze してしまうとこれらのメモ化ができなくなる
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift '../lib'
|
4
|
+
require 'memory_record'
|
5
|
+
|
6
|
+
class C
|
7
|
+
def self.x
|
8
|
+
@x ||= 'OK'
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class C2
|
13
|
+
include MemoryRecord
|
14
|
+
memory_record [
|
15
|
+
{model: C},
|
16
|
+
], attr_reader_auto: true
|
17
|
+
|
18
|
+
def x
|
19
|
+
@x ||= 'OK'
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
C2.first.x # => "OK"
|
24
|
+
C2.first.model.x # => "OK"
|
@@ -0,0 +1,20 @@
|
|
1
|
+
$LOAD_PATH.unshift '../lib'
|
2
|
+
require 'memory_record'
|
3
|
+
|
4
|
+
class Foo
|
5
|
+
include MemoryRecord
|
6
|
+
memory_record [
|
7
|
+
{a: 10},
|
8
|
+
], attr_reader: :a
|
9
|
+
|
10
|
+
def a
|
11
|
+
super * 2
|
12
|
+
end
|
13
|
+
|
14
|
+
def name
|
15
|
+
"(#{super})"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
Foo.first.a # => 20
|
20
|
+
Foo.first.name # => "(Key0)"
|
@@ -0,0 +1,23 @@
|
|
1
|
+
$LOAD_PATH.unshift '../lib'
|
2
|
+
require 'memory_record'
|
3
|
+
|
4
|
+
class Foo
|
5
|
+
include MemoryRecord
|
6
|
+
memory_record [
|
7
|
+
{name: 'alice'},
|
8
|
+
{name: 'bob'},
|
9
|
+
], attr_reader_auto: true
|
10
|
+
end
|
11
|
+
|
12
|
+
require 'active_model'
|
13
|
+
|
14
|
+
class Foo
|
15
|
+
include ActiveModel::Validations
|
16
|
+
validates :name, length: { maximum: 3 }
|
17
|
+
end
|
18
|
+
|
19
|
+
Foo.collect(&:valid?) # => [false, true]
|
20
|
+
|
21
|
+
foo = Foo.first
|
22
|
+
foo.valid? # => false
|
23
|
+
foo.errors.full_messages # => ['Name is too long (maximum is 3 characters)']
|
@@ -0,0 +1,12 @@
|
|
1
|
+
$LOAD_PATH.unshift '../lib'
|
2
|
+
require 'memory_record'
|
3
|
+
|
4
|
+
class Foo
|
5
|
+
include MemoryRecord
|
6
|
+
memory_record [{key: :a}, {key: :a},] rescue $! # => #<ArgumentError: Foo#key の :a が重複しています
|
7
|
+
end
|
8
|
+
|
9
|
+
class Bar
|
10
|
+
include MemoryRecord
|
11
|
+
memory_record [{code: 0}, {code: 0},] rescue $! # => #<ArgumentError: Bar#code の 0 が重複しています
|
12
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
$LOAD_PATH.unshift '../lib'
|
2
|
+
require 'memory_record'
|
3
|
+
|
4
|
+
class Foo
|
5
|
+
include MemoryRecord
|
6
|
+
memory_record [
|
7
|
+
{key: '01', name: '→'},
|
8
|
+
{key: '02', name: '←'},
|
9
|
+
], attr_reader: :name
|
10
|
+
end
|
11
|
+
|
12
|
+
Foo['01'] # => #<Foo:0x007fac2c35db28 @attributes={:key=>:'01', :name=>'→', :code=>0}>
|
13
|
+
Foo['02'] # => #<Foo:0x007fac2c35d9e8 @attributes={:key=>:'02', :name=>'←', :code=>1}>
|
14
|
+
|
15
|
+
# このようにもできるがまったくオススメできない
|
16
|
+
# マジックナンバーを増やすメリットはない
|
17
|
+
# なんのためにキーがあるのか考えよう
|
@@ -0,0 +1,33 @@
|
|
1
|
+
$LOAD_PATH.unshift '../lib'
|
2
|
+
require 'memory_record'
|
3
|
+
|
4
|
+
require 'active_record'
|
5
|
+
|
6
|
+
ActiveRecord::VERSION::STRING # => "5.1.3"
|
7
|
+
ActiveRecord::Migration.verbose = false
|
8
|
+
ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
|
9
|
+
|
10
|
+
ActiveRecord::Schema.define do
|
11
|
+
create_table :users do |t|
|
12
|
+
t.integer :gender_key
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class GenderInfo
|
17
|
+
include MemoryRecord
|
18
|
+
memory_record [
|
19
|
+
{key: :male, name: 'otoko'},
|
20
|
+
{key: :female, name: 'onna'},
|
21
|
+
], attr_reader: :name
|
22
|
+
end
|
23
|
+
|
24
|
+
class User < ActiveRecord::Base
|
25
|
+
enum gender_key: GenderInfo.keys
|
26
|
+
|
27
|
+
def gender_info
|
28
|
+
GenderInfo[gender_key]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
user = User.create!(gender_key: :male)
|
33
|
+
user.gender_info.name # => "otoko"
|
@@ -0,0 +1,153 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'active_support/concern'
|
3
|
+
require 'active_support/core_ext/module/concerning'
|
4
|
+
require 'active_support/core_ext/class/attribute'
|
5
|
+
require 'active_support/core_ext/array/wrap'
|
6
|
+
require 'active_model'
|
7
|
+
|
8
|
+
module MemoryRecord
|
9
|
+
extend ActiveSupport::Concern
|
10
|
+
|
11
|
+
class_methods do
|
12
|
+
def memory_record(list, **options, &block)
|
13
|
+
return if memory_record_defined?
|
14
|
+
|
15
|
+
extend ActiveModel::Translation
|
16
|
+
extend Enumerable
|
17
|
+
include ::MemoryRecord::SingletonMethods
|
18
|
+
|
19
|
+
class_attribute :memory_record_configuration
|
20
|
+
self.memory_record_configuration = {
|
21
|
+
attr_reader: [],
|
22
|
+
}.merge(options)
|
23
|
+
|
24
|
+
if block_given?
|
25
|
+
yield memory_record_configuration
|
26
|
+
end
|
27
|
+
|
28
|
+
if memory_record_configuration[:attr_reader_auto]
|
29
|
+
_attr_reader = list.inject([]) { |a, e| a | e.keys.collect(&:to_sym) }
|
30
|
+
else
|
31
|
+
_attr_reader = memory_record_configuration[:attr_reader]
|
32
|
+
end
|
33
|
+
|
34
|
+
include Module.new.tap { |m|
|
35
|
+
([:key, :code] + Array.wrap(_attr_reader)).uniq.each do |key|
|
36
|
+
m.class_eval do
|
37
|
+
define_method(key) { @attributes[key.to_sym] }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
unless m.method_defined?(:name)
|
42
|
+
m.class_eval do
|
43
|
+
define_method(:name) do
|
44
|
+
if self.class.name
|
45
|
+
self.class.human_attribute_name(key)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
}
|
51
|
+
|
52
|
+
memory_record_list_set(list)
|
53
|
+
end
|
54
|
+
|
55
|
+
def memory_record_defined?
|
56
|
+
ancestors.include?(SingletonMethods)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
concern :SingletonMethods do
|
61
|
+
class_methods do
|
62
|
+
def memory_record?
|
63
|
+
memory_record_defined?
|
64
|
+
end
|
65
|
+
|
66
|
+
def lookup(key)
|
67
|
+
return key if key.kind_of? self
|
68
|
+
case key
|
69
|
+
when Symbol, String
|
70
|
+
@values_hash[:key][key.to_sym]
|
71
|
+
else
|
72
|
+
@values_hash[:code][key]
|
73
|
+
end
|
74
|
+
end
|
75
|
+
alias [] lookup
|
76
|
+
|
77
|
+
def fetch(key, default = nil, &block)
|
78
|
+
raise ArgumentError if block_given? && default
|
79
|
+
v = lookup(key)
|
80
|
+
unless v
|
81
|
+
case
|
82
|
+
when block_given?
|
83
|
+
v = yield
|
84
|
+
when default
|
85
|
+
v = default
|
86
|
+
else
|
87
|
+
raise KeyError, [
|
88
|
+
"#{name}.fetch(#{key.inspect}) does not match anything",
|
89
|
+
"keys: #{keys.inspect}",
|
90
|
+
"codes: #{codes.inspect}",
|
91
|
+
].join("\n")
|
92
|
+
end
|
93
|
+
end
|
94
|
+
v
|
95
|
+
end
|
96
|
+
|
97
|
+
def fetch_if(key, default = nil, &block)
|
98
|
+
if key
|
99
|
+
fetch(key, default, &block)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
delegate :each, to: :values
|
104
|
+
|
105
|
+
def keys
|
106
|
+
@keys ||= @values_hash[:key].keys
|
107
|
+
end
|
108
|
+
|
109
|
+
def codes
|
110
|
+
@codes ||= @values_hash[:code].keys
|
111
|
+
end
|
112
|
+
|
113
|
+
attr_reader :values
|
114
|
+
|
115
|
+
def memory_record_list_set(list)
|
116
|
+
@keys = nil
|
117
|
+
@codes = nil
|
118
|
+
@values = list.collect.with_index {|e, i| new(_attributes_normalize(e, i)) }.freeze
|
119
|
+
@values_hash = {}
|
120
|
+
[:code, :key].each do |pk|
|
121
|
+
@values_hash[pk] = @values.inject({}) do |a, e|
|
122
|
+
a.merge(e.send(pk) => e) do |key, a, b|
|
123
|
+
raise ArgumentError, [
|
124
|
+
"#{name}##{pk} #{key.inspect} is duplicate",
|
125
|
+
"Existing: #{a.attributes.inspect}",
|
126
|
+
"Collision: #{b.attributes.inspect}",
|
127
|
+
].join("\n")
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
private
|
134
|
+
|
135
|
+
def _attributes_normalize(attrs, index)
|
136
|
+
key = attrs[:key] || "_key#{index}"
|
137
|
+
if key.kind_of? Array
|
138
|
+
key = key.join('_')
|
139
|
+
end
|
140
|
+
attrs.merge(code: attrs[:code] || index, key: key.to_sym)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
attr_reader :attributes
|
145
|
+
|
146
|
+
delegate :[], to: :attributes
|
147
|
+
delegate :to_s, to: :name
|
148
|
+
|
149
|
+
def initialize(attributes)
|
150
|
+
@attributes = attributes
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
lib = File.expand_path('../lib', __FILE__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
require 'memory_record/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = 'memory_record'
|
7
|
+
spec.version = MemoryRecord::VERSION
|
8
|
+
spec.authors = ['akicho8']
|
9
|
+
spec.email = ['akicho8@gmail.com']
|
10
|
+
spec.description = %q{A small number of records Easy handling library}
|
11
|
+
spec.summary = %q{A small number of records Easy handling library}
|
12
|
+
spec.homepage = ''
|
13
|
+
spec.license = 'MIT'
|
14
|
+
|
15
|
+
spec.files = `git ls-files`.split($/)
|
16
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
17
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
|
+
spec.require_paths = ['lib']
|
19
|
+
|
20
|
+
spec.add_development_dependency 'rake'
|
21
|
+
spec.add_development_dependency 'rspec'
|
22
|
+
spec.add_development_dependency 'test-unit'
|
23
|
+
|
24
|
+
spec.add_dependency 'activesupport'
|
25
|
+
spec.add_dependency 'activemodel'
|
26
|
+
end
|
@@ -0,0 +1,164 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class Model
|
4
|
+
include MemoryRecord
|
5
|
+
memory_record [
|
6
|
+
{name: 'A'},
|
7
|
+
{name: 'B'},
|
8
|
+
], attr_reader: :name
|
9
|
+
end
|
10
|
+
|
11
|
+
class Legacy
|
12
|
+
include MemoryRecord
|
13
|
+
memory_record [
|
14
|
+
{code: 10, key: :a, name: 'A'},
|
15
|
+
{code: 20, key: :b, name: 'B'},
|
16
|
+
], attr_reader: :name
|
17
|
+
end
|
18
|
+
|
19
|
+
RSpec.describe MemoryRecord do
|
20
|
+
def __define(table)
|
21
|
+
Class.new {
|
22
|
+
include MemoryRecord
|
23
|
+
memory_record table, attr_reader_auto: true
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
let(:instance) { Model.first }
|
28
|
+
|
29
|
+
context 'Useful Class Methods' do
|
30
|
+
it 'each' do
|
31
|
+
assert Model.each
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'keys' do
|
35
|
+
assert_equal [:_key0, :_key1], Model.keys
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'values' do
|
39
|
+
assert_equal Model.each.to_a, Model.values
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context 'Subscript access to class' do
|
44
|
+
it 'When code and key are automatically waved' do
|
45
|
+
assert_equal 'A', Model[0].name
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'It will not cause an error even if there is no corresponding key' do
|
49
|
+
assert_nothing_raised { Model[:unknown] }
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'In the case of fetch, if there is no corresponding key, an error occurs' do
|
53
|
+
assert_raises { Model.fetch(:unknown) }
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'fetch_if' do
|
57
|
+
assert_nothing_raised { Model.fetch_if(nil) }
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context 'Subscript access to instance' do
|
62
|
+
it do
|
63
|
+
instance[:name].should == 'A'
|
64
|
+
instance[:xxxx].should == nil
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
context 'to_s' do
|
69
|
+
it do
|
70
|
+
instance.to_s.should == 'A'
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
context 'instance accessor' do
|
75
|
+
it do
|
76
|
+
assert instance.attributes
|
77
|
+
assert instance.key
|
78
|
+
assert instance.code
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
context 'Re-set' do
|
83
|
+
before do
|
84
|
+
@model = __define [{key: :a}]
|
85
|
+
@model.memory_record_list_set [{key: :b}, {key: :c}]
|
86
|
+
end
|
87
|
+
it 'changed' do
|
88
|
+
assert_equal [:b, :c], @model.keys
|
89
|
+
assert_equal [0, 1], @model.codes
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
context 'Subtle specifications' do
|
94
|
+
it 'When keys are specified as an array, they become symbols with underscores' do
|
95
|
+
model = __define [{key: [:id, :desc]}]
|
96
|
+
assert_equal [:id_desc], model.keys
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'Name method is automatically defined if it is not defined' do
|
100
|
+
model = __define []
|
101
|
+
assert_equal true, model.instance_methods.include?(:name)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'Japanese can be used for key' do
|
106
|
+
model = __define [{key: 'あ'}]
|
107
|
+
assert model['あ']
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'When you define code and keys yourself' do
|
111
|
+
assert_equal 'A', Legacy[10].name
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'We do not freeze values because memoization becomes impossible' do
|
115
|
+
Model.first.name.upcase!
|
116
|
+
end
|
117
|
+
|
118
|
+
describe 'super' do
|
119
|
+
class Model2
|
120
|
+
include MemoryRecord
|
121
|
+
memory_record [
|
122
|
+
{var: 'x'},
|
123
|
+
], attr_reader: :var
|
124
|
+
|
125
|
+
def var
|
126
|
+
super + 'y'
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
it 'Since methods are defined in ancestry, you can use super' do
|
131
|
+
assert_equal 'xy', Model2.first.var
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
describe 'attr_reader_auto' do
|
136
|
+
class Model3
|
137
|
+
include MemoryRecord
|
138
|
+
memory_record [
|
139
|
+
{a: 1},
|
140
|
+
{b: 2},
|
141
|
+
], attr_reader_auto: true
|
142
|
+
end
|
143
|
+
|
144
|
+
it do
|
145
|
+
assert_equal 1, Model3.first.a
|
146
|
+
assert_equal nil, Model3.first.b
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
describe 'Do not duplicate key and code' do
|
151
|
+
it do
|
152
|
+
expect { Model.memory_record_list_set([{key: :a}, {key: :a}]) }.to raise_error(ArgumentError)
|
153
|
+
expect { Model.memory_record_list_set([{code: 0}, {code: 0}]) }.to raise_error(ArgumentError)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
describe 'Human_attribute_name can not be used in anonymous class' do
|
158
|
+
let(:model) { __define [{foo: 1}] }
|
159
|
+
|
160
|
+
it 'It does not cause an error' do
|
161
|
+
model.first.name.should == nil
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,144 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: memory_record
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- akicho8
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-08-11 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rake
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rspec
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: test-unit
|
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: activesupport
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
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: activemodel
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
description: A small number of records Easy handling library
|
84
|
+
email:
|
85
|
+
- akicho8@gmail.com
|
86
|
+
executables: []
|
87
|
+
extensions: []
|
88
|
+
extra_rdoc_files: []
|
89
|
+
files:
|
90
|
+
- ".gitignore"
|
91
|
+
- ".rspec"
|
92
|
+
- ".travis.yml"
|
93
|
+
- Gemfile
|
94
|
+
- LICENSE.txt
|
95
|
+
- README.org
|
96
|
+
- Rakefile
|
97
|
+
- examples/0110_basic.rb
|
98
|
+
- examples/0120_attr_reader_auto_option.rb
|
99
|
+
- examples/0130_for_legacy_code.rb
|
100
|
+
- examples/0140_key_join_underscore_if_array.rb
|
101
|
+
- examples/0150_behavior_like_hash.rb
|
102
|
+
- examples/0160_practice.rb
|
103
|
+
- examples/0170_reset.rb
|
104
|
+
- examples/0180_key_is_anything_ok.rb
|
105
|
+
- examples/0190_tips_on_handling_boolean_type.rb
|
106
|
+
- examples/0200_freeze_makes_it_impossible_to_memorize.rb
|
107
|
+
- examples/0210_super_usable.rb
|
108
|
+
- examples/0230_validation.rb
|
109
|
+
- examples/0240_key_duplicate.rb
|
110
|
+
- examples/0250_when_key_like_numeric.rb
|
111
|
+
- examples/0260_with_use_activerecord_enum.rb
|
112
|
+
- lib/memory_record.rb
|
113
|
+
- lib/memory_record/memory_record.rb
|
114
|
+
- lib/memory_record/version.rb
|
115
|
+
- memory_record.gemspec
|
116
|
+
- spec/memory_record_spec.rb
|
117
|
+
- spec/spec_helper.rb
|
118
|
+
homepage: ''
|
119
|
+
licenses:
|
120
|
+
- MIT
|
121
|
+
metadata: {}
|
122
|
+
post_install_message:
|
123
|
+
rdoc_options: []
|
124
|
+
require_paths:
|
125
|
+
- lib
|
126
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
127
|
+
requirements:
|
128
|
+
- - ">="
|
129
|
+
- !ruby/object:Gem::Version
|
130
|
+
version: '0'
|
131
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
132
|
+
requirements:
|
133
|
+
- - ">="
|
134
|
+
- !ruby/object:Gem::Version
|
135
|
+
version: '0'
|
136
|
+
requirements: []
|
137
|
+
rubyforge_project:
|
138
|
+
rubygems_version: 2.6.11
|
139
|
+
signing_key:
|
140
|
+
specification_version: 4
|
141
|
+
summary: A small number of records Easy handling library
|
142
|
+
test_files:
|
143
|
+
- spec/memory_record_spec.rb
|
144
|
+
- spec/spec_helper.rb
|