yose 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +56 -0
- data/Gemfile +6 -0
- data/LICENSE +21 -0
- data/README.md +155 -0
- data/Rakefile +2 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/yose.rb +212 -0
- data/lib/yose/version.rb +3 -0
- data/sample/shift_table.rb +92 -0
- data/yose.gemspec +30 -0
- metadata +69 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: de20f89269c68a14ab49bf3d36616ec838bb78ee53474f170c497defeed781c1
|
4
|
+
data.tar.gz: 5d509316146b1e8e572a189dcc4d1e9b5e6d82fb398c5a28f8e59a36635a10b4
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3afe45de85b82de653d23395fc5dd774999f9ed3388dd2d86d7674b89ec5bfbdfa902ae1119c3ff435b6a35451b5e2e7a31dfa07c10ac9d48f9ac325bcc7a19b
|
7
|
+
data.tar.gz: 2e6be48bc2f4cd50c1fbe32393d6075862dfeb93f32ac1f14becce5de402cc4b9e87bd4fb7f7d91daf603719b5ae615b9dfa95b092f82586fa80e90862878121
|
data/.gitignore
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
/.config
|
4
|
+
/coverage/
|
5
|
+
/InstalledFiles
|
6
|
+
/pkg/
|
7
|
+
/spec/reports/
|
8
|
+
/spec/examples.txt
|
9
|
+
/test/tmp/
|
10
|
+
/test/version_tmp/
|
11
|
+
/tmp/
|
12
|
+
|
13
|
+
# Used by dotenv library to load environment variables.
|
14
|
+
# .env
|
15
|
+
|
16
|
+
# Ignore Byebug command history file.
|
17
|
+
.byebug_history
|
18
|
+
|
19
|
+
## Specific to RubyMotion:
|
20
|
+
.dat*
|
21
|
+
.repl_history
|
22
|
+
build/
|
23
|
+
*.bridgesupport
|
24
|
+
build-iPhoneOS/
|
25
|
+
build-iPhoneSimulator/
|
26
|
+
|
27
|
+
## Specific to RubyMotion (use of CocoaPods):
|
28
|
+
#
|
29
|
+
# We recommend against adding the Pods directory to your .gitignore. However
|
30
|
+
# you should judge for yourself, the pros and cons are mentioned at:
|
31
|
+
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
|
32
|
+
#
|
33
|
+
# vendor/Pods/
|
34
|
+
|
35
|
+
## Documentation cache and generated files:
|
36
|
+
/.yardoc/
|
37
|
+
/_yardoc/
|
38
|
+
/doc/
|
39
|
+
/rdoc/
|
40
|
+
|
41
|
+
## Environment normalization:
|
42
|
+
/.bundle/
|
43
|
+
/vendor/bundle
|
44
|
+
/lib/bundler/man/
|
45
|
+
|
46
|
+
# for a library or gem, you might want to ignore these files since the code is
|
47
|
+
# intended to run in multiple environments; otherwise, check them in:
|
48
|
+
# Gemfile.lock
|
49
|
+
# .ruby-version
|
50
|
+
# .ruby-gemset
|
51
|
+
|
52
|
+
# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
|
53
|
+
.rvmrc
|
54
|
+
|
55
|
+
# Used by RuboCop. Remote config files pulled in from inherit_from directive.
|
56
|
+
# .rubocop-https?--*
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2021 Masatoshi SEKI
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,155 @@
|
|
1
|
+
# Yose
|
2
|
+
Simple JSON DB using psql, jsonb, ruby
|
3
|
+
|
4
|
+
# setup PG
|
5
|
+
|
6
|
+
create database 'yose'
|
7
|
+
|
8
|
+
```
|
9
|
+
% createdb yose
|
10
|
+
```
|
11
|
+
|
12
|
+
or use existing database,
|
13
|
+
|
14
|
+
```
|
15
|
+
% export YOSE_DATABASE_URL=existing_database
|
16
|
+
```
|
17
|
+
```
|
18
|
+
% export DATABASE_URL=existing_database
|
19
|
+
```
|
20
|
+
|
21
|
+
searching order:
|
22
|
+
```
|
23
|
+
url = ENV['YOSE_DATABASE_URL'] || ENV['DATABASE_URL'] || 'postgres:///yose'
|
24
|
+
```
|
25
|
+
|
26
|
+
# usage
|
27
|
+
|
28
|
+
## create_table, drop_table
|
29
|
+
|
30
|
+
default table name is 'world'.
|
31
|
+
|
32
|
+
```
|
33
|
+
irb(main):001:0> Yose::create_table
|
34
|
+
irb(main):002:0> Yose::create_table('world')
|
35
|
+
...
|
36
|
+
PG::DuplicateTable (ERROR: relation "world" already exists)
|
37
|
+
irb(main):003:0> Yose::drop_table('world')
|
38
|
+
irb(main):004:0> Yose::create_table('world')
|
39
|
+
```
|
40
|
+
|
41
|
+
```
|
42
|
+
irb(main):007:0> Yose::create_table('another')
|
43
|
+
irb(main):008:0> Yose::drop_table('another')
|
44
|
+
```
|
45
|
+
|
46
|
+
## alloc jsonb object
|
47
|
+
|
48
|
+
alloc, and free. alloc retruns the UID.
|
49
|
+
|
50
|
+
```
|
51
|
+
% irb -r yose --simple-prompt
|
52
|
+
>> root = Yose::Store.new
|
53
|
+
>> it
|
54
|
+
=> "f51a923e-99aa-41f0-9b9f-e3b596b66aa9"
|
55
|
+
>> it = root.alloc({'foo' => 'bar', 'ary' => [1, 2, 3]}.to_json)
|
56
|
+
>> root[it]
|
57
|
+
=> {"ary"=>[1, 2, 3], "foo"=>"bar"}
|
58
|
+
>> root.free(it)
|
59
|
+
=> {"ary"=>[1, 2, 3], "foo"=>"bar"}
|
60
|
+
>> root[it]
|
61
|
+
=> nil
|
62
|
+
```
|
63
|
+
|
64
|
+
for Hash, obj.to_json automatically.
|
65
|
+
|
66
|
+
```
|
67
|
+
>> it = root.alloc({'foo' => 'bar', 'ary' => [1, 2, 3]})
|
68
|
+
>> root[it]
|
69
|
+
=> {"ary"=>[1, 2, 3], "foo"=>"bar"}
|
70
|
+
```
|
71
|
+
|
72
|
+
alloc! checks for uniqueness. (using @> op.)
|
73
|
+
|
74
|
+
```
|
75
|
+
>> root.alloc!({'type' => 'employee', 'name' => '@m_seki'})
|
76
|
+
=> "ed98d46c-673a-4dd2-838f-194e42291f2f"
|
77
|
+
>> root.alloc!({'type' => 'employee', 'name' => '@m_seki'})
|
78
|
+
...
|
79
|
+
RuntimeError (already exist {"type":"employee","name":"@m_seki"})
|
80
|
+
>> root.alloc!({'type' => 'employee', 'name' => '@awazeki'})
|
81
|
+
=> "39bfdf2b-4fa1-4320-9cff-efbce77b6cea"
|
82
|
+
>> root.alloc!({'type' => 'employee'})
|
83
|
+
...
|
84
|
+
RuntimeError (already exist {"type":"employee"})
|
85
|
+
>> root.alloc!({'type' => 'author', 'name' => '@m_seki'})
|
86
|
+
=> "2d0b596d-bc09-4687-a9d5-9dec087ad9df"
|
87
|
+
```
|
88
|
+
|
89
|
+
## search
|
90
|
+
|
91
|
+
```
|
92
|
+
>> pp root.search({'type' => 'employee'})
|
93
|
+
[{"uid"=>"ed98d46c-673a-4dd2-838f-194e42291f2f",
|
94
|
+
"obj"=>{"name"=>"@m_seki", "type"=>"employee"},
|
95
|
+
"mtime"=>2021-02-18 18:37:30.220031 +0900},
|
96
|
+
{"uid"=>"39bfdf2b-4fa1-4320-9cff-efbce77b6cea",
|
97
|
+
"obj"=>{"name"=>"@awazeki", "type"=>"employee"},
|
98
|
+
"mtime"=>2021-02-18 18:37:42.337694 +0900}]
|
99
|
+
>> pp root.search({'name' => '@m_seki'})
|
100
|
+
[{"uid"=>"ed98d46c-673a-4dd2-838f-194e42291f2f",
|
101
|
+
"obj"=>{"name"=>"@m_seki", "type"=>"employee"},
|
102
|
+
"mtime"=>2021-02-18 18:37:30.220031 +0900},
|
103
|
+
{"uid"=>"2d0b596d-bc09-4687-a9d5-9dec087ad9df",
|
104
|
+
"obj"=>{"name"=>"@m_seki", "type"=>"author"},
|
105
|
+
"mtime"=>2021-02-18 18:37:56.323186 +0900}]
|
106
|
+
>> pp root.search({'name' => '@m_seki', 'type' => 'author'})
|
107
|
+
[{"uid"=>"2d0b596d-bc09-4687-a9d5-9dec087ad9df",
|
108
|
+
"obj"=>{"name"=>"@m_seki", "type"=>"author"},
|
109
|
+
"mtime"=>2021-02-18 18:37:56.323186 +0900}]
|
110
|
+
```
|
111
|
+
|
112
|
+
## merge (update), delete
|
113
|
+
|
114
|
+
update merges jsonb object. (using || op.)
|
115
|
+
|
116
|
+
delete delets jsonb object. (using - op.)
|
117
|
+
|
118
|
+
```
|
119
|
+
>> root[it].update({'ary' => 'not ary'})
|
120
|
+
=> {"ary"=>"no ary", "foo"=>"bar"}
|
121
|
+
>> root[it]
|
122
|
+
=> {"ary"=>[1, 2, 3], "foo"=>"bar"}
|
123
|
+
>> root.update(it, {'ary' => 'not ary'})
|
124
|
+
=> {"ary"=>"not ary", "foo"=>"bar"}
|
125
|
+
>> root[it]
|
126
|
+
=> {"ary"=>"not ary", "foo"=>"bar"}
|
127
|
+
>> root.delete(it, 'ary')
|
128
|
+
=> {"foo"=>"bar"}
|
129
|
+
>> root[it]
|
130
|
+
=> {"foo"=>"bar"}
|
131
|
+
>> root.merge(it, {'hash' => {'foo' => 'bar', 'bar' => 'baz'}})
|
132
|
+
=> {"foo"=>"bar", "hash"=>{"bar"=>"baz", "foo"=>"bar"}}
|
133
|
+
>> root[it]
|
134
|
+
=> {"foo"=>"bar", "hash"=>{"bar"=>"baz", "foo"=>"bar"}}
|
135
|
+
```
|
136
|
+
|
137
|
+
## mtime and recent changes
|
138
|
+
|
139
|
+
```
|
140
|
+
>> he = root.search({'type' => 'employee', 'name' => '@awazeki'}).dig(0, 'uid')
|
141
|
+
>> root[he]
|
142
|
+
=> {"name"=>"@awazeki", "type"=>"employee"}
|
143
|
+
>> root.mtime(he)
|
144
|
+
=> 2021-02-18 18:37:42.337694 +0900
|
145
|
+
```
|
146
|
+
|
147
|
+
```
|
148
|
+
>> pp root.recent(root.mtime(he))
|
149
|
+
[{"uid"=>"3e13e180-e5fc-4179-9f4c-d8fbb5fe9bf4",
|
150
|
+
"obj"=>{"foo"=>"bar", "hash"=>{"bar"=>"baz", "foo"=>"bar"}},
|
151
|
+
"mtime"=>2021-02-18 18:42:42.658912 +0900},
|
152
|
+
{"uid"=>"2d0b596d-bc09-4687-a9d5-9dec087ad9df",
|
153
|
+
"obj"=>{"name"=>"@m_seki", "type"=>"author"},
|
154
|
+
"mtime"=>2021-02-18 18:37:56.323186 +0900}]
|
155
|
+
```
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "yose"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
data/lib/yose.rb
ADDED
@@ -0,0 +1,212 @@
|
|
1
|
+
require "pg"
|
2
|
+
require "monitor"
|
3
|
+
require "pp"
|
4
|
+
require "json"
|
5
|
+
require "securerandom"
|
6
|
+
|
7
|
+
module Yose
|
8
|
+
module_function
|
9
|
+
=begin
|
10
|
+
create extension moddatetime;
|
11
|
+
|
12
|
+
create table world (
|
13
|
+
uid uuid
|
14
|
+
, obj jsonb
|
15
|
+
, mtime timestamp DEFAULT CURRENT_TIMESTAMP NOT NULL
|
16
|
+
, primary key(uid));
|
17
|
+
|
18
|
+
CREATE TRIGGER world_moddatetime
|
19
|
+
BEFORE UPDATE ON world
|
20
|
+
FOR EACH ROW
|
21
|
+
EXECUTE PROCEDURE moddatetime (mtime);
|
22
|
+
|
23
|
+
CREATE INDEX world_idxgin ON world USING GIN (obj jsonb_path_ops);
|
24
|
+
=end
|
25
|
+
def create_table(name="world")
|
26
|
+
db = Yose::DB.instance
|
27
|
+
db.conn.exec("create extension moddatetime;") rescue nil
|
28
|
+
db.transaction do |c|
|
29
|
+
c.exec("create table #{name} (
|
30
|
+
uid uuid
|
31
|
+
, obj jsonb
|
32
|
+
, mtime timestamp DEFAULT CURRENT_TIMESTAMP NOT NULL
|
33
|
+
, primary key(uid));")
|
34
|
+
c.exec("CREATE TRIGGER #{name}_moddatetime
|
35
|
+
BEFORE UPDATE ON #{name}
|
36
|
+
FOR EACH ROW
|
37
|
+
EXECUTE PROCEDURE moddatetime (mtime);")
|
38
|
+
c.exec("CREATE INDEX #{name}_idxgin
|
39
|
+
ON #{name} USING GIN (obj jsonb_path_ops);")
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def drop_table(name)
|
44
|
+
db = Yose::DB.instance
|
45
|
+
db.conn.exec("drop INDEX #{name}_idxgin") rescue nil
|
46
|
+
db.conn.exec("drop TRIGGER #{name}_moddatetime on #{name}") rescue nil
|
47
|
+
db.conn.exec("drop table #{name}") rescue nil
|
48
|
+
end
|
49
|
+
|
50
|
+
class DB
|
51
|
+
include MonitorMixin
|
52
|
+
def self.instance
|
53
|
+
@instance = self.new unless @instance
|
54
|
+
@instance.synchronize do
|
55
|
+
@instance = self.new unless @instance.ping
|
56
|
+
end
|
57
|
+
return @instance
|
58
|
+
rescue
|
59
|
+
nil
|
60
|
+
end
|
61
|
+
|
62
|
+
def initialize
|
63
|
+
super()
|
64
|
+
url = ENV['YOSE_DATABASE_URL'] || ENV['DATABASE_URL'] || 'postgres:///yose'
|
65
|
+
@conn = PG.connect(url)
|
66
|
+
@conn.type_map_for_results = PG::BasicTypeMapForResults.new(@conn)
|
67
|
+
end
|
68
|
+
attr :conn
|
69
|
+
|
70
|
+
def ping
|
71
|
+
@conn.exec("select 1")
|
72
|
+
true
|
73
|
+
rescue
|
74
|
+
false
|
75
|
+
end
|
76
|
+
|
77
|
+
def transaction
|
78
|
+
#FIXME
|
79
|
+
synchronize do
|
80
|
+
@conn.transaction do |c|
|
81
|
+
yield(c)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
class Store
|
88
|
+
def initialize(name='world')
|
89
|
+
@name = name
|
90
|
+
end
|
91
|
+
|
92
|
+
def as_json(json)
|
93
|
+
(Hash === json) ? json.to_json : json
|
94
|
+
end
|
95
|
+
|
96
|
+
def as_pg_time(time)
|
97
|
+
time.strftime("%F %T.%6N")
|
98
|
+
end
|
99
|
+
|
100
|
+
def transaction(&proc)
|
101
|
+
DB.instance.transaction(&proc)
|
102
|
+
end
|
103
|
+
|
104
|
+
def alloc(json)
|
105
|
+
sql =<<EOQ
|
106
|
+
insert into #{@name}
|
107
|
+
(uid, obj)
|
108
|
+
values
|
109
|
+
($1, $2)
|
110
|
+
returning uid::text;
|
111
|
+
EOQ
|
112
|
+
transaction do |c|
|
113
|
+
c.exec_params(sql, [SecureRandom.uuid, as_json(json)]).first['uid']
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def alloc!(json)
|
118
|
+
sql =<<EOQ
|
119
|
+
insert into #{@name}
|
120
|
+
(uid, obj)
|
121
|
+
values
|
122
|
+
($1, $2)
|
123
|
+
returning uid::text;
|
124
|
+
EOQ
|
125
|
+
transaction do |c|
|
126
|
+
json = as_json(json)
|
127
|
+
sz = c.exec_params("select count(*) from #{@name} where obj @> $1",
|
128
|
+
[json]).first['count']
|
129
|
+
raise "already exist #{json}" if sz > 0
|
130
|
+
c.exec_params(sql, [SecureRandom.uuid, json]).first['uid']
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def free(uid)
|
135
|
+
sql =<<EOQ
|
136
|
+
delete from #{@name} where uid = $1 returning obj;
|
137
|
+
EOQ
|
138
|
+
transaction do |c|
|
139
|
+
c.exec_params(sql, [uid]).to_a.dig(0, 'obj')
|
140
|
+
end
|
141
|
+
end
|
142
|
+
alias forget free
|
143
|
+
|
144
|
+
def [](uid)
|
145
|
+
sql =<<EOQ
|
146
|
+
select obj from #{@name} where uid = $1 limit 1;
|
147
|
+
EOQ
|
148
|
+
transaction do |c|
|
149
|
+
c.exec_params(sql, [uid]).to_a.dig(0, 'obj')
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def mtime(uid)
|
154
|
+
sql =<<EOQ
|
155
|
+
select mtime from #{@name} where uid = $1 limit 1;
|
156
|
+
EOQ
|
157
|
+
transaction do |c|
|
158
|
+
c.exec_params(sql, [uid]).to_a.dig(0, 'mtime')
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def search(json)
|
163
|
+
sql =<<EOQ
|
164
|
+
select uid::text, obj, mtime from #{@name} where obj @> $1::jsonb;
|
165
|
+
EOQ
|
166
|
+
transaction do |c|
|
167
|
+
c.exec_params(sql, [as_json(json)]).to_a
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def update(uid, json)
|
172
|
+
sql =<<EOQ
|
173
|
+
update #{@name} set obj = obj || $2::jsonb where uid = $1 returning obj;
|
174
|
+
EOQ
|
175
|
+
transaction do |c|
|
176
|
+
c.exec_params(sql, [uid, as_json(json)]).to_a.to_a.dig(0, 'obj')
|
177
|
+
end
|
178
|
+
end
|
179
|
+
alias merge update
|
180
|
+
|
181
|
+
def replace(uid, json)
|
182
|
+
sql =<<EOQ
|
183
|
+
update #{@name} set obj = $2::jsonb where uid = $1 returning obj;
|
184
|
+
EOQ
|
185
|
+
transaction do |c|
|
186
|
+
c.exec_params(sql, [uid, as_json(json)]).to_a.dig(0, 'obj')
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
def delete(uid, key)
|
191
|
+
sql =<<EOQ
|
192
|
+
update #{@name} set obj = obj - $2 where uid = $1 returning obj;
|
193
|
+
EOQ
|
194
|
+
transaction do |c|
|
195
|
+
c.exec_params(sql, [uid, key]).to_a.dig(0, 'obj')
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
def recent(since)
|
200
|
+
sql =<<EOQ
|
201
|
+
select uid::text, obj, mtime from #{@name} where mtime > $1 order by mtime desc;
|
202
|
+
EOQ
|
203
|
+
transaction do |c|
|
204
|
+
c.exec_params(sql, [as_pg_time(since)]).to_a
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
if __FILE__ == $0
|
211
|
+
Yose::create_table
|
212
|
+
end
|
data/lib/yose/version.rb
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
require_relative '../lib/yose'
|
2
|
+
require 'date'
|
3
|
+
|
4
|
+
class ShiftStore
|
5
|
+
def initialize
|
6
|
+
@db = Yose::Store.new
|
7
|
+
end
|
8
|
+
attr_reader :db
|
9
|
+
|
10
|
+
def add_employee(name)
|
11
|
+
user = {
|
12
|
+
'type' => '従業員',
|
13
|
+
'name' => name
|
14
|
+
}
|
15
|
+
@db.alloc!(user.to_json)
|
16
|
+
end
|
17
|
+
|
18
|
+
def all_employee
|
19
|
+
@db.search({'type' => '従業員'})
|
20
|
+
end
|
21
|
+
|
22
|
+
def employee_by_name(name)
|
23
|
+
@db.search({'type' => '従業員', 'name' => name})
|
24
|
+
end
|
25
|
+
|
26
|
+
def employee_by_store(store)
|
27
|
+
@db.search({'type' => '従業員', '店' => [store]})
|
28
|
+
end
|
29
|
+
|
30
|
+
def add_plan(employee_uid, store, y, m, d, hhmm, hours)
|
31
|
+
plan = {
|
32
|
+
'type' => '予定',
|
33
|
+
'従業員' => employee_uid,
|
34
|
+
'店' => store,
|
35
|
+
'y' => y,
|
36
|
+
'm' => m,
|
37
|
+
'd' => d,
|
38
|
+
'start' => hhmm,
|
39
|
+
'hours' => hours
|
40
|
+
}
|
41
|
+
@db.alloc!(plan) rescue nil
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
if __FILE__ == $0
|
46
|
+
app = ShiftStore.new
|
47
|
+
|
48
|
+
seki = app.add_employee('関将俊') rescue app.employee_by_name('関将俊').first['uid']
|
49
|
+
ikezawa = app.add_employee('池澤一廣') rescue app.employee_by_name('池澤一廣').first['uid']
|
50
|
+
pp app.all_employee
|
51
|
+
seki = app.employee_by_name('関将俊').first['uid']
|
52
|
+
pp app.db[seki]
|
53
|
+
app.db.update(seki, {'店' => %w(矢板 西那須野 大田原)})
|
54
|
+
pp app.db[seki]
|
55
|
+
pp app.employee_by_name('関将俊')
|
56
|
+
pp app.employee_by_name('池澤一廣')
|
57
|
+
app.db.merge(ikezawa, {'店' => %w(矢板)})
|
58
|
+
miwa = app.add_employee('深谷美和') rescue nil
|
59
|
+
if miwa
|
60
|
+
app.db.merge(miwa, {'店' => %w(大田原 矢板)})
|
61
|
+
end
|
62
|
+
puts '矢板'
|
63
|
+
pp app.employee_by_store('矢板').map{|x| x['obj']}
|
64
|
+
puts '大田原'
|
65
|
+
pp app.employee_by_store('大田原').map{|x| x['obj']}
|
66
|
+
puts '西那須野'
|
67
|
+
pp app.employee_by_store('西那須野').map{|x| x['obj']}
|
68
|
+
|
69
|
+
|
70
|
+
s = Date.parse('2021-01-01')
|
71
|
+
(s ... (s >> 2)).each do |d|
|
72
|
+
app.add_plan(ikezawa, '矢板', d.year, d.month, d.day, '8:00', 4)
|
73
|
+
app.add_plan(ikezawa, '矢板', d.year, d.month, d.day, '13:00', 4)
|
74
|
+
app.add_plan(miwa, '大田原', d.year, d.month, d.day, '13:00', 4)
|
75
|
+
app.add_plan(seki, '大田原', d.year, d.month, d.day, '8:00', 4)
|
76
|
+
app.add_plan(seki, '西那須野', d.year, d.month, d.day, '13:00', 4)
|
77
|
+
end
|
78
|
+
|
79
|
+
employee = Hash.new do |h, k|
|
80
|
+
h[k] = app.db[k].dig("name") rescue nil
|
81
|
+
end
|
82
|
+
pp app.db.search({"type" => "予定", "店" => "大田原", "y" => 2021, "m" => 2}).map {|x|
|
83
|
+
it = x['obj']
|
84
|
+
[employee[it['従業員']]] + it.values_at(* %w(y m d start hours))
|
85
|
+
}
|
86
|
+
|
87
|
+
pp app.db.search({"type" => "予定", "店" => "矢板", "y" => 2021, "m" => 2}).map {|x|
|
88
|
+
it = x['obj']
|
89
|
+
[employee[it['従業員']]] + it.values_at(* %w(y m d start hours))
|
90
|
+
}
|
91
|
+
end
|
92
|
+
|
data/yose.gemspec
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require_relative 'lib/yose/version'
|
2
|
+
|
3
|
+
Gem::Specification.new do |spec|
|
4
|
+
spec.name = "yose"
|
5
|
+
spec.version = Yose::VERSION
|
6
|
+
spec.authors = ["Masatoshi SEKI"]
|
7
|
+
spec.email = ["seki@ruby-lang.org"]
|
8
|
+
|
9
|
+
spec.summary = %q{simple json storage using psql, jsonb.}
|
10
|
+
spec.description = %q{simple json storage using psql, jsonb.}
|
11
|
+
spec.homepage = "https://github.com/seki/Yose"
|
12
|
+
spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
|
13
|
+
|
14
|
+
# spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
|
15
|
+
|
16
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
17
|
+
spec.metadata["source_code_uri"] = "https://github.com/seki/Yose"
|
18
|
+
# spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
|
19
|
+
|
20
|
+
# Specify which files should be added to the gem when it is released.
|
21
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
22
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
23
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
24
|
+
end
|
25
|
+
spec.bindir = "exe"
|
26
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
27
|
+
spec.require_paths = ["lib"]
|
28
|
+
|
29
|
+
spec.add_dependency "pg"
|
30
|
+
end
|
metadata
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: yose
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Masatoshi SEKI
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2021-02-18 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: pg
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
description: simple json storage using psql, jsonb.
|
28
|
+
email:
|
29
|
+
- seki@ruby-lang.org
|
30
|
+
executables: []
|
31
|
+
extensions: []
|
32
|
+
extra_rdoc_files: []
|
33
|
+
files:
|
34
|
+
- ".gitignore"
|
35
|
+
- Gemfile
|
36
|
+
- LICENSE
|
37
|
+
- README.md
|
38
|
+
- Rakefile
|
39
|
+
- bin/console
|
40
|
+
- bin/setup
|
41
|
+
- lib/yose.rb
|
42
|
+
- lib/yose/version.rb
|
43
|
+
- sample/shift_table.rb
|
44
|
+
- yose.gemspec
|
45
|
+
homepage: https://github.com/seki/Yose
|
46
|
+
licenses: []
|
47
|
+
metadata:
|
48
|
+
homepage_uri: https://github.com/seki/Yose
|
49
|
+
source_code_uri: https://github.com/seki/Yose
|
50
|
+
post_install_message:
|
51
|
+
rdoc_options: []
|
52
|
+
require_paths:
|
53
|
+
- lib
|
54
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
55
|
+
requirements:
|
56
|
+
- - ">="
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
version: 2.3.0
|
59
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: '0'
|
64
|
+
requirements: []
|
65
|
+
rubygems_version: 3.1.2
|
66
|
+
signing_key:
|
67
|
+
specification_version: 4
|
68
|
+
summary: simple json storage using psql, jsonb.
|
69
|
+
test_files: []
|