memory_record 0.0.1
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 +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
|