ordlite 0.1.2 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1 -1
- data/Manifest.txt +5 -0
- data/README.md +125 -54
- data/Rakefile +3 -1
- data/lib/ordlite/base.rb +45 -5
- data/lib/ordlite/cache.rb +46 -105
- data/lib/ordlite/factory.rb +132 -0
- data/lib/ordlite/importer.rb +218 -0
- data/lib/ordlite/models/blob.rb +4 -0
- data/lib/ordlite/models/collection.rb +14 -0
- data/lib/ordlite/models/factory.rb +25 -0
- data/lib/ordlite/models/forward.rb +22 -3
- data/lib/ordlite/models/generative.rb +12 -0
- data/lib/ordlite/models/inscribe.rb +232 -5
- data/lib/ordlite/schema.rb +105 -2
- data/lib/ordlite/version.rb +2 -2
- data/lib/ordlite.rb +12 -2
- metadata +36 -3
@@ -0,0 +1,218 @@
|
|
1
|
+
module OrdDb
|
2
|
+
|
3
|
+
class Importer
|
4
|
+
Inscribe = Model::Inscribe
|
5
|
+
Blob = Model::Blob
|
6
|
+
Collection = Model::Collection
|
7
|
+
|
8
|
+
|
9
|
+
|
10
|
+
def import_collection_csv( path,
|
11
|
+
name:,
|
12
|
+
content: true )
|
13
|
+
## or use
|
14
|
+
## import_collection( format: 'csv') - why? why not?
|
15
|
+
recs = read_csv( path )
|
16
|
+
puts " #{recs.size} inscribe id(s)"
|
17
|
+
|
18
|
+
col = Collection.find_by( name: name )
|
19
|
+
if col && col.items.count > 0
|
20
|
+
puts "!! WARN - collection already in db; delete first to reimport"
|
21
|
+
return
|
22
|
+
elsif col
|
23
|
+
## do nothing; (re)use collection record; add items
|
24
|
+
else
|
25
|
+
col = Collection.create(
|
26
|
+
name: name
|
27
|
+
## max: recs.size ## auto-add max - why? why not?
|
28
|
+
)
|
29
|
+
end
|
30
|
+
|
31
|
+
recs.each_with_index do |rec,i|
|
32
|
+
id = rec['id']
|
33
|
+
name = rec['name'] || rec['title']
|
34
|
+
puts "==> #{i+1}/#{recs.size} >#{name}< @ #{id}..."
|
35
|
+
|
36
|
+
col.items.create( pos: i,
|
37
|
+
inscribe_id: id,
|
38
|
+
name: name )
|
39
|
+
|
40
|
+
_import( id, content: content )
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
def import_collection_inscriptions( path,
|
46
|
+
name:,
|
47
|
+
content: true )
|
48
|
+
recs = read_json( path )
|
49
|
+
puts " #{recs.size} inscribe id(s)"
|
50
|
+
|
51
|
+
col = Collection.find_by( name: name )
|
52
|
+
if col && col.items.count > 0
|
53
|
+
puts "!! WARN - collection already in db; delete first to reimport"
|
54
|
+
return
|
55
|
+
elsif col
|
56
|
+
## do nothing; (re)use collection record; add items
|
57
|
+
else
|
58
|
+
col = Model::Collection.create(
|
59
|
+
name: name
|
60
|
+
## max: recs.size ## auto-add max - why? why not?
|
61
|
+
)
|
62
|
+
end
|
63
|
+
|
64
|
+
recs.each_with_index do |rec,i|
|
65
|
+
id = rec['id']
|
66
|
+
meta = rec['meta']
|
67
|
+
name = meta['name']
|
68
|
+
puts "==> #{i+1}/#{recs.size} >#{name}< @ #{id}..."
|
69
|
+
|
70
|
+
col.items.create( pos: i,
|
71
|
+
inscribe_id: id,
|
72
|
+
name: name )
|
73
|
+
|
74
|
+
_import( id, content: content )
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
|
79
|
+
def import_collection( path, content: true )
|
80
|
+
data = read_json( path )
|
81
|
+
|
82
|
+
meta = data['collection']
|
83
|
+
pp meta
|
84
|
+
|
85
|
+
name = meta['name']
|
86
|
+
|
87
|
+
col = Collection.find_by( name: name )
|
88
|
+
if col
|
89
|
+
puts "!! WARN - collection already in db; delete first to reimport"
|
90
|
+
return
|
91
|
+
end
|
92
|
+
|
93
|
+
col = Collection.create(
|
94
|
+
name: name,
|
95
|
+
desc: meta['description'],
|
96
|
+
max: meta['max_supply']
|
97
|
+
)
|
98
|
+
|
99
|
+
items = data['items']
|
100
|
+
puts " #{items.size} inscribe id(s)"
|
101
|
+
items.each_with_index do |rec,i|
|
102
|
+
id = rec['inscription_id']
|
103
|
+
name = rec['name']
|
104
|
+
puts "==> #{i+1}/#{items.size} @ #{id}..."
|
105
|
+
|
106
|
+
col.items.create( pos: i,
|
107
|
+
inscribe_id: id,
|
108
|
+
name: name )
|
109
|
+
|
110
|
+
_import( id, content: content )
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
|
115
|
+
def import_csv( path, content: true )
|
116
|
+
recs = read_csv( path )
|
117
|
+
puts " #{recs.size} inscribe id(s)"
|
118
|
+
#=> 1000 inscribe id(s)
|
119
|
+
|
120
|
+
recs.each_with_index do |rec,i|
|
121
|
+
id = rec['id']
|
122
|
+
puts "==> #{i+1}/#{rec.size} @ #{id}..."
|
123
|
+
|
124
|
+
_import( id, content: content )
|
125
|
+
end
|
126
|
+
end # method import_csv
|
127
|
+
|
128
|
+
def import( id_or_ids, content: true )
|
129
|
+
if id_or_ids.is_a?( String )
|
130
|
+
id = id_or_ids
|
131
|
+
_import( id, content: content )
|
132
|
+
else ## assume array
|
133
|
+
ids = id_or_ids
|
134
|
+
ids.each do |id|
|
135
|
+
_import( id, content: content )
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def _import( id, content: true )
|
141
|
+
## check if inscription / inscribe is already in db?
|
142
|
+
inscribe = Inscribe.find_by( id: id )
|
143
|
+
if inscribe ## already in db; dump record
|
144
|
+
## pp inscribe
|
145
|
+
else ## fetch via ordinals.com api and update db
|
146
|
+
data = Ordinals.inscription( id )
|
147
|
+
|
148
|
+
pp data
|
149
|
+
Inscribe.create_from_api( data )
|
150
|
+
end
|
151
|
+
|
152
|
+
if content
|
153
|
+
## check if (content) blob is already in db?
|
154
|
+
blob = Blob.find_by( id: id )
|
155
|
+
if blob ## already in db; do nothing
|
156
|
+
else ## fetch via ordinals.com api and update db
|
157
|
+
content = Ordinals.content( id )
|
158
|
+
|
159
|
+
puts " content-type: #{content.type}"
|
160
|
+
puts " content-length: #{content.length}"
|
161
|
+
|
162
|
+
Blob.create( id: id, content: content.data )
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
end # class Importer
|
168
|
+
|
169
|
+
|
170
|
+
|
171
|
+
###
|
172
|
+
## convenience helpers
|
173
|
+
|
174
|
+
def self.importer ## "default" importer
|
175
|
+
@importer ||= Importer.new
|
176
|
+
end
|
177
|
+
|
178
|
+
def self.import( id_or_ids, content: true )
|
179
|
+
importer.import( id_or_ids, content: content )
|
180
|
+
end
|
181
|
+
|
182
|
+
|
183
|
+
def self.import_csv( path, content: true )
|
184
|
+
importer.import_csv( path, content: content )
|
185
|
+
end
|
186
|
+
|
187
|
+
|
188
|
+
def self.import_collection( path, content: true )
|
189
|
+
importer.import_collection( path, content: content )
|
190
|
+
end
|
191
|
+
|
192
|
+
def self.import_collection_inscriptions( path,
|
193
|
+
name:,
|
194
|
+
content: true )
|
195
|
+
importer.import_collection_inscriptions( path,
|
196
|
+
name: name,
|
197
|
+
content: content )
|
198
|
+
end
|
199
|
+
|
200
|
+
def self.import_collection_csv( path,
|
201
|
+
name:,
|
202
|
+
content: true )
|
203
|
+
importer.import_collection_csv( path,
|
204
|
+
name: name,
|
205
|
+
content: content )
|
206
|
+
end
|
207
|
+
|
208
|
+
|
209
|
+
module Model
|
210
|
+
class Inscribe
|
211
|
+
def self.import( id_or_ids, content: true )
|
212
|
+
OrdDb.importer.import( id_or_ids, content: content )
|
213
|
+
end
|
214
|
+
end # class Inscribe
|
215
|
+
end # module Model
|
216
|
+
|
217
|
+
|
218
|
+
end # module OrdDb
|
data/lib/ordlite/models/blob.rb
CHANGED
@@ -0,0 +1,14 @@
|
|
1
|
+
module OrdDb
|
2
|
+
module Model
|
3
|
+
|
4
|
+
class Collection < ActiveRecord::Base
|
5
|
+
has_many :items
|
6
|
+
## -> { order('pos') }
|
7
|
+
## note: default_scope (order)
|
8
|
+
## will break all count queries and more
|
9
|
+
## thus - no "magic" - always sort if pos order required!!!
|
10
|
+
has_many :inscribes, :through => :items
|
11
|
+
end # class Collection
|
12
|
+
|
13
|
+
end # module Model
|
14
|
+
end # module OrdDb
|
@@ -0,0 +1,25 @@
|
|
1
|
+
|
2
|
+
module OrdDb
|
3
|
+
module Model
|
4
|
+
|
5
|
+
class Factory < ActiveRecord::Base
|
6
|
+
self.table_name = 'factories' ## non-standard plural (factory/factories)
|
7
|
+
|
8
|
+
belongs_to :inscribe
|
9
|
+
|
10
|
+
has_many :inscriberefs ## join table (use habtm - why? why not?)
|
11
|
+
## -> { order('pos') }
|
12
|
+
## note: default_scope (order)
|
13
|
+
## will break all count queries and more
|
14
|
+
## thus - no "magic" - always sort if pos order required!!!
|
15
|
+
|
16
|
+
has_many :layers, :through => :inscriberefs,
|
17
|
+
:source => :inscribe
|
18
|
+
|
19
|
+
has_many :generatives
|
20
|
+
has_many :inscribes, :through => :generatives
|
21
|
+
end # class Factory
|
22
|
+
|
23
|
+
end # module Model
|
24
|
+
end # module OrdDb
|
25
|
+
|
@@ -12,12 +12,31 @@ Prop = ConfDb::Model::Prop
|
|
12
12
|
|
13
13
|
class Inscribe < ActiveRecord::Base ; end
|
14
14
|
class Blob < ActiveRecord::Base ; end
|
15
|
-
|
16
|
-
|
15
|
+
class Collection < ActiveRecord::Base ; end
|
16
|
+
class Item < ActiveRecord::Base ## change to CollectionItem - why? why not?
|
17
|
+
belongs_to :collection
|
18
|
+
belongs_to :inscribe
|
19
|
+
end
|
20
|
+
|
21
|
+
class Factory < ActiveRecord::Base ; end
|
22
|
+
class Generative < ActiveRecord::Base ; end
|
23
|
+
|
24
|
+
### join tables - add inline here - why? why not?
|
25
|
+
## rename to CollectionLayer? or
|
26
|
+
## CollectionInscribeRef? or
|
27
|
+
## Layerref? or
|
28
|
+
## InscribeRef?
|
29
|
+
## FactoryItem ????
|
30
|
+
class Inscriberef < ActiveRecord::Base
|
31
|
+
belongs_to :factory
|
32
|
+
belongs_to :inscribe
|
33
|
+
end
|
34
|
+
|
35
|
+
end # module Model
|
17
36
|
|
18
37
|
# note: convenience alias for Model
|
19
38
|
# lets you use include OrdDb::Models
|
20
39
|
Models = Model
|
21
|
-
end # module
|
40
|
+
end # module OrdDb
|
22
41
|
|
23
42
|
|
@@ -4,12 +4,17 @@ module OrdDb
|
|
4
4
|
|
5
5
|
class Inscribe < ActiveRecord::Base
|
6
6
|
has_one :blob, foreign_key: 'id'
|
7
|
+
|
8
|
+
has_one :factory ## optional (auto-added via og/orc-721 deploy)
|
9
|
+
has_one :generative, foreign_key: 'id' ## optional (auto-added via og/orc-721 deploy)
|
10
|
+
|
11
|
+
## convernience helper
|
12
|
+
## forward to blob.content
|
13
|
+
## blob.content - encoding is BINARY (ASCII-7BIT)
|
14
|
+
## blob.text - force_encoding is UTF-8 (return a copy)
|
15
|
+
def content() blob.content; end
|
16
|
+
def text() blob.text; end
|
7
17
|
|
8
|
-
def content
|
9
|
-
## convernience helper
|
10
|
-
## forward to blob.content
|
11
|
-
blob.content
|
12
|
-
end
|
13
18
|
|
14
19
|
################################
|
15
20
|
### scope like helpers
|
@@ -54,6 +59,228 @@ SQL
|
|
54
59
|
|
55
60
|
joins(:blob).where( where_clause ).order( 'num' )
|
56
61
|
end
|
62
|
+
|
63
|
+
def self.sub1k() where( 'num < 1000' ); end
|
64
|
+
def self.sub2k() where( 'num < 2000' ); end
|
65
|
+
def self.sub10k() where( 'num < 10000' ); end
|
66
|
+
def self.sub20k() where( 'num < 20000' ); end
|
67
|
+
def self.sub100k() where( 'num < 100000' ); end
|
68
|
+
def self.sub1m() where( 'num < 1000000' ); end
|
69
|
+
def self.sub2m() where( 'num < 2000000' ); end
|
70
|
+
def self.sub10m() where( 'num < 10000000' ); end
|
71
|
+
def self.sub20m() where( 'num < 20000000' ); end
|
72
|
+
def self.sub21m() where( 'num < 21000000' ); end
|
73
|
+
|
74
|
+
|
75
|
+
def self.largest
|
76
|
+
order( 'bytes DESC' )
|
77
|
+
end
|
78
|
+
|
79
|
+
def self.address_counts
|
80
|
+
group( 'address' )
|
81
|
+
.order( Arel.sql( 'COUNT(*) DESC')).count
|
82
|
+
end
|
83
|
+
|
84
|
+
def self.block_counts
|
85
|
+
group( 'block' )
|
86
|
+
.order( 'block').count
|
87
|
+
end
|
88
|
+
|
89
|
+
def self.block_with_timestamp_counts
|
90
|
+
group( Arel.sql( "block || ' @ ' || date" ))
|
91
|
+
.order( Arel.sql( "block || ' @ ' || date" ) ).count
|
92
|
+
end
|
93
|
+
|
94
|
+
def self.content_type_counts
|
95
|
+
group( 'content_type' )
|
96
|
+
.order( Arel.sql( 'COUNT(*) DESC, content_type')).count
|
97
|
+
end
|
98
|
+
|
99
|
+
|
100
|
+
def self.date_counts
|
101
|
+
## note: strftime is SQLite specific/only!!!
|
102
|
+
group( Arel.sql("strftime('%Y-%m-%d', date)"))
|
103
|
+
.order( Arel.sql("strftime('%Y-%m-%d', date)")).count
|
104
|
+
end
|
105
|
+
|
106
|
+
def self.month_counts
|
107
|
+
## note: strftime is SQLite specific/only!!!
|
108
|
+
group( Arel.sql("strftime('%Y-%m', date)"))
|
109
|
+
.order( Arel.sql("strftime('%Y-%m', date)")).count
|
110
|
+
end
|
111
|
+
|
112
|
+
def self.hour_counts
|
113
|
+
## note: strftime is SQLite specific/only!!!
|
114
|
+
group( Arel.sql("strftime('%Y-%m-%d %Hh', date)"))
|
115
|
+
.order( Arel.sql("strftime('%Y-%m-%d %Hh', date)")).count
|
116
|
+
end
|
117
|
+
|
118
|
+
|
119
|
+
class << self
|
120
|
+
alias_method :biggest, :largest
|
121
|
+
alias_method :counts_by_address, :address_counts
|
122
|
+
alias_method :counts_by_content_type, :content_type_counts
|
123
|
+
alias_method :counts_by_date, :date_counts
|
124
|
+
alias_method :counts_by_day, :date_counts
|
125
|
+
alias_method :counts_by_month, :month_counts
|
126
|
+
alias_method :counts_by_hour, :hour_counts
|
127
|
+
alias_method :counts_by_block, :block_counts
|
128
|
+
alias_method :counts_by_block_with_timestamp, :block_with_timestamp_counts
|
129
|
+
end
|
130
|
+
|
131
|
+
|
132
|
+
def self.text
|
133
|
+
## note: for now include:
|
134
|
+
## - text/plain (all variants)
|
135
|
+
## - text/json (all variants)
|
136
|
+
## - text/markdown
|
137
|
+
where( content_type:
|
138
|
+
['text/plain',
|
139
|
+
'text/plain;charset=utf-8',
|
140
|
+
'text/markdown',
|
141
|
+
'application/json',
|
142
|
+
]
|
143
|
+
)
|
144
|
+
end
|
145
|
+
def self.png() where( content_type: 'image/png' ); end
|
146
|
+
|
147
|
+
###
|
148
|
+
## add support for ordinals.com api txt (headers format)
|
149
|
+
|
150
|
+
|
151
|
+
def self.create_from_api( data ) create( _parse_api( data )); end
|
152
|
+
class << self
|
153
|
+
alias_method :create_from_cache, :create_from_api ## add alias - why? why not?
|
154
|
+
end
|
155
|
+
|
156
|
+
|
157
|
+
def self._parse_api( data ) ## parse api json data
|
158
|
+
## num via title
|
159
|
+
attributes = {
|
160
|
+
id: data['id'],
|
161
|
+
num: _title_to_num( data['title'] ),
|
162
|
+
bytes: _content_length_to_bytes( data['content-length'] ),
|
163
|
+
sat: data['sat'].to_i(10),
|
164
|
+
content_type: data['content-type'],
|
165
|
+
block: data['genesis-height'].to_i(10),
|
166
|
+
fee: data['genesis-fee'].to_i(10),
|
167
|
+
tx: data['genesis-transaction'],
|
168
|
+
address: data['address'],
|
169
|
+
output: data['output'],
|
170
|
+
value: data['output-value'].to_i(10),
|
171
|
+
offset: data['offset'].to_i(10),
|
172
|
+
# "2023-06-01 05:00:57 UTC"
|
173
|
+
date: DateTime.strptime( data['timestamp'],
|
174
|
+
'%Y-%m-%d %H:%M:%S %z')
|
175
|
+
}
|
176
|
+
|
177
|
+
attributes
|
178
|
+
end
|
179
|
+
|
180
|
+
|
181
|
+
## "title": "Inscription 9992615",
|
182
|
+
TITLE_RX = /^Inscription (?<num>[0-9]+)$/i
|
183
|
+
|
184
|
+
def self._title_to_num( str )
|
185
|
+
if m=TITLE_RX.match( str )
|
186
|
+
m[:num].to_i(10) ## use base 10
|
187
|
+
else
|
188
|
+
puts "!! ERROR - no inscribe num found in title >#{str}<"
|
189
|
+
exit 1 ## not found - raise exception - why? why not?
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
CONTENT_LENGTH_RX = /^(?<num>[0-9]+) bytes$/i
|
194
|
+
|
195
|
+
def self._content_length_to_bytes( str )
|
196
|
+
if m=CONTENT_LENGTH_RX.match( str )
|
197
|
+
m[:num].to_i(10) ## use base 10
|
198
|
+
else
|
199
|
+
puts "!! ERROR - bytes found in content lenght >#{str}<"
|
200
|
+
exit 1 ## not found - raise exception - why? why not?
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
|
205
|
+
|
206
|
+
###
|
207
|
+
# instance methods
|
208
|
+
def extname
|
209
|
+
## map mime type to file extname
|
210
|
+
## see https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types
|
211
|
+
## for real-world usage, see https://dune.com/dgtl_assets/bitcoin-ordinals-analysis
|
212
|
+
## https://github.com/casey/ord/blob/master/src/media.rs
|
213
|
+
|
214
|
+
if content_type.start_with?( 'text/plain' )
|
215
|
+
'.txt'
|
216
|
+
elsif content_type.start_with?( 'text/markdown' )
|
217
|
+
'.md'
|
218
|
+
elsif content_type.start_with?( 'text/html' )
|
219
|
+
'.html'
|
220
|
+
elsif content_type.start_with?( 'text/javascript' ) ||
|
221
|
+
content_type.start_with?( 'application/javascript' )
|
222
|
+
## note: application/javascript is considered bad practice/legacy
|
223
|
+
'.js'
|
224
|
+
elsif content_type.start_with?( 'image/png' )
|
225
|
+
## Portable Network Graphics (PNG)
|
226
|
+
'.png'
|
227
|
+
elsif content_type.start_with?( 'image/jpeg' )
|
228
|
+
## Joint Photographic Expert Group image (JPEG)
|
229
|
+
'.jpg' ## use jpeg - why? why not?
|
230
|
+
elsif content_type.start_with?( 'image/webp' )
|
231
|
+
## Web Picture format (WEBP)
|
232
|
+
'.webp' ## note: no three-letter extension available
|
233
|
+
elsif content_type.start_with?( 'image/svg' )
|
234
|
+
## Scalable Vector Graphics (SVG)
|
235
|
+
'.svg'
|
236
|
+
elsif content_type.start_with?( 'image/gif' )
|
237
|
+
## Graphics Interchange Format (GIF)
|
238
|
+
'.gif'
|
239
|
+
elsif content_type.start_with?( 'image/avif' )
|
240
|
+
## AV1 Image File Format (AVIF)
|
241
|
+
'.avif'
|
242
|
+
elsif content_type.start_with?( 'application/epub' )
|
243
|
+
'.epub'
|
244
|
+
elsif content_type.start_with?( 'application/pdf' )
|
245
|
+
'.pdf'
|
246
|
+
elsif content_type.start_with?( 'application/json' )
|
247
|
+
'.json'
|
248
|
+
elsif content_type.start_with?( 'application/pgp-signature' )
|
249
|
+
'.sig'
|
250
|
+
elsif content_type.start_with?( 'audio/mpeg' )
|
251
|
+
'.mp3'
|
252
|
+
elsif content_type.start_with?( 'audio/midi' )
|
253
|
+
'.midi'
|
254
|
+
elsif content_type.start_with?( 'video/mp4' )
|
255
|
+
'.mp4'
|
256
|
+
elsif content_type.start_with?( 'video/webm' )
|
257
|
+
'.wepm'
|
258
|
+
elsif content_type.start_with?( 'audio/mod' )
|
259
|
+
## is typo? possible? only one inscription in 20m?
|
260
|
+
'.mod' ## check/todo/fix if is .wav??
|
261
|
+
else
|
262
|
+
puts "!! ERROR - no file extension configured for content type >#{content_type}<; sorry:"
|
263
|
+
pp self
|
264
|
+
exit 1
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
def export_path ## default export path
|
269
|
+
numstr = "%08d" % num ### e.g. 00000001
|
270
|
+
"./tmp/#{numstr}#{extname}"
|
271
|
+
end
|
272
|
+
def export( path=export_path )
|
273
|
+
if blob
|
274
|
+
write_blob( path, blob.content )
|
275
|
+
else
|
276
|
+
## todo/fix: raise exception - no content
|
277
|
+
puts "!! ERROR - inscribe has no content (blob); sorry:"
|
278
|
+
pp self
|
279
|
+
exit 1
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
|
57
284
|
end # class Inscribe
|
58
285
|
|
59
286
|
end # module Model
|
data/lib/ordlite/schema.rb
CHANGED
@@ -87,7 +87,7 @@ create_table :inscribes, :id => :string do |t|
|
|
87
87
|
## what is location ???
|
88
88
|
## "location": "0a3a4dbf6630338bc4df8e36bd081f8f7d2dee9441131cb03a18d43eb4882d5c:0:0",
|
89
89
|
|
90
|
-
## timestamp
|
90
|
+
## timestamp last
|
91
91
|
t.timestamps
|
92
92
|
end
|
93
93
|
|
@@ -99,14 +99,117 @@ create_table :blobs, :id => :string do |t|
|
|
99
99
|
## t.string :id, null: false, index: { unique: true, name: 'blob_uuids' }
|
100
100
|
|
101
101
|
t.binary :content, null: false
|
102
|
+
t.string :sha256 ## sha256 hash
|
103
|
+
t.string :md5 ## md5 hash - add why? why not?
|
102
104
|
|
103
|
-
## timestamp
|
105
|
+
## timestamp last
|
104
106
|
t.timestamps
|
105
107
|
end
|
106
108
|
|
107
109
|
|
110
|
+
=begin
|
111
|
+
"name": "Planetary Ordinals",
|
112
|
+
"inscription_icon": "98da33abe2045ec1421fcf1bc376dea5beb17ded15aa70ca5da490f50d95a6d9i0",
|
113
|
+
"supply": "69",
|
114
|
+
"slug": "planetary-ordinals",
|
115
|
+
"description": "",
|
116
|
+
"twitter_link": "https://twitter.com/ordinalswallet",
|
117
|
+
"discord_link": "https://discord.com/invite/ordinalswallet",
|
118
|
+
"website_link": ""
|
119
|
+
=end
|
120
|
+
|
121
|
+
create_table :collections do |t|
|
122
|
+
t.string :name, null: false
|
123
|
+
t.string :slug
|
124
|
+
t.text :desc # description
|
125
|
+
t.integer :max # supply
|
126
|
+
t.string :icon_id ## rename to inscribe_icon_id or such - why? why not?
|
127
|
+
## add twitter_link, discord_link, website_link - why? why not?
|
128
|
+
|
129
|
+
## if on-chain and metadata inscribed - add why? why not??
|
130
|
+
## t.string :source_id, null: false ## foreign key reference
|
131
|
+
|
132
|
+
## timestamp last
|
133
|
+
t.timestamps
|
134
|
+
end
|
135
|
+
|
136
|
+
create_table :items do |t|
|
137
|
+
t.integer :collection_id, null: false
|
138
|
+
t.string :inscribe_id, null: false
|
139
|
+
t.integer :pos, null: false
|
140
|
+
t.string :name
|
141
|
+
|
142
|
+
## timestamp last
|
143
|
+
t.timestamps
|
144
|
+
|
145
|
+
## todo/fix: add unique index for :pos+:collection_id !!!
|
146
|
+
end
|
147
|
+
|
148
|
+
|
149
|
+
|
150
|
+
###
|
151
|
+
# generative (collection) factory
|
152
|
+
create_table :factories, :id => :string do |t|
|
153
|
+
t.string :name
|
154
|
+
t.integer :max # max limit
|
155
|
+
t.integer :maxblock # max block limit
|
156
|
+
t.string :dim # dimension e.g. 24x24 (in px)
|
157
|
+
|
158
|
+
t.string :inscribe_id, null: false ## foreign key reference
|
159
|
+
|
160
|
+
## timestamp last
|
161
|
+
t.timestamps
|
162
|
+
end
|
163
|
+
|
164
|
+
#####
|
165
|
+
## join table (factory has_many modules)
|
166
|
+
## rename to layer / sprites / blocks / tiles / modules / submodules / subs / mods / ...etc - why? why not?
|
167
|
+
## layerlists or inscribelists or ???
|
168
|
+
## change/rename to factory_items or layer_items or such?
|
169
|
+
create_table :inscriberefs, :id => false do |t|
|
170
|
+
t.string :factory_id, null: false
|
171
|
+
t.string :inscribe_id, null: false
|
172
|
+
t.integer :pos, null: false ## position (index) in list (starting at 0)
|
173
|
+
## todo/fix: make factory_id + inscribe_id + pos unique index - why? why not?
|
174
|
+
|
175
|
+
## timestamp last
|
176
|
+
t.timestamps
|
177
|
+
end
|
178
|
+
|
179
|
+
|
180
|
+
create_table :generatives, :id => :string do |t|
|
181
|
+
t.string :factory_id, null: false
|
182
|
+
t.string :g, null: false ## use space separated numbers - why? why not?
|
183
|
+
t.binary :content ### optional for now - why? why not?
|
184
|
+
|
185
|
+
## timestamp last
|
186
|
+
t.timestamps
|
187
|
+
end
|
188
|
+
|
189
|
+
|
190
|
+
|
191
|
+
|
108
192
|
end # block Schema.define
|
109
193
|
|
110
194
|
end # method up
|
111
195
|
end # class CreateDb
|
196
|
+
|
197
|
+
###
|
198
|
+
# migrations helpers
|
199
|
+
class AddGeneratives
|
200
|
+
|
201
|
+
def up
|
202
|
+
ActiveRecord::Schema.define do
|
203
|
+
create_table :generatives, :id => :string do |t|
|
204
|
+
t.string :factory_id, null: false
|
205
|
+
t.string :g, null: false ## use space separated numbers - why? why not?
|
206
|
+
t.binary :content ### optional for now - why? why not?
|
207
|
+
|
208
|
+
## timestamp last
|
209
|
+
t.timestamps
|
210
|
+
end
|
211
|
+
end # block Schema.define
|
212
|
+
end # method up
|
213
|
+
end # class AddGeneratives
|
214
|
+
|
112
215
|
end # module OrdDb
|
data/lib/ordlite/version.rb
CHANGED