ordinals 1.1.1 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 586a022b2167110472eaea7be6d88e4f21fe72ea1f05ed6465ce29913a10cc15
4
- data.tar.gz: fe6f05e73dfdfd42560607c0721b0bdf642718debc83f48e0c36ba2a0ac82db2
3
+ metadata.gz: 4dbc3bf008b7d878df205f804f9a30887c0602afc3e16ccb64ed3749be911d82
4
+ data.tar.gz: a5562501103ac3be5d475216c7c1ae45c5f4c1e519105dcdd8adf1250fe5184a
5
5
  SHA512:
6
- metadata.gz: 0b7550126e529e67d821042f6fa5d2092f8e93460d4b6e892fe9d21e7bd4e7e5f83673beb39557dafbb1617eb7527569c2b4347e57cf6ce61090e1c00979d335
7
- data.tar.gz: 452fc65024f99d80bddf2254e2177a4397b816525d43cf8e42c7e914b95706c4f1a5b4086e5a2e2135792cfe895edcd9dca9e39190c95c74c63cee1550a0da41
6
+ metadata.gz: 0140e9330cff62dc983f91a5a851cbfc3d188d3711537304e538d3ea8664d7cfebc76d9f19596db60f51b9d23f117f18ec44e9fcebb5dfdf6e72131a8b87b8f6
7
+ data.tar.gz: 7605cf138cf69f8e7b1cf3f8e7e06c837623e1720c558b85f345bb02eb8da07b48c4882a8822ec6e7c9992756a8cb1d04089a27164e71374a26e1f3784e07943
data/CHANGELOG.md CHANGED
@@ -1,4 +1,4 @@
1
- ### 1.1.1
1
+ ### 1.2.0
2
2
 
3
3
  ### 0.0.1 / 2023-03-23
4
4
 
data/Manifest.txt CHANGED
@@ -4,7 +4,10 @@ README.md
4
4
  Rakefile
5
5
  lib/ordinals.rb
6
6
  lib/ordinals/api.rb
7
+ lib/ordinals/cache.rb
7
8
  lib/ordinals/recursive/composite.rb
8
9
  lib/ordinals/recursive/generator.rb
9
10
  lib/ordinals/recursive/image.rb
11
+ lib/ordinals/sandbox.rb
10
12
  lib/ordinals/stats.rb
13
+ lib/ordinals/version.rb
data/README.md CHANGED
@@ -13,12 +13,15 @@ ordinals gem - ordinals (inscription) api wrapper & helpers for Bitcoin, Litcoin
13
13
 
14
14
 
15
15
  For now ordinal inscription api queries are supported on
16
- - Bitcoin (via <https://ordinals.com>)
17
- - Litecoin (via <https://ordinalslite.com>) and
18
- - Dogecoin (via <https://doginals.com>)
16
+ - Bitcoin (via <https://ordinals.com>) and
17
+ - Litecoin (via <https://ordinalslite.com>)
19
18
 
19
+ <!--
20
+ chekc if doginals still in operation???
21
+ - Dogecoin (via <https://doginals.com>)
22
+ -->
20
23
 
21
- Let's start querying for bitcoin (btc) ordinal inscriptions:
24
+ Let's start querying for bitcoin (btc) ordinal inscriptions by id:
22
25
 
23
26
  ``` ruby
24
27
  require 'ordinals'
@@ -44,20 +47,50 @@ pp data
44
47
  # "address"=>"bc1pyncs638hxfhaqjvnedw2lyc2jta6zuvr9erj6m9cwlghp6qqwvhqeq574l",
45
48
  # "output value"=>"9667",
46
49
  # "sat"=>"1391087135005306",
47
- # "preview"=>"link",
48
- # "content"=>"link",
49
- # "content length"=>"3072 bytes",
50
- # "content type"=>"image/png",
50
+ # "content-length"=>"3072 bytes",
51
+ # "content-type"=>"image/png",
51
52
  # "timestamp"=>"2023-02-18 03:02:18 UTC",
52
- # "genesis height"=>"777099",
53
- # "genesis fee"=>"1814",
54
- # "genesis transaction"=>"c41021cb11dce003e6a20a3420cf5954a1d104a1fe314393b915a62f020dcd0a",
53
+ # "genesis-height"=>"777099",
54
+ # "genesis-fee"=>"1814",
55
+ # "genesis-transaction"=>"c41021cb11dce003e6a20a3420cf5954a1d104a1fe314393b915a62f020dcd0a",
55
56
  # "location"=>"6a57dedb854c9bcac3fd2daab1172dd9a45f24c366dd8db33fe80557f11c6b39:22:0",
56
57
  # "output"=>"6a57dedb854c9bcac3fd2daab1172dd9a45f24c366dd8db33fe80557f11c6b39:22",
57
58
  # "offset"=>"0"}
58
59
  ```
59
60
 
60
61
 
62
+ Or by number (starting at 0):
63
+
64
+ ``` ruby
65
+ data = Ordinals.inscription( 21_000_000 )
66
+ pp data
67
+ # GET https://ordinals.com/inscription/4825a59486fa91ee88b6dd58caf0f9ba0f7273e2f30d9eccf9bfdabee55028d2i0...
68
+ #=> {"title"=>"Inscription 21000000",
69
+ # "id"=>"4825a59486fa91ee88b6dd58caf0f9ba0f7273e2f30d9eccf9bfdabee55028d2i0",
70
+ # "address"=>"bc1qq7dx8c9842jm6kf4gn83mft07jqtvye0gm5sq9",
71
+ # "output-value"=>"330",
72
+ # "sat"=>"1894095647358032",
73
+ # "content-length"=>"57 bytes",
74
+ # "content-type"=>"text/plain",
75
+ # "timestamp"=>"2023-08-02 11:00:22 UTC",
76
+ # "genesis-height"=>"801343",
77
+ # "genesis-fee"=>"842",
78
+ # "genesis-transaction"=>"4825a59486fa91ee88b6dd58caf0f9ba0f7273e2f30d9eccf9bfdabee55028d2",
79
+ # "location"=>"4825a59486fa91ee88b6dd58caf0f9ba0f7273e2f30d9eccf9bfdabee55028d2:0:0",
80
+ # "output"=>"4825a59486fa91ee88b6dd58caf0f9ba0f7273e2f30d9eccf9bfdabee55028d2:0",
81
+ # "offset"=>"0"}
82
+
83
+ content = Ordinals.content( 21_000_000 )
84
+ pp content
85
+ # GET https://ordinals.com/content/4825a59486fa91ee88b6dd58caf0f9ba0f7273e2f30d9eccf9bfdabee55028d2i0...
86
+ #=> <Ordinals::Api::Content:0x0000028bd4c1ff18
87
+ # @data="{\"p\":\"brc-20\",\"op\":\"mint\",\"tick\":\"sats\",\"amt\":\"99999999\"}",
88
+ # @length=nil,
89
+ # @type="text/plain">
90
+ ```
91
+
92
+
93
+
61
94
  Let's try querying for litecoin (ltc) ordinal inscriptions:
62
95
 
63
96
  ``` ruby
@@ -83,18 +116,43 @@ pp data
83
116
  #=> {"title"=>"Inscription 642",
84
117
  # "id"=>"f2b6adf7b2d0f128fb14817ff37f5f36e9176b17571e20c49871875553f937b4i0",
85
118
  # "address"=>"LSDz4fM4mLFyAZVoDuKyMHYq3af6cSZGSY",
86
- # "output value"=>"9878",
87
- # "preview"=>"link",
88
- # "content"=>"link",
89
- # "content length"=>"816 bytes",
90
- # "content type"=>"image/png",
119
+ # "output-value"=>"9878",
120
+ # "content-length"=>"816 bytes",
121
+ # "content-type"=>"image/png",
91
122
  # "timestamp"=>"2023-02-21 05:29:33 UTC",
92
- # "genesis height"=>"2426201",
93
- # "genesis fee"=>"361",
94
- # "genesis transaction"=>"f2b6adf7b2d0f128fb14817ff37f5f36e9176b17571e20c49871875553f937b4",
123
+ # "genesis-height"=>"2426201",
124
+ # "genesis-fee"=>"361",
125
+ # "genesis-transaction"=>"f2b6adf7b2d0f128fb14817ff37f5f36e9176b17571e20c49871875553f937b4",
95
126
  # "location"=>"6802c71b49f18aab91d0b363762b24afb1bf00c4820a5f782de6dd1b1cfbd68a:0:0",
96
127
  # "output"=>"6802c71b49f18aab91d0b363762b24afb1bf00c4820a5f782de6dd1b1cfbd68a:0",
97
128
  # "offset"=>"0"}
129
+
130
+
131
+ content = Ordinals.content( 0 )
132
+ pp content
133
+ # GET https://ordinalslite.com/content/71e0f6dc87a473aa69787fff8e09e5eddfdca96e587928a5b1a25c0ae16dc0eei0...
134
+ #=> <Ordinals::Api::Content:0x00000242a9a874e0
135
+ # @data="%PDF-1.3\...",
136
+ # @length=nil,
137
+ # @type="application/pdf">
138
+
139
+ data = Ordinals.inscription( 0 )
140
+ pp data
141
+ # GET https://ordinalslite.com/inscription/71e0f6dc87a473aa69787fff8e09e5eddfdca96e587928a5b1a25c0ae16dc0eei0...
142
+ #=> {"title"=>"Inscription 0",
143
+ # "id"=>"71e0f6dc87a473aa69787fff8e09e5eddfdca96e587928a5b1a25c0ae16dc0eei0",
144
+ # "address"=>"ltc1qz7qy4m8nhfqggxrtj308njs52lu9f5l9v7d5wd",
145
+ # "output-value"=>"10000",
146
+ # "sat"=>"906691404536244",
147
+ # "content-length"=>"57237 bytes",
148
+ # "content-type"=>"application/pdf",
149
+ # "timestamp"=>"2023-02-19 20:26:43 UTC",
150
+ # "genesis-height"=>"2425370",
151
+ # "genesis-fee"=>"15968",
152
+ # "genesis-transaction"=>"71e0f6dc87a473aa69787fff8e09e5eddfdca96e587928a5b1a25c0ae16dc0ee",
153
+ # "location"=>"71e0f6dc87a473aa69787fff8e09e5eddfdca96e587928a5b1a25c0ae16dc0ee:0:0",
154
+ # "output"=>"71e0f6dc87a473aa69787fff8e09e5eddfdca96e587928a5b1a25c0ae16dc0ee:0",
155
+ # "offset"=>"0"}
98
156
  ```
99
157
 
100
158
  That's it.
@@ -267,7 +325,7 @@ composite = RecursiveImageComposite.new( 10, 10, width: 96,
267
325
  data = read_json( "./ordinalpunks.json" )
268
326
  puts " #{data['items'].size} inscribe(s)"
269
327
 
270
- data['items'].each_with_index do |rec|
328
+ data['items'].each do |rec|
271
329
  id = rec['inscription_id']
272
330
  comment = rec['name']
273
331
 
@@ -353,7 +411,7 @@ Use it as you please with no restrictions whatsoever.
353
411
  ## Questions? Comments?
354
412
 
355
413
  Join us in the [Ordgen / ORC-721 discord (chat server)](https://discord.gg/dDhvHKjm2t). Yes you can.
356
- Your questions and commetary welcome.
414
+ Your questions and commentary welcome.
357
415
 
358
416
 
359
417
  Or post them over at the [Help & Support](https://github.com/geraldb/help) page. Thanks.
data/Rakefile CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'hoe'
2
+ require './lib/ordinals/version.rb'
2
3
 
3
4
 
4
5
  ###
@@ -10,7 +11,7 @@ require 'hoe'
10
11
 
11
12
  Hoe.spec 'ordinals' do
12
13
 
13
- self.version = '1.1.1'
14
+ self.version = Ordinals::VERSION
14
15
 
15
16
  self.summary = 'ordinals gem - ordinals (inscription) api wrapper & helpers for Bitcoin, Litecoin, Dogecoin & co.'
16
17
  self.description = summary
data/lib/ordinals/api.rb CHANGED
@@ -24,9 +24,13 @@ class Api ## change/rename Api to Client - why? why not?
24
24
  end
25
25
 
26
26
 
27
+ def config() Ordinals.config; end ## convenience shortcut helper
28
+
27
29
  def initialize( base, inscription: 'inscription' )
28
- @base = base
29
- @inscription = inscription
30
+ @base = base
31
+ @inscription = inscription
32
+ @requests = 0 ## count requests (for delay_in_s sleeping/throttling)
33
+ @pages = {} ## (0-)99, (100-)199, (200-)299 etc.
30
34
  end
31
35
 
32
36
 
@@ -46,8 +50,9 @@ class Content
46
50
  end ## (nested) class Content
47
51
 
48
52
 
53
+ def content( num_or_id )
54
+ id = num_or_id.is_a?( Integer ) ? _num_to_id( num_or_id ) : num_or_id
49
55
 
50
- def content( id )
51
56
  src = "#{@base}/content/#{id}"
52
57
  res = get( src )
53
58
 
@@ -67,6 +72,56 @@ end ## (nested) class Content
67
72
  end
68
73
 
69
74
 
75
+ INSCRIBE_ID_RX = %r{
76
+ inscription/(?<id>[a-fi0-9]+)
77
+ }ix
78
+
79
+
80
+ def inscription_ids( offset: ) ## note: page size is for now fixed 100
81
+ ids = []
82
+
83
+ src = "#{@base}/inscriptions/#{offset}"
84
+ res = get( src )
85
+
86
+ page = res.text ### assumes utf-8 for now
87
+ page.scan( INSCRIBE_ID_RX ) do |_|
88
+ m = Regexp.last_match
89
+ ids << m[:id]
90
+ end
91
+ puts " #{ids.size} inscribe id(s)"
92
+ ids = ids.reverse ## assume latest inscribe id is on top (reverse)
93
+ ids
94
+ end
95
+
96
+ def sub10k_ids
97
+ ids = []
98
+ limit = 100
99
+
100
+ 100.times do |i| ## fetch first hundred (100*100=10000) inscribe ids
101
+ offset = 99 + limit*i
102
+ puts "==> #{i} - @ #{offset}..."
103
+ ids += inscription_ids( offset: offset )
104
+ end
105
+ puts " #{ids.size} inscribe id(s) - total"
106
+ ids
107
+ end
108
+
109
+
110
+ def _num_to_id( num )
111
+ limit = 100
112
+ page, i = num.divmod( limit )
113
+ offset = 99+limit*page
114
+ ## e.g. 100.divmod( 100 ) => [1,0]
115
+ ## 100.divmod( 100 ) => [0,0]
116
+ ## 99.divmod( 100 ) => [0,99]
117
+ ## etc.
118
+
119
+ ## auto-add to page cache
120
+ ids = @pages[ offset ] ||= inscription_ids( offset: offset )
121
+ ids[i]
122
+ end
123
+
124
+
70
125
  =begin
71
126
  <dl>
72
127
  <dt>id</dt>
@@ -158,7 +213,9 @@ offset: 0
158
213
 
159
214
  =end
160
215
 
161
- def inscription( id )
216
+ def inscription( num_or_id )
217
+ id = num_or_id.is_a?( Integer ) ? _num_to_id( num_or_id ) : num_or_id
218
+
162
219
  src = "#{@base}/#{@inscription}/#{id}"
163
220
  res = get( src )
164
221
 
@@ -203,14 +260,34 @@ offset: 0
203
260
  data[ k ] = v
204
261
  end
205
262
  data
263
+
264
+
265
+ ## post process to convert to headers format
266
+ ## e.g. replace space with -
267
+ ## and remove/exclude content & preview
268
+ h = {}
269
+ data.each do |k,v|
270
+ next if ['preview', 'content'].include?( k )
271
+ h[ k.gsub( ' ', '-') ] = v
272
+ end
273
+ h
206
274
  end
207
275
 
208
276
 
209
277
  def get( src )
278
+ @requests += 1
279
+
280
+ if @requests > 1 && config.delay_in_s
281
+ puts "request no. #{@requests}@#{@base}; sleeping #{config.delay_in_s} sec(s)..."
282
+ sleep( config.delay_in_s )
283
+ end
284
+
210
285
  res = Webclient.get( src )
286
+
211
287
  if res.status.ok?
212
288
  res
213
289
  else
290
+ ## todo/fix: raise exception here!!!!
214
291
  puts "!! ERROR - HTTP #{res.status.code} #{res.status.message} - failed web request >#{src}<; sorry"
215
292
  exit 1
216
293
  end
@@ -0,0 +1,208 @@
1
+
2
+ module Ordinals
3
+
4
+ class Cache
5
+
6
+ def initialize( dir )
7
+ @dir = dir
8
+ @force = false
9
+ @delay_in_s = 0.5 ## wait 0.5s before next (possible) request
10
+ @requests = 0
11
+ end
12
+
13
+
14
+
15
+ def json_to_txt( data )
16
+ buf = ''
17
+ data.each do |k,v|
18
+ next if ['preview', 'content'].include?( k )
19
+ buf << "#{k.gsub( ' ', '-')}: #{v}\n"
20
+ end
21
+ buf
22
+ end
23
+
24
+
25
+
26
+ def _find_blobs
27
+ ## note: *.{txt,json,png} will also include *.meta.txt
28
+ ### CANNOT use, thus, try *i?.*
29
+ Dir.glob( "#{@dir}/**/*i?.{txt,json,png}" )
30
+ end
31
+
32
+ def _find_meta
33
+ Dir.glob( "#{@dir}/**/*.meta.txt" )
34
+ end
35
+
36
+ def stats
37
+ paths = _find_meta
38
+ puts " #{paths.size} inscribe metadatafile(s) found"
39
+
40
+ paths = _find_blobs
41
+ puts " #{paths.size} inscribe blob(s) found"
42
+ end
43
+
44
+
45
+
46
+
47
+
48
+
49
+ def add( id )
50
+ ## step 1) get metadata records
51
+ meta_path = "#{@dir}/#{id[0,2]}/#{id[2..-1]}.meta.txt"
52
+
53
+ meta = nil
54
+ if !@force && File.exist?( meta_path )
55
+ meta = read_headers( meta_path )
56
+ print "."
57
+ else
58
+ print " meta-#{id} "
59
+ sleep( @delay_in_s ) if @delay_in_s && @requests > 0
60
+
61
+ ## fetch and cache in cache
62
+ meta = Ordinals.inscription( id )
63
+ pp meta
64
+ meta_txt = json_to_txt( meta )
65
+
66
+ write_text( meta_path, meta_txt )
67
+ @requests += 1
68
+ end
69
+
70
+
71
+ content_type = meta['content-type']
72
+ extname = if content_type.start_with?( 'text/plain' )
73
+ '.txt'
74
+ elsif content_type.start_with?( 'application/json' )
75
+ '.json'
76
+ elsif content_type.start_with?( 'image/png' )
77
+ '.png'
78
+ else
79
+ puts "!! ERROR - unexpected content type; got: #{content_type}"
80
+ ## pp meta
81
+ exit 1
82
+ end
83
+
84
+
85
+ path = "#{@dir}/#{id[0,2]}/#{id[2..-1]}#{extname}"
86
+ if !@force && File.exist?( path )
87
+ ## puts " in cache"
88
+ print "."
89
+ else
90
+ print " blob-#{id} "
91
+ sleep( @delay_in_s ) if @delay_in_s && @requests > 0
92
+
93
+ ## note: save text as blob - byte-by-byte as is (might be corrupt text)
94
+ content = Ordinals.content( id )
95
+ ## pp content
96
+ ## puts "data:"
97
+ ## puts content.data
98
+
99
+ write_blob( path, content.data )
100
+ end
101
+ end # method add
102
+
103
+
104
+ def _prepare_csv( path )
105
+ ids = []
106
+ recs = read_csv( path )
107
+ recs.each do |rec|
108
+ ids << rec['id']
109
+ end
110
+ ids
111
+ end
112
+
113
+ def add_csv( path )
114
+ ids = _prepare_csv( path )
115
+ ids = ids.uniq ## make uniq by default - why? why not?
116
+ puts " #{ids.size} inscribe id(s)"
117
+ ids.each { |id| add(id) }
118
+ end
119
+
120
+
121
+
122
+ ## get inscription links
123
+ ## e.g.
124
+ ## inscription/e8830c5fbdfc2f529350fd04a2ac20bb097dce98f159223837513ae3c8d05714i0
125
+
126
+ =begin
127
+ INSCRIBE_ID_RX = %r{
128
+ /b # boundry (non-alphanum)
129
+ (?<id>[a-f0-9]{64}i[0-9])
130
+ /b # boundry (non-alphanum)
131
+ }ix
132
+ =end
133
+
134
+ INSCRIBE_ID_RX = %r{
135
+ inscription/(?<id>[a-fi0-9]+)
136
+ }ix
137
+
138
+
139
+ ##
140
+ ## strict rx gets:
141
+ ## 168 inscribe id(s)
142
+ ## 118 inscribe id(s) - unique
143
+ ##
144
+ ## loose gets:
145
+ ## 300 inscribe id(s)
146
+ ## 250 inscribe id(s) - unique
147
+ ##
148
+ ## /b for boundray NOT working - why? why not?
149
+
150
+
151
+ def _prepare_html( path )
152
+ ids = []
153
+ txt = read_text( path )
154
+ txt.scan( INSCRIBE_ID_RX ) do |_|
155
+ m = Regexp.last_match
156
+
157
+ ids << m[:id]
158
+ end
159
+ ids
160
+ end
161
+
162
+ def add_html( path )
163
+ ids = _prepare_html( path )
164
+ ids = ids.uniq ## make uniq by default - why? why not?
165
+ puts " #{ids.size} inscribe id(s)"
166
+ ids.each { |id| add(id) }
167
+ end
168
+
169
+ def _prepare_dir( dir )
170
+ ids = []
171
+
172
+ ## in html format
173
+ pages = Dir.glob( "#{dir}/**/*.html")
174
+ pages.each do |path|
175
+ ids += _prepare_html( path )
176
+ end
177
+
178
+ ### in csv format
179
+ datasets = Dir.glob( "#{dir}/**/*.csv")
180
+ datasets.each do |path|
181
+ ids += _prepare_csv( path )
182
+ end
183
+
184
+ ids
185
+ end
186
+
187
+ def add_dir( dir )
188
+ ids = _prepare_dir( dir )
189
+ puts " #{ids.size} inscribe id(s)"
190
+ ## turn into symbol (to make uniq work ??)
191
+ ids = ids.uniq
192
+ pp ids
193
+ puts " #{ids.size} inscribe id(s) - unique"
194
+ ids.each { |id| add(id) }
195
+ end
196
+
197
+
198
+
199
+ def read( id )
200
+ _read_inscribe( "#{@dir}/#{id[0,2]}/#{id[2..-1]}.meta.txt" )
201
+ end
202
+
203
+ def _read_inscribe( path )
204
+ read_headers( path )
205
+ end
206
+
207
+ end # class Cache
208
+ end # module Ordinals
@@ -0,0 +1,127 @@
1
+
2
+
3
+ module Ordinals
4
+ class Sandbox
5
+ def initialize( dir='./content' )
6
+ @dir = dir
7
+ @force = false
8
+ @delay_in_s = 0.5 ## wait 0.5s before next (possible) request
9
+ @requests = 0
10
+ end
11
+
12
+
13
+ def exist?( id ) ### add alias for different name(s) - why? why not?
14
+ path = "#{@dir}/#{id}"
15
+ File.exist?( path )
16
+ end
17
+
18
+ def read( id )
19
+ path = "#{@dir}/#{id}"
20
+ read_blob( path )
21
+ end
22
+
23
+ def add( id )
24
+ path = "#{@dir}/#{id}"
25
+ if !@force && File.exist?( path )
26
+ ## puts " in sandbox"
27
+ else
28
+ sleep( @delay_in_s ) if @delay_in_s && @requests > 0
29
+
30
+ ## note: save text as blob - byte-by-byte as is (might be corrupt text)
31
+ content = Ordinals.content( id )
32
+ ## pp content
33
+ #=> #<Ordinals::Api::Content:0x000001a1352df938
34
+ # @data="RIFF\xF8\v\x00\x00WEBPVP8 \xEC\v\x00\x00...",
35
+ # @length=3072,
36
+ # @type="image/png"
37
+
38
+ ## puts "data:"
39
+ ## puts content.data
40
+ write_blob( path, content.data )
41
+ @requests += 1
42
+ end
43
+ end
44
+
45
+
46
+ def add_csv( path )
47
+ recs = read_csv( path )
48
+ puts " #{recs.size} record(s)"
49
+
50
+ recs.each_with_index do |rec,i|
51
+ id = rec['id']
52
+ puts "==> #{i+1}/#{recs.size} - #{id}..."
53
+ add( id )
54
+ end
55
+ end
56
+
57
+ def add_collection( path )
58
+ data = read_json( path )
59
+ puts " #{data['items'].size} record(s)"
60
+
61
+ data['items'].each_with_index do |rec,i|
62
+ name = rec['name']
63
+ id = rec['inscription_id']
64
+ puts "==> #{i+1}/#{data['items'].size} - #{name} @ #{id}..."
65
+ add( id )
66
+ end
67
+ end
68
+
69
+
70
+
71
+ ID_RX = /[a-f0-9]{64}i[0-9]/x
72
+
73
+ def _find_ids( values )
74
+ values.select { |val| val.is_a?(String) && ID_RX.match(val) }
75
+ end
76
+
77
+ def add_data( obj )
78
+ if obj.is_a?( Array )
79
+ recs = obj
80
+ puts " #{recs.size} record(s)"
81
+ recs.each_with_index do |rec,i|
82
+ if rec.is_a?( String )
83
+ id = rec
84
+ puts "==> #{i+1}/#{recs.size} #{id}..."
85
+ add( id )
86
+ else ## assume array of strings for now
87
+ values = rec
88
+ print "==> #{i+1}/#{recs.size} "
89
+ ids = _find_ids( values )
90
+ if ids.size == 1
91
+ id = ids[0]
92
+ puts " #{id}..."
93
+ add( id )
94
+ else
95
+ puts
96
+ if ids.empty?
97
+ puts "!! ERROR - no inscribe id found in data row:"
98
+ else
99
+ puts "!! ERROR - more than one (that is, #{ids.size}) inscribe id found in data row:"
100
+ end
101
+ pp values
102
+ exit 1
103
+ end
104
+ end
105
+ end
106
+ else
107
+ ## todo/fix: raise ArgumentError - why? why not?
108
+ puts "!! ERROR - sorry"
109
+ exit 1
110
+ end
111
+ end
112
+
113
+ # todo/add
114
+ # def add_html( path )
115
+ # end
116
+
117
+
118
+ def add_dir( rootdir )
119
+ ## glob for **.csv and **.html
120
+ datasets = Dir.glob( "#{rootdir}/**/*.csv" )
121
+ datasets.each do |path|
122
+ add_csv( path )
123
+ end
124
+ end
125
+ end ## class Sandbox
126
+ end ## module Ordinals
127
+
@@ -0,0 +1,20 @@
1
+
2
+ module Ordinals
3
+ # sync version w/ sport.db n friends - why? why not?
4
+ MAJOR = 1 ## todo: namespace inside version or something - why? why not??
5
+ MINOR = 2
6
+ PATCH = 0
7
+ VERSION = [MAJOR,MINOR,PATCH].join('.')
8
+
9
+ def self.version
10
+ VERSION
11
+ end
12
+
13
+ def self.banner
14
+ "ordinals/#{VERSION} on Ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}] in >#{root}<"
15
+ end
16
+
17
+ def self.root
18
+ File.expand_path( File.dirname(File.dirname(File.dirname(__FILE__))) )
19
+ end
20
+ end
data/lib/ordinals.rb CHANGED
@@ -5,6 +5,49 @@ require 'nokogiri'
5
5
 
6
6
 
7
7
 
8
+ ### todo/fix:
9
+ ### move upstream - read_headers
10
+
11
+ ##
12
+ # note: key only supports a-z0-9 AND dash(-)
13
+ # no underscore(_) or dot(.)
14
+ #
15
+ # follow HTTP headers and domain names format
16
+ ##
17
+
18
+ HEADER_RX = /\A(?<key>[a-z][a-z0-9-]*)
19
+ :
20
+ [ ]*
21
+ (?<value>.+?) ## non-greedy
22
+ \z
23
+ /x
24
+
25
+
26
+ def read_headers( path )
27
+ txt = read_text( path )
28
+ h = {}
29
+ txt.each_line do |line|
30
+ line = line.strip
31
+ ## skip empty and comment lines
32
+ next if line.empty? || line.start_with?( '#' )
33
+
34
+ if m=HEADER_RX.match(line)
35
+ key = m[:key]
36
+ value = m[:value]
37
+
38
+ h[key] = value
39
+ else
40
+ puts "!! ERROR - parse error - no header pattern match for:"
41
+ puts line
42
+ exit 1
43
+ end
44
+ end
45
+ h
46
+ end
47
+
48
+
49
+
50
+
8
51
 
9
52
  module Ordinals
10
53
  class Configuration
@@ -43,6 +86,15 @@ module Ordinals
43
86
  self.chain = 'btc' unless defined?( @client )
44
87
  @client
45
88
  end
89
+
90
+ def delay_in_s
91
+ ## note - default to 1 (sec) if not set
92
+ self.delay_in_s = 1 unless defined?( @delay_in_s )
93
+ @delay_in_s
94
+ end
95
+ def delay_in_s=(value) @delay_in_s = value; end
96
+ alias_method :sleep, :delay_in_s ## add sleep alias (or wait) - why? why not?
97
+ alias_method :sleep=, :delay_in_s=
46
98
  end # class Configuration
47
99
 
48
100
 
@@ -76,15 +128,22 @@ module Ordinals
76
128
 
77
129
  ###################
78
130
  ### more convenience shortcuts
79
- def self.inscription( id ) client.inscription( id ); end
80
- def self.content( id ) client.content( id ); end
131
+ def self.inscription( num_or_id ) client.inscription( num_or_id ); end
132
+ def self.content( num_or_id ) client.content( num_or_id ); end
133
+
134
+ def self.inscription_ids( offset: ) client.inscription_ids( offset: offset ); end
135
+ def self.sub10k_ids() client.sub10k_ids; end
81
136
  end # module Ordinals
82
137
 
83
138
 
84
139
 
85
140
 
86
141
  ## our own code
142
+ require_relative 'ordinals/version'
87
143
  require_relative 'ordinals/api'
144
+
145
+ require_relative 'ordinals/cache'
146
+ require_relative 'ordinals/sandbox' ## (blob) "flat" content cache by inscribe id (./content)
88
147
  require_relative 'ordinals/stats'
89
148
 
90
149
 
@@ -98,3 +157,7 @@ require_relative 'ordinals/recursive/generator'
98
157
  RcsvImage = RecursiveImage
99
158
  RcsvImageComposite = RecursiveImageComposite
100
159
 
160
+
161
+
162
+ # say hello
163
+ puts Ordinals.banner ## if defined?($RUBYCOCOS_DEBUG) && $RUBCOCOS_DEBUG
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ordinals
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.1
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gerald Bauer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-07-21 00:00:00.000000000 Z
11
+ date: 2023-08-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: cocos
@@ -88,10 +88,13 @@ files:
88
88
  - Rakefile
89
89
  - lib/ordinals.rb
90
90
  - lib/ordinals/api.rb
91
+ - lib/ordinals/cache.rb
91
92
  - lib/ordinals/recursive/composite.rb
92
93
  - lib/ordinals/recursive/generator.rb
93
94
  - lib/ordinals/recursive/image.rb
95
+ - lib/ordinals/sandbox.rb
94
96
  - lib/ordinals/stats.rb
97
+ - lib/ordinals/version.rb
95
98
  homepage: https://github.com/ordbase/ordbase
96
99
  licenses:
97
100
  - Public Domain