ordlite 0.2.0 → 0.3.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: 6051393488775c98fdee4716e6c49b5cd89c6cd404a45f48024a47dea633acfb
4
- data.tar.gz: d43e1d73f1b49b27ae35e745eaa8e13be394635d2367233bc6c341fd1aa54933
3
+ metadata.gz: 44ca59e7026e8b717d26f8ae42ed7050d08abd44047dca6de505582253e2f22b
4
+ data.tar.gz: f2728433967a7d58612e68d0402a3d9c9c9b644e71bdfe6c4aeca2e103cd116f
5
5
  SHA512:
6
- metadata.gz: 05e95871ce3a85dd31557e38a4247d5cd38e25406e2ac535a1254e6952e68b8054e8c1a06378b4620ce8168c67d1e4cb7eac50305da9e7e59e2f10152a4aa971
7
- data.tar.gz: 3fe7780c583d525dd60170f4ec0e996f430722d53899e3edb321d9af67425024c262a253e1cf718ab0b7316f9fbb3ae0a6903b3a82debf3860511a167872a1ab
6
+ metadata.gz: 6dfd8030275128218b5057c0ed7a585f5a1688acfe894da4183e7e788b26b239565f84ac25bead42cf144d393a1e04ee68c900b918d15b8d4202267d2cad4815
7
+ data.tar.gz: af1eab5bb9c6e88b698ccd485ef6d8fc08bcd0f2c714faed0ee66d30c834d9ecc2c250c2792285f772a705a33cf5d13fa558965207d6d237b161de6e9c2ff3dc
data/CHANGELOG.md CHANGED
@@ -1,4 +1,4 @@
1
- ### 0.2.0 / 2023-07-28
1
+ ### 0.3.0
2
2
  ### 0.0.1 / 2023-07-01
3
3
 
4
4
  * Everything is new. First release
data/Manifest.txt CHANGED
@@ -5,6 +5,7 @@ Rakefile
5
5
  lib/ordlite.rb
6
6
  lib/ordlite/base.rb
7
7
  lib/ordlite/cache.rb
8
+ lib/ordlite/factory.rb
8
9
  lib/ordlite/importer.rb
9
10
  lib/ordlite/models/blob.rb
10
11
  lib/ordlite/models/collection.rb
data/README.md CHANGED
@@ -13,7 +13,7 @@ ordlite - ordinals inscription (on bitcoin & co) database let's you query via sq
13
13
 
14
14
  ## SQL Database Model
15
15
 
16
- Inscribes • Blobs • Collections • Generatives
16
+ Inscribes • Blobs • Collections • Factories • Generatives
17
17
 
18
18
 
19
19
  Table Inscribes
@@ -63,6 +63,7 @@ puts
63
63
  puts " #{Inscribe.count} inscribe(s)"
64
64
  puts " #{Blob.count} blob(s)"
65
65
  puts " #{Collection.count} collection(s)"
66
+ puts " #{Factory.count} factories"
66
67
  puts " #{Generative.count} generative(s)"
67
68
 
68
69
  #=> 0 inscribe(s)
@@ -74,68 +75,134 @@ puts " #{Generative.count} generative(s)"
74
75
 
75
76
 
76
77
 
77
- ### Example No 1 - Query Ordgen Deploy / Mint Inscriptions
78
+ ### Example No 1 - Auto-Add (Via Ordinals.com) First Thousand Inscriptions (Sub 1k)
78
79
 
79
80
  ``` ruby
80
81
  require 'ordlite'
81
82
 
82
83
 
83
- OrdDb.connect( adapter: 'sqlite3',
84
- database: './ord.db' )
84
+ OrdDb.open( './ord.db' )
85
85
 
86
86
 
87
- ####################
88
- ## query for deploy candidates
89
- ##
90
- ## e.g. sql where clause like
91
- ## content LIKE '%deploy%'
92
- ## AND ( content LIKE '%orc-721%'
93
- ## OR content LIKE '%og%')
94
- ##
87
+ 1000.times do |num| # auto-add inscription 0-999
88
+ OrdDb.import( num )
89
+ end
90
+
91
+ puts
92
+ puts " #{Inscribe.count} inscribe(s)"
93
+ puts " #{Blob.count} blob(s)"
94
+ #=> 1000 inscribe(s)
95
+ #=> 1000 blob(s)
96
+ ```
95
97
 
96
- deploys = Inscribe.deploys
97
- puts " #{deploys.size} deploy candidate(s)"
98
+ Let's query for the ten biggest (by bytes) inscriptions
99
+ (and pretty print the result):
98
100
 
99
- deploys.each_with_index do |rec,i|
100
- puts "==> deploy #{i} - num #{rec.num} - #{rec.bytes} bytes - #{rec.date}"
101
- puts rec.content
101
+ ```ruby
102
+ Inscribe.biggest.limit(10).each do |rec|
103
+ print "#{number_to_human_size(rec.bytes)} (#{rec.bytes} bytes) - "
104
+ print "Inscribe №#{rec.num} (#{rec.content_type}) - "
105
+ print "#{rec.date} - #{rec.fee} fee in sats"
106
+ print "\n"
102
107
  end
108
+ ```
103
109
 
110
+ resulting in:
104
111
 
105
- punks_deploys = Inscribe.deploys_by( slug: 'diypunks')
106
- puts " #{punks_deploys.size} deploy candidate(s)"
112
+ ```
113
+ 3.73 MB (3915537 bytes) - Inscribe №652 (image/jpeg) - 2023-02-01 20:38:33 - 0 fee in sats
114
+ 385 KB (394718 bytes) - Inscribe №978 (application/epub+zip) - 2023-02-02 06:46:04 - 109325 fee in sats
115
+ 385 KB (394479 bytes) - Inscribe №546 (image/gif) - 2023-02-01 10:41:50 - 1489860 fee in sats
116
+ 385 KB (394440 bytes) - Inscribe №833 (image/png) - 2023-02-02 01:13:51 - 99314 fee in sats
117
+ 381 KB (389858 bytes) - Inscribe №388 (image/jpeg) - 2023-01-31 14:01:38 - 981620 fee in sats
118
+ 379 KB (388417 bytes) - Inscribe №291 (image/gif) - 2023-01-30 17:58:54 - 586794 fee in sats
119
+ 378 KB (386858 bytes) - Inscribe №857 (image/png) - 2023-02-02 01:17:54 - 97407 fee in sats
120
+ 374 KB (383322 bytes) - Inscribe №538 (image/jpeg) - 2023-02-01 10:20:28 - 96519 fee in sats
121
+ 367 KB (375414 bytes) - Inscribe №378 (image/gif) - 2023-01-31 09:47:55 - 945300 fee in sats
122
+ 365 KB (373504 bytes) - Inscribe №288 (image/jpeg) - 2023-01-30 16:51:46 - 94050 fee in sats
123
+ ```
107
124
 
108
125
 
126
+ Let's query for all inscriptions grouped by date (day) and dump the results:
109
127
 
110
- #######################
111
- ## query for mint candidates
112
- ##
113
- ## e.g. sql where clause like
114
- ## content LIKE '%mint%'
115
- ## AND ( content LIKE '%orc-721%'
116
- ## OR content LIKE '%og%')
128
+ ```ruby
129
+ pp Inscribe.counts_by_date ## or count_by_day
130
+ ```
117
131
 
118
- mints = Inscribe.mints
119
- puts " #{mints.size} mint candidate(s)"
132
+ resulting in:
120
133
 
121
- ## print last hundred mint candidates
122
- mints[-100,100].each_with_index do |rec,i|
123
- puts "==> mint #{i} - num #{rec.num} - #{rec.bytes} bytes - #{rec.date}"
124
- puts rec.content
125
- end
134
+ ```
135
+ {"2022-12-14" => 1,
136
+ "2022-12-17" => 1,
137
+ "2022-12-19" => 1,
138
+ "2023-01-05" => 1,
139
+ "2023-01-10" => 1,
140
+ "2023-01-12" => 1,
141
+ "2023-01-13" => 2,
142
+ "2023-01-15" => 1,
143
+ "2023-01-16" => 1,
144
+ "2023-01-19" => 5,
145
+ "2023-01-20" => 3,
146
+ "2023-01-21" => 5,
147
+ "2023-01-22" => 34,
148
+ "2023-01-23" => 23,
149
+ "2023-01-24" => 4,
150
+ "2023-01-25" => 9,
151
+ "2023-01-26" => 12,
152
+ "2023-01-27" => 19,
153
+ "2023-01-28" => 16,
154
+ "2023-01-29" => 128,
155
+ "2023-01-30" => 82,
156
+ "2023-01-31" => 98,
157
+ "2023-02-01" => 220,
158
+ "2023-02-02" => 332}
159
+ ```
126
160
 
161
+ Let's query for all inscriptions grouped by month and dump the results:
162
+
163
+ ```ruby
164
+ pp Inscribe.counts_by_month
165
+ ```
166
+
167
+ resulting in:
168
+
169
+ ```
170
+ {"2022-12" => 3,
171
+ "2023-01" => 445,
172
+ "2023-02" => 552}
173
+ ```
127
174
 
128
- phunks_mints = Inscribe.mints_by( slug: 'diyphunks')
129
- puts " #{phunks_mints.size} mint candidate(s)"
130
175
 
176
+ Let's query for all content types and group by count (descending) and dump the results:
131
177
 
132
- puts " #{deploys.size} deploy candidate(s)"
133
- puts " #{mints.size} mint candidate(s)"
134
178
 
135
- #=> 123 deploy candidate(s)
136
- #=> 7453 mint candidate(s)
179
+ ```ruby
180
+ pp Inscribe.counts_by_content_type
137
181
  ```
138
182
 
183
+ resulting in:
184
+
185
+ ```
186
+ {"image/png" => 475,
187
+ "image/jpeg" => 188,
188
+ "image/webp" => 117,
189
+ "text/plain;charset=utf-8" => 112,
190
+ "image/svg+xml" => 62,
191
+ "text/html;charset=utf-8" => 18,
192
+ "image/gif" => 11,
193
+ "audio/mpeg" => 6,
194
+ "application/pdf" => 2,
195
+ "image/avif" => 2,
196
+ "video/webm" => 2,
197
+ "application/epub+zip" => 1,
198
+ "application/pgp-signature" => 1,
199
+ "audio/midi" => 1,
200
+ "audio/mod" => 1,
201
+ "video/mp4" => 1}
202
+ ```
203
+
204
+ and so on.
205
+
139
206
 
140
207
 
141
208
 
@@ -150,13 +217,10 @@ into an (sql) database e.g. `ord.db`:
150
217
  ``` ruby
151
218
  require 'ordlite'
152
219
 
153
- OrdDb.connect( adapter: 'sqlite3',
154
- database: './ord.db' )
155
-
156
- OrdDb.create_all # build table schema
220
+ OrdDb.open( './ord.db' )
157
221
 
158
222
  cache_dir = './ordinals.cache/inscription'
159
- cache = OrdDb::Cache.new( cache_dir )
223
+ cache = Ordinals::Cache.new( cache_dir )
160
224
  cache.import_all
161
225
 
162
226
 
data/Rakefile CHANGED
@@ -27,6 +27,7 @@ Hoe.spec 'ordlite' do
27
27
  ['props'],
28
28
  ['props-activerecord'],
29
29
  ['sqlite3'],
30
+ ['pixelart'], ## required for factory (ordgen/orc-721) support for now
30
31
  ]
31
32
 
32
33
  self.licenses = ['Public Domain']
data/lib/ordlite/base.rb CHANGED
@@ -134,5 +134,13 @@ module OrdDb
134
134
  end # module OrdDb
135
135
 
136
136
 
137
+
138
+ ####
139
+ # add factory (ordgen/orc-721) support here for now - why? why not?
140
+ require 'pixelart'
141
+ require_relative 'factory'
142
+
143
+
144
+
137
145
  # say hello
138
146
  puts Ordlite.banner ## if defined?($RUBYCOCOS_DEBUG) && $RUBCOCOS_DEBUG
@@ -0,0 +1,132 @@
1
+
2
+
3
+ class FactorySpritesheet ## check - rename to catalog or atlas NOT spritesheet - why? why not?
4
+ def self.read_inscribes( *inscribes, width:,
5
+ height: )
6
+ ## map inscribes to images
7
+ images = inscribes.map {|inscribe| Pixelart::Image.blob( inscribe.content ) }
8
+ ## puts " #{images.size} image(s)"
9
+
10
+ new( *images, width: width,
11
+ height: height)
12
+ end
13
+
14
+ def initialize( *images, width:,
15
+ height: )
16
+ @tile_width = width
17
+ @tile_height = height
18
+ @tiles = []
19
+ images.each {|img| add(img) }
20
+ end
21
+
22
+ def count() @tiles.size; end
23
+ alias_method :size, :count
24
+ alias_method :tile_count, :count ## add tile_count - why? why not?
25
+ def tile_width() @tile_width; end ## use width - why? why not?
26
+ def tile_height() @tile_height; end ## use height - why? why not?
27
+
28
+ def tile( index ) @tiles[ index ]; end
29
+ alias_method :[], :tile
30
+
31
+ def add_inscribe( inscribe ) _add( Pixelart::Image.blob( inscribe.content )); end
32
+ def add( img )
33
+ ## 1:1 tile; use as is
34
+ if img.width == @tile_width && img.height == @tile_height
35
+ @tiles << img
36
+ else ## assume spritesheet??
37
+ ## wrap into composite image
38
+ composite = Pixelart::ImageComposite.new( img.image, width: @tile_width,
39
+ height: @tile_height )
40
+ cols = img.width / composite.tile_width
41
+ rows = img.height / composite.tile_height
42
+ puts " #{composite.count} tile(s) in #{cols}x#{rows} grid"
43
+ composite.each {|tile| @tiles << tile }
44
+ end
45
+ end
46
+ alias_method :<<, :add
47
+ end ## class Spritesheet
48
+
49
+
50
+ class FactoryGenerator
51
+ ###################
52
+ ## convenience setup helper(s)
53
+ def self.read_inscribes( *inscribes, width:,
54
+ height: )
55
+ new( FactorySpritesheet.read_inscribes( *inscribes,
56
+ width: width,
57
+ height: height ))
58
+ end
59
+
60
+ def initialize( spritesheet )
61
+ @spritesheet = spritesheet
62
+ end
63
+
64
+ def _parse( spec )
65
+ ## for delimiter allow for now: - why? why not?
66
+ ## (multiple) space ( )
67
+ ## command or semicolon
68
+ spec.strip.split( %r{[ ,;/_-]+} ).map {|v| v.to_i(10) }
69
+ end
70
+
71
+ def parse( spec )
72
+ ## convenience helper
73
+ ## parses g spec in various (delimited) formats
74
+ g = _parse( spec )
75
+ generate( *g )
76
+ end
77
+
78
+ def generate( *attributes )
79
+ img = Pixelart::Image.new( width, height )
80
+ attributes.each do |num|
81
+ img.compose!( @spritesheet[ num ] )
82
+ end
83
+ img
84
+ end
85
+ alias_method :g, :generate
86
+
87
+ def width() @spritesheet.tile_width; end
88
+ def height() @spritesheet.tile_height; end
89
+ def count() @spritesheet.count; end
90
+ end # class FactoryGenerator
91
+
92
+
93
+
94
+ module OrdDb
95
+ module Model
96
+
97
+ class Factory
98
+
99
+ ## use id/slug to cache generators / spritesheets - why? why not?
100
+ def self.generators
101
+ @generators ||= {}
102
+ end
103
+
104
+ def generator
105
+ generators = self.class.generators
106
+ if generators.has_key?( id )
107
+ generators[ id ]
108
+ else
109
+ ## auto-add generator on first-time/hit/demand
110
+ width, height = _parse_dimension( dim )
111
+ inscribes = layers.to_a ## get layer inscribe records
112
+ generator = FactoryGenerator.read_inscribes( *inscribes,
113
+ width: width,
114
+ height: height )
115
+ generators[ id ] = generator
116
+ generator
117
+ end
118
+ end
119
+
120
+ def generate( *attributes ) ## add g shortcut alias - why? why not?
121
+ generator.generate( *attributes )
122
+ end
123
+
124
+
125
+ ## e.g. convert dimension (width x height) "24x24" or "24 x 24" to [24,24]
126
+ def _parse_dimension( str )
127
+ str.split( /x/i ).map { |str| str.strip.to_i(10) }
128
+ end
129
+
130
+ end # class Factory
131
+ end # module Model
132
+ end # module OrdDb
@@ -1,8 +1,82 @@
1
+ module OrdDb
1
2
 
3
+ class Importer
4
+ Inscribe = Model::Inscribe
5
+ Blob = Model::Blob
6
+ Collection = Model::Collection
2
7
 
3
- module OrdDb
4
8
 
5
- def self.import_collection( path, content: true )
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 )
6
80
  data = read_json( path )
7
81
 
8
82
  meta = data['collection']
@@ -10,13 +84,13 @@ def self.import_collection( path, content: true )
10
84
 
11
85
  name = meta['name']
12
86
 
13
- col = Model::Collection.find_by( name: name )
87
+ col = Collection.find_by( name: name )
14
88
  if col
15
89
  puts "!! WARN - collection already in db; delete first to reimport"
16
90
  return
17
91
  end
18
92
 
19
- col = Model::Collection.create(
93
+ col = Collection.create(
20
94
  name: name,
21
95
  desc: meta['description'],
22
96
  max: meta['max_supply']
@@ -33,35 +107,12 @@ def self.import_collection( path, content: true )
33
107
  inscribe_id: id,
34
108
  name: name )
35
109
 
36
- ## check if inscription / inscribe is already in db?
37
- inscribe = Model::Inscribe.find_by( id: id )
38
- if inscribe ## already in db; dump record
39
- ## pp inscribe
40
- else ## fetch via ordinals.com api and update db
41
- data = Ordinals.inscription( id )
42
- pp data
43
- Model::Inscribe.create_from_api( data )
44
- sleep( 1 ) ## delay in seconds (before next request)
45
- end
46
-
47
- if content
48
- ## check if (content) blob is already in db?
49
- blob = Model::Blob.find_by( id: id )
50
- if blob ## already in db; do nothing
51
- else ## fetch via ordinals.com api and update db
52
- content = Ordinals.content( id )
53
- puts " content-type: #{content.type}"
54
- puts " content-length: #{content.length}"
55
-
56
- Model::Blob.create( id: id, content: content.data )
57
- sleep( 1 ) ## delay in seconds (before next request)
58
- end
59
- end
110
+ _import( id, content: content )
60
111
  end
61
112
  end
62
113
 
63
114
 
64
- def self.import_csv( path, content: true )
115
+ def import_csv( path, content: true )
65
116
  recs = read_csv( path )
66
117
  puts " #{recs.size} inscribe id(s)"
67
118
  #=> 1000 inscribe id(s)
@@ -70,30 +121,154 @@ def self.import_csv( path, content: true )
70
121
  id = rec['id']
71
122
  puts "==> #{i+1}/#{rec.size} @ #{id}..."
72
123
 
73
- ## check if inscription / inscribe is already in db?
74
- inscribe = Model::Inscribe.find_by( id: id )
75
- if inscribe ## already in db; dump record
76
- ## pp inscribe
77
- else ## fetch via ordinals.com api and update db
78
- data = Ordinals.inscription( id )
79
- pp data
80
- Model::Inscribe.create_from_api( data )
81
- sleep( 1 ) ## delay in seconds (before next request)
82
- end
124
+ _import( id, content: content )
125
+ end
126
+ end # method import_csv
83
127
 
84
- if content
85
- ## check if (content) blob is already in db?
86
- blob = Model::Blob.find_by( id: id )
87
- if blob ## already in db; do nothing
88
- else ## fetch via ordinals.com api and update db
89
- content = Ordinals.content( id )
90
- puts " content-type: #{content.type}"
91
- puts " content-length: #{content.length}"
92
-
93
- Model::Blob.create( id: id, content: content.data )
94
- sleep( 1 ) ## delay in seconds (before next request)
95
- end
128
+
129
+ def import( id_or_ids, content: true )
130
+ ## note: support (integer) numbers too (e.g. 0/1/2, etc.)
131
+ if id_or_ids.is_a?( String )
132
+ id = id_or_ids
133
+ _import( id, content: content )
134
+ elsif id_or_ids.is_a?( Integer )
135
+ num = id_or_ids
136
+ _import_by_num( num, content: content )
137
+ elsif id_or_ids.is_a?( Array )
138
+ if id_or_ids.empty? ## id_or_ids.size == 0
139
+ ## do nothing; empty array
140
+ else
141
+ first = id_or_ids[0]
142
+ if first.is_a?( String )
143
+ ids = id_or_ids
144
+ ids.each do |id|
145
+ _import( id, content: content )
146
+ end
147
+ elsif first.is_a?( Integer )
148
+ nums = id_or_ids
149
+ nums.each do |num|
150
+ _import_by_num( num, content: content )
151
+ end
152
+ elsif first.is_a?( Hash ) && first.has_key?( 'id' )
153
+ ## try to get ids with records
154
+ recs = id_or_ids
155
+ ids = recs.map {|rec| rec['id'] }
156
+ ids.each do |id|
157
+ _import( id, content: content )
158
+ end
159
+ elsif first.is_a?( Hash ) && first.has_key?( 'num' )
160
+ ## try to get nums with records
161
+ recs = id_or_ids
162
+ nums = recs.map {|rec| rec['num'] }
163
+ nums.each do |num|
164
+ ## note: support numbers as strings too
165
+ num = num.to_i(10) if num.is_a?( String )
166
+ _import_by_num( num, content: content )
167
+ end
168
+ else
169
+ raise ArgumentError, "expected Array of String|Integer or Hash (with keys id|num); got #{first.class.name}"
170
+ end
96
171
  end
97
- end
98
- end # method self.import_csv
172
+ else
173
+ raise ArgumentError, "expected String or Array; got #{id_or_ids.class.name}"
174
+ end
175
+ end # method import
176
+
177
+
178
+ def _import_content( id )
179
+ ## check if (content) blob is already in db?
180
+ blob = Blob.find_by( id: id )
181
+ if blob ## already in db; do nothing
182
+ else ## fetch via ordinals.com api and update db
183
+ content = Ordinals.content( id )
184
+
185
+ puts " content-type: #{content.type}"
186
+ puts " content-length: #{content.length}"
187
+
188
+ Blob.create( id: id, content: content.data )
189
+ end
190
+ end
191
+
192
+
193
+ def _import( id, content: true )
194
+ ## check if inscription / inscribe is already in db?
195
+ inscribe = Inscribe.find_by( id: id )
196
+ if inscribe ## already in db; dump record
197
+ ## pp inscribe
198
+ else ## fetch via ordinals.com api and update db
199
+ data = Ordinals.inscription( id )
200
+
201
+ pp data
202
+ Inscribe.create_from_api( data )
203
+ end
204
+
205
+ _import_content( id ) if content
206
+ end
207
+
208
+ def _import_by_num( num, content: true )
209
+ ## check if inscription / inscribe is already in db?
210
+ inscribe = Inscribe.find_by( num: num )
211
+ if inscribe ## already in db; dump record
212
+ ## pp inscribe
213
+ else ## fetch via ordinals.com api and update db
214
+ data = Ordinals.inscription( num )
215
+
216
+ pp data
217
+ inscribe = Inscribe.create_from_api( data )
218
+ end
219
+
220
+ _import_content( inscribe.id ) if content
221
+ end
222
+
223
+ end # class Importer
224
+
225
+
226
+
227
+ ###
228
+ ## convenience helpers
229
+
230
+ def self.importer ## "default" importer
231
+ @importer ||= Importer.new
232
+ end
233
+
234
+ def self.import( id_or_ids, content: true )
235
+ importer.import( id_or_ids, content: content )
236
+ end
237
+
238
+
239
+ def self.import_csv( path, content: true )
240
+ importer.import_csv( path, content: content )
241
+ end
242
+
243
+
244
+ def self.import_collection( path, content: true )
245
+ importer.import_collection( path, content: content )
246
+ end
247
+
248
+ def self.import_collection_inscriptions( path,
249
+ name:,
250
+ content: true )
251
+ importer.import_collection_inscriptions( path,
252
+ name: name,
253
+ content: content )
254
+ end
255
+
256
+ def self.import_collection_csv( path,
257
+ name:,
258
+ content: true )
259
+ importer.import_collection_csv( path,
260
+ name: name,
261
+ content: content )
262
+ end
263
+
264
+
265
+ module Model
266
+ class Inscribe
267
+ def self.import( id_or_ids, content: true )
268
+ OrdDb.importer.import( id_or_ids, content: content )
269
+ end
270
+ end # class Inscribe
271
+ end # module Model
272
+
273
+
99
274
  end # module OrdDb
@@ -2,7 +2,11 @@ module OrdDb
2
2
  module Model
3
3
 
4
4
  class Collection < ActiveRecord::Base
5
- has_many :items, -> { order('pos') }
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!!!
6
10
  has_many :inscribes, :through => :items
7
11
  end # class Collection
8
12
 
@@ -7,7 +7,12 @@ module OrdDb
7
7
 
8
8
  belongs_to :inscribe
9
9
 
10
- has_many :inscriberefs, -> { order('pos') } ## join table (use habtm - why? why not?)
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
+
11
16
  has_many :layers, :through => :inscriberefs,
12
17
  :source => :inscribe
13
18
 
@@ -18,6 +18,68 @@ module OrdDb
18
18
 
19
19
  ################################
20
20
  ### scope like helpers
21
+ def self.png() where( content_type: 'image/png' ); end
22
+ def self.gif() where( content_type: 'image/gif' ); end
23
+ def self.jpg() where( content_type: 'image/jpeg' ); end
24
+ def self.webp() where( content_type: 'image/webp' ); end
25
+ def self.svg() where( content_type: 'image/svg+xml' ); end
26
+ def self.avif() where( content_type: 'image/avif' ); end
27
+
28
+ class << self
29
+ alias_method :jpeg, :jpg
30
+ end
31
+
32
+ def self.image
33
+ ## change to/or add alias e.g. image/images - why? why not
34
+ where( content_type: [
35
+ 'image/png',
36
+ 'image/jpeg',
37
+ 'image/gif',
38
+ 'image/webp',
39
+ 'image/svg+xml',
40
+ 'image/avif',
41
+ ])
42
+ end
43
+
44
+ def self.html
45
+ where( content_type: [
46
+ 'text/html;charset=utf-8',
47
+ 'text/html',
48
+ ])
49
+ end
50
+
51
+ def self.js
52
+ where( content_type: [
53
+ 'text/javascript',
54
+ 'application/javascript',
55
+ ])
56
+ end
57
+
58
+ class << self
59
+ alias_method :javascript, :js
60
+ end
61
+
62
+ def self.text
63
+ ## change to/or add alias e.g. text/texts - why? why not
64
+ ## include html or svg in text-only inscription - why? why not?
65
+ ## include markdown in text-only inscription - why? why not?
66
+ ## make content_type lower case with lower() - why? why not?
67
+ where( content_type: [
68
+ 'text/plain',
69
+ 'text/plain;charset=utf-8',
70
+ 'text/plain;charset=us-ascii',
71
+ 'application/json',
72
+ ])
73
+ end
74
+
75
+ def self.search( q ) ## "full-text" search helper
76
+ ## rename to text_search - why? why not?
77
+ ## auto-sort by num - why? why not?
78
+ joins(:blob).text.where( "content LIKE '%#{q}%'" ).order('num')
79
+ end
80
+
81
+
82
+
21
83
  def self.deploys
22
84
  where_clause =<<SQL
23
85
  content LIKE '%deploy%'
@@ -25,7 +87,7 @@ AND ( content LIKE '%orc-721%'
25
87
  OR content LIKE '%og%')
26
88
  SQL
27
89
 
28
- joins(:blob).where( where_clause ).order( 'num' )
90
+ joins(:blob).text.where( where_clause ).order( 'num' )
29
91
  end
30
92
 
31
93
  def self.deploys_by( slug: )
@@ -36,7 +98,7 @@ AND ( content LIKE '%orc-721%'
36
98
  AND content LIKE '%#{slug}%'
37
99
  SQL
38
100
 
39
- joins(:blob).where( where_clause ).order( 'num' )
101
+ joins(:blob).text.where( where_clause ).order( 'num' )
40
102
  end
41
103
 
42
104
  def self.mints
@@ -46,7 +108,7 @@ AND ( content LIKE '%orc-721%'
46
108
  OR content LIKE '%og%')
47
109
  SQL
48
110
 
49
- joins(:blob).where( where_clause ).order( 'num' )
111
+ joins(:blob).text.where( where_clause ).order( 'num' )
50
112
  end
51
113
 
52
114
  def self.mints_by( slug: )
@@ -57,38 +119,50 @@ AND ( content LIKE '%orc-721%'
57
119
  AND content LIKE '%#{slug}%'
58
120
  SQL
59
121
 
60
- joins(:blob).where( where_clause ).order( 'num' )
122
+ joins(:blob).text.where( where_clause ).order( 'num' )
61
123
  end
62
124
 
125
+
63
126
  def self.sub1k() where( 'num < 1000' ); end
64
127
  def self.sub2k() where( 'num < 2000' ); end
65
128
  def self.sub10k() where( 'num < 10000' ); end
66
129
  def self.sub20k() where( 'num < 20000' ); end
67
130
  def self.sub100k() where( 'num < 100000' ); end
68
131
  def self.sub1m() where( 'num < 1000000' ); end
132
+ def self.sub2m() where( 'num < 2000000' ); end
133
+ def self.sub10m() where( 'num < 10000000' ); end
134
+ def self.sub20m() where( 'num < 20000000' ); end
135
+ def self.sub21m() where( 'num < 21000000' ); end
69
136
 
70
137
 
71
138
  def self.largest
72
139
  order( 'bytes DESC' )
73
140
  end
74
141
 
142
+ def self.address_counts
143
+ group( 'address' )
144
+ .order( Arel.sql( 'COUNT(*) DESC')).count
145
+ end
146
+
75
147
  def self.block_counts
76
- group( 'block' ).count
148
+ group( 'block' )
149
+ .order( 'block').count
77
150
  end
78
151
 
79
152
  def self.block_with_timestamp_counts
80
- group( Arel.sql( "block || ' @ ' || date" )).count
153
+ group( Arel.sql( "block || ' @ ' || date" ))
154
+ .order( Arel.sql( "block || ' @ ' || date" ) ).count
81
155
  end
82
156
 
83
157
  def self.content_type_counts
84
- group( 'content_type' )
158
+ group( 'content_type' )
85
159
  .order( Arel.sql( 'COUNT(*) DESC, content_type')).count
86
160
  end
87
161
 
88
162
 
89
163
  def self.date_counts
90
164
  ## note: strftime is SQLite specific/only!!!
91
- group( Arel.sql("strftime('%Y-%m-%d', date)"))
165
+ group( Arel.sql("strftime('%Y-%m-%d', date)"))
92
166
  .order( Arel.sql("strftime('%Y-%m-%d', date)")).count
93
167
  end
94
168
 
@@ -100,13 +174,14 @@ SQL
100
174
 
101
175
  def self.hour_counts
102
176
  ## note: strftime is SQLite specific/only!!!
103
- group( Arel.sql("strftime('%Y-%m-%d %Hh', date)"))
177
+ group( Arel.sql("strftime('%Y-%m-%d %Hh', date)"))
104
178
  .order( Arel.sql("strftime('%Y-%m-%d %Hh', date)")).count
105
179
  end
106
180
 
107
181
 
108
182
  class << self
109
183
  alias_method :biggest, :largest
184
+ alias_method :counts_by_address, :address_counts
110
185
  alias_method :counts_by_content_type, :content_type_counts
111
186
  alias_method :counts_by_date, :date_counts
112
187
  alias_method :counts_by_day, :date_counts
@@ -117,23 +192,12 @@ SQL
117
192
  end
118
193
 
119
194
 
120
- def self.text
121
- ## note: for now include:
122
- ## - text/plain (all variants)
123
- ## - text/json (all variants)
124
- ## - text/markdown
125
- where( content_type:
126
- ['text/plain',
127
- 'text/plain;charset=utf-8',
128
- 'text/markdown',
129
- 'application/json',
130
- ]
131
- )
132
- end
133
- def self.png() where( content_type: 'image/png' ); end
195
+
134
196
 
135
197
  ###
136
198
  ## add support for ordinals.com api txt (headers format)
199
+ ##
200
+ ## todo/fix: move to importer!!! - why? why not?
137
201
 
138
202
 
139
203
  def self.create_from_api( data ) create( _parse_api( data )); end
@@ -56,6 +56,7 @@ create_table :inscribes, :id => :string do |t|
56
56
 
57
57
  ## "timestamp": "2023-06-01 05:00:57 UTC"
58
58
  ## or use date_utc ???
59
+ ## or change to t.integer AND timestamp or time or epoch(time) - why? why not?
59
60
  t.datetime :date, null: false
60
61
 
61
62
  ##
@@ -99,8 +100,8 @@ create_table :blobs, :id => :string do |t|
99
100
  ## t.string :id, null: false, index: { unique: true, name: 'blob_uuids' }
100
101
 
101
102
  t.binary :content, null: false
102
- t.string :sha256 ## sha256 hash
103
- t.string :md5 ## md5 hash - add why? why not?
103
+ t.string :sha256 ## sha256 hash as hexstring
104
+ t.string :md5 ## md5 hash as hexstring - add why? why not?
104
105
 
105
106
  ## timestamp last
106
107
  t.timestamps
@@ -193,23 +194,5 @@ end # block Schema.define
193
194
 
194
195
  end # method up
195
196
  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
197
 
215
198
  end # module OrdDb
@@ -3,7 +3,7 @@ module Ordlite
3
3
 
4
4
  # sync version w/ sport.db n friends - why? why not?
5
5
  MAJOR = 0 ## todo: namespace inside version or something - why? why not??
6
- MINOR = 2
6
+ MINOR = 3
7
7
  PATCH = 0
8
8
  VERSION = [MAJOR,MINOR,PATCH].join('.')
9
9
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ordlite
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.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-28 00:00:00.000000000 Z
11
+ date: 2023-08-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ordinals
@@ -122,6 +122,20 @@ dependencies:
122
122
  - - ">="
123
123
  - !ruby/object:Gem::Version
124
124
  version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: pixelart
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :runtime
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
125
139
  - !ruby/object:Gem::Dependency
126
140
  name: rdoc
127
141
  requirement: !ruby/object:Gem::Requirement
@@ -173,6 +187,7 @@ files:
173
187
  - lib/ordlite.rb
174
188
  - lib/ordlite/base.rb
175
189
  - lib/ordlite/cache.rb
190
+ - lib/ordlite/factory.rb
176
191
  - lib/ordlite/importer.rb
177
192
  - lib/ordlite/models/blob.rb
178
193
  - lib/ordlite/models/collection.rb