ordlite 0.1.2 → 0.2.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 +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