ordlite 0.1.2 → 0.2.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 238e3e5f3913821c8f0a70bdedb444deeea65da4ecdb5827f239b3b4a682a151
4
- data.tar.gz: d01946f571d34b1b29ca7fe0216898f31fddc75e818d07300129af5d1a808c5e
3
+ metadata.gz: 6051393488775c98fdee4716e6c49b5cd89c6cd404a45f48024a47dea633acfb
4
+ data.tar.gz: d43e1d73f1b49b27ae35e745eaa8e13be394635d2367233bc6c341fd1aa54933
5
5
  SHA512:
6
- metadata.gz: 1545c336ad9d22d92235bbe7160b3224a311cf5dd06b8a944d18d5d2e80c62a24f342be5ed3b295dd9b99cddd7654f931893f811a9571ef80688956cdff27178
7
- data.tar.gz: 7300eb7a35ddcf56a41e10cdbc8844d81f8340147b18125d62a528d1eb27e4f8854e72ff70ec3858723ce2da99a3105b80f4414ec1706a593e81915b1aec9fb5
6
+ metadata.gz: 05e95871ce3a85dd31557e38a4247d5cd38e25406e2ac535a1254e6952e68b8054e8c1a06378b4620ce8168c67d1e4cb7eac50305da9e7e59e2f10152a4aa971
7
+ data.tar.gz: 3fe7780c583d525dd60170f4ec0e996f430722d53899e3edb321d9af67425024c262a253e1cf718ab0b7316f9fbb3ae0a6903b3a82debf3860511a167872a1ab
data/CHANGELOG.md CHANGED
@@ -1,4 +1,4 @@
1
- ### 0.1.2 / 2023-07-02
1
+ ### 0.2.0 / 2023-07-28
2
2
  ### 0.0.1 / 2023-07-01
3
3
 
4
4
  * Everything is new. First release
data/Manifest.txt CHANGED
@@ -5,8 +5,12 @@ Rakefile
5
5
  lib/ordlite.rb
6
6
  lib/ordlite/base.rb
7
7
  lib/ordlite/cache.rb
8
+ lib/ordlite/importer.rb
8
9
  lib/ordlite/models/blob.rb
10
+ lib/ordlite/models/collection.rb
11
+ lib/ordlite/models/factory.rb
9
12
  lib/ordlite/models/forward.rb
13
+ lib/ordlite/models/generative.rb
10
14
  lib/ordlite/models/inscribe.rb
11
15
  lib/ordlite/schema.rb
12
16
  lib/ordlite/version.rb
data/README.md CHANGED
@@ -4,8 +4,8 @@
4
4
  ordlite - ordinals inscription (on bitcoin & co) database let's you query via sql and more
5
5
 
6
6
 
7
- * home :: [github.com/ordbase/generative-orc-721](https://github.com/ordbase/generative-orc-721)
8
- * bugs :: [github.com/ordbase/generative-orc-721/issues](https://github.com/ordbase/generative-orc-721/issues)
7
+ * home :: [github.com/ordbase/ordbase](https://github.com/ordbase/ordbase)
8
+ * bugs :: [github.com/ordbase/ordbase/issues](https://github.com/ordbase/ordbase/issues)
9
9
  * gem :: [rubygems.org/gems/ordlite](https://rubygems.org/gems/ordlite)
10
10
  * rdoc :: [rubydoc.info/gems/ordlite](http://rubydoc.info/gems/ordlite)
11
11
 
@@ -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
- Insribes • Blobs
16
+ Inscribes • Blobs • Collections • Generatives
17
17
 
18
18
 
19
19
  Table Inscribes
@@ -62,9 +62,13 @@ OrdDb.create_all # build table schema
62
62
  puts
63
63
  puts " #{Inscribe.count} inscribe(s)"
64
64
  puts " #{Blob.count} blob(s)"
65
+ puts " #{Collection.count} collection(s)"
66
+ puts " #{Generative.count} generative(s)"
65
67
 
66
68
  #=> 0 inscribe(s)
67
69
  #=> 0 blob(s)
70
+ #=> 0 collection(s)
71
+ #=> 0 generative(s)
68
72
  ```
69
73
 
70
74
 
@@ -135,11 +139,11 @@ puts " #{mints.size} mint candidate(s)"
135
139
 
136
140
 
137
141
 
138
- ### Bonus: Import (Cached) Inscription Datafiles (& Content)
142
+ ### Bonus: Import (Cached) Inscription Meta Datafiles (& Content Blobs)
139
143
 
140
144
  Let's import all cached
141
- inscriptions metadata datafiles (& content)
142
- from [/ordinals.cache](https://github.com/ordbase/ordinals.cache)
145
+ inscriptions metadata datafiles (& content blobs)
146
+ from [/ordinals.cache](https://github.com/ordbase/ordinals.cache)
143
147
  into an (sql) database e.g. `ord.db`:
144
148
 
145
149
 
@@ -151,7 +155,7 @@ OrdDb.connect( adapter: 'sqlite3',
151
155
 
152
156
  OrdDb.create_all # build table schema
153
157
 
154
- cache_dir = './ordinals.cache/btc'
158
+ cache_dir = './ordinals.cache/inscription'
155
159
  cache = OrdDb::Cache.new( cache_dir )
156
160
  cache.import_all
157
161
 
@@ -171,9 +175,12 @@ The scripts are dedicated to the public domain.
171
175
  Use it as you please with no restrictions whatsoever.
172
176
 
173
177
 
178
+ ## Questions? Comments?
179
+
180
+ Join us in the [Ordgen / ORC-721 discord (chat server)](https://discord.gg/dDhvHKjm2t). Yes you can.
181
+ Your questions and commetary welcome.
182
+
183
+
184
+ Or post them over at the [Help & Support](https://github.com/geraldb/help) page. Thanks.
174
185
 
175
- Please post in the #generative-orc-721 channel
176
- in the ordinal punks discord.
177
- For an invite
178
- see <https://twitter.com/OrdinalPunks/status/1620230583711576068>.
179
186
 
data/Rakefile CHANGED
@@ -9,7 +9,7 @@ Hoe.spec 'ordlite' do
9
9
  self.summary = "ordlite - ordinals inscription (on bitcoin & co) database let's you query via sql and more"
10
10
  self.description = summary
11
11
 
12
- self.urls = { home: 'https://github.com/ordbase/generative-orc-721' }
12
+ self.urls = { home: 'https://github.com/ordbase/ordbase' }
13
13
 
14
14
  self.author = 'Gerald Bauer'
15
15
  self.email = 'gerald.bauer@gmail.com'
@@ -19,6 +19,7 @@ Hoe.spec 'ordlite' do
19
19
  self.history_file = 'CHANGELOG.md'
20
20
 
21
21
  self.extra_deps = [
22
+ ['ordinals'],
22
23
  ['activerecord'],
23
24
  ['activerecord-utils'],
24
25
  ['logutils'],
data/lib/ordlite/base.rb CHANGED
@@ -1,10 +1,6 @@
1
1
  # core and stlibs
2
+ require 'ordinals' ## will pull-in cocos & friends
2
3
 
3
- require 'pp'
4
- require 'fileutils'
5
- require 'uri'
6
- require 'json'
7
- require 'yaml'
8
4
 
9
5
  require 'logger' # Note: use for ActiveRecord::Base.logger -- remove/replace later w/ LogUtils::Logger ???
10
6
 
@@ -31,6 +27,9 @@ require_relative 'models/forward'
31
27
 
32
28
  require_relative 'models/inscribe'
33
29
  require_relative 'models/blob'
30
+ require_relative 'models/collection'
31
+ require_relative 'models/factory'
32
+ require_relative 'models/generative'
34
33
 
35
34
 
36
35
  require_relative 'schema'
@@ -38,6 +37,10 @@ require_relative 'schema'
38
37
  require_relative 'cache'
39
38
 
40
39
 
40
+ require_relative 'importer' ## note: require (soft dep) ordinals gems!!!
41
+
42
+
43
+
41
44
 
42
45
  module OrdDb
43
46
 
@@ -53,6 +56,35 @@ module OrdDb
53
56
  OrdDb.create
54
57
  end
55
58
 
59
+ def self.auto_migrate!
60
+ ### todo/fix:
61
+ ## check props table and versions!!!!!
62
+
63
+ # first time? - auto-run db migratation, that is, create db tables
64
+ unless LogDb::Model::Log.table_exists?
65
+ LogDb.create # add logs table
66
+ end
67
+
68
+ unless ConfDb::Model::Prop.table_exists?
69
+ ConfDb.create # add props table
70
+ end
71
+
72
+ unless OrdDb::Model::Inscribe.table_exists?
73
+ OrdDb.create
74
+ end
75
+ end # method auto_migrate!
76
+
77
+
78
+ def self.open( database='./ord.db' ) ## convenience helper for sqlite only
79
+ connect( adapter: 'sqlite3',
80
+ database: database )
81
+
82
+ ## build schema if database new/empty
83
+ auto_migrate!
84
+ end
85
+
86
+
87
+
56
88
  def self.connect( config={} )
57
89
 
58
90
  if config.empty?
data/lib/ordlite/cache.rb CHANGED
@@ -1,123 +1,64 @@
1
+ ###
2
+ ## add (database) import machinery to cache
1
3
 
2
- module OrdDb
3
-
4
-
5
- class Cache
6
-
7
- Inscribe = Model::Inscribe
8
- Blob = Model::Blob
9
-
10
- def initialize( dir='.' )
11
- @dir = dir
12
- end
13
-
14
-
15
-
16
- def import_all
17
- paths = Dir.glob( "#{@dir}/**.json" )
18
- puts " #{paths.size} inscribe datafile(s) found"
4
+ module Ordinals
5
+ class Cache
6
+ Inscribe = OrdDb::Model::Inscribe
7
+ Blob = OrdDb::Model::Blob
19
8
 
9
+ def import_all
10
+ paths = _find_meta
11
+ puts " #{paths.size} inscribe metaddatafile(s) found"
20
12
  paths.each_with_index do |path, i|
21
- puts "==> inscribe #{i+1}/#{paths.size}..."
13
+ ## puts "==> inscribe #{i+1}/#{paths.size}..."
22
14
  data = _read_inscribe( path )
23
-
24
- Inscribe.create( _parse_inscribe( data ))
15
+ id = data['id']
16
+
17
+ ## skip if exists (use update later - why? why not?)
18
+ rec = Inscribe.find_by( id: id )
19
+ if rec
20
+ ## skip - already in db
21
+ print '.'
22
+ else
23
+ print " #{id}" # NEW - add / insert into db"
24
+ rec = Inscribe.create_from_cache( data )
25
+ end
25
26
  end
27
+ puts
26
28
 
27
29
 
28
- paths = Dir.glob( "#{@dir}/content/**.txt" )
29
- puts " #{paths.size} content datafile(s) found"
30
-
30
+ paths = _find_blobs
31
+ puts " #{paths.size} inscribe blob(s) found"
31
32
  paths.each_with_index do |path, i|
32
- puts "==> blob #{i+1}/#{paths.size}..."
33
- content = _read_blob( path )
34
- id = File.basename( path, File.extname( path ))
35
-
36
- Blob.create( id: id,
37
- content: content )
33
+ ## puts "==> blob #{i+1}/#{paths.size}..."
34
+ content = read_blob( path )
35
+ id = File.basename( File.dirname(path)) +
36
+ File.basename( path, File.extname( path ))
37
+
38
+ rec = Blob.find_by( id: id )
39
+ if rec
40
+ ## skip - already in db
41
+ print '.'
42
+ else
43
+ print " #{id}" # NEW - add / insert into db"
44
+ rec = Blob.create( id: id,
45
+ content: content )
46
+ end
38
47
  end
48
+ puts
39
49
  end
40
50
 
41
-
42
51
  def import( id )
43
- data = read( id )
44
- rec = Inscribe.create( _parse_inscribe( data ))
45
- rec
46
- end
47
-
48
- def read( id )
49
- _read_inscribe( "#{@dir}/#{id}.json" )
50
- end
51
-
52
- def _read_inscribe( path )
53
- JSON.parse( _read_text( path ))
54
- end
55
-
56
- def _read_blob( path )
57
- blob = File.open( path, 'rb' ) { |f| f.read }
58
- ## auto force to ASCII-7BIT if not already - why? why not?
59
- blob
60
- end
61
-
62
-
63
- def _read_text( path )
64
- File.open( path, 'r:utf-8' ){ |f| f.read }
65
- end
66
-
67
- def _parse_inscribe( data )
68
- ## num via title
69
- attributes = {
70
- id: data['id'],
71
- num: _title_to_num( data['title'] ),
72
- bytes: _content_length_to_bytes( data['content length'] ),
73
- sat: data['sat'].to_i(10),
74
- content_type: data['content type'],
75
- block: data['genesis height'].to_i(10),
76
- fee: data['genesis fee'].to_i(10),
77
- tx: data['genesis transaction'],
78
- address: data['address'],
79
- output: data['output'],
80
- value: data['output value'].to_i(10),
81
- offset: data['offset'].to_i(10),
82
- # "2023-06-01 05:00:57 UTC"
83
- date: DateTime.strptime( data['timestamp'],
84
- '%Y-%m-%d %H:%M:%S %z')
85
- }
86
-
87
- attributes
88
- end
89
-
90
-
91
-
92
- ## "title": "Inscription 9992615",
93
- TITLE_RX = /^Inscription (?<num>[0-9]+)$/i
94
-
95
- def _title_to_num( str )
96
- if m=TITLE_RX.match( str )
97
- m[:num].to_i(10) ## use base 10
52
+ data = read( id )
53
+ rec = Inscribe.find_by( id: id )
54
+ if rec
55
+ # skip - already in db
98
56
  else
99
- puts "!! ERROR - no inscribe num found in title >#{str}<"
100
- exit 1 ## not found - raise exception - why? why not?
57
+ rec = Inscribe.create_from_cache( data )
101
58
  end
102
59
  end
103
60
 
104
- CONTENT_LENGTH_RX = /^(?<num>[0-9]+) bytes$/i
105
-
106
- def _content_length_to_bytes( str )
107
- if m=CONTENT_LENGTH_RX.match( str )
108
- m[:num].to_i(10) ## use base 10
109
- else
110
- puts "!! ERROR - bytes found in content lenght >#{str}<"
111
- exit 1 ## not found - raise exception - why? why not?
112
- end
113
- end
114
-
115
-
116
-
117
-
118
- end # class Cache
119
- end # module OrdDb
120
-
121
-
122
61
 
62
+ end # class Cache
63
+ end # module Ordinals
123
64
 
@@ -0,0 +1,99 @@
1
+
2
+
3
+ module OrdDb
4
+
5
+ def self.import_collection( path, content: true )
6
+ data = read_json( path )
7
+
8
+ meta = data['collection']
9
+ pp meta
10
+
11
+ name = meta['name']
12
+
13
+ col = Model::Collection.find_by( name: name )
14
+ if col
15
+ puts "!! WARN - collection already in db; delete first to reimport"
16
+ return
17
+ end
18
+
19
+ col = Model::Collection.create(
20
+ name: name,
21
+ desc: meta['description'],
22
+ max: meta['max_supply']
23
+ )
24
+
25
+ items = data['items']
26
+ puts " #{items.size} inscribe id(s)"
27
+ items.each_with_index do |rec,i|
28
+ id = rec['inscription_id']
29
+ name = rec['name']
30
+ puts "==> #{i+1}/#{items.size} @ #{id}..."
31
+
32
+ col.items.create( pos: i,
33
+ inscribe_id: id,
34
+ name: name )
35
+
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
60
+ end
61
+ end
62
+
63
+
64
+ def self.import_csv( path, content: true )
65
+ recs = read_csv( path )
66
+ puts " #{recs.size} inscribe id(s)"
67
+ #=> 1000 inscribe id(s)
68
+
69
+ recs.each_with_index do |rec,i|
70
+ id = rec['id']
71
+ puts "==> #{i+1}/#{rec.size} @ #{id}..."
72
+
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
83
+
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
96
+ end
97
+ end
98
+ end # method self.import_csv
99
+ end # module OrdDb
@@ -4,6 +4,10 @@ module OrdDb
4
4
 
5
5
  class Blob < ActiveRecord::Base
6
6
  belongs_to :inscribe, foreign_key: 'id'
7
+
8
+ def text
9
+ content.force_encoding(Encoding::UTF_8)
10
+ end
7
11
  end # class Blob
8
12
 
9
13
  end # module Model
@@ -0,0 +1,10 @@
1
+ module OrdDb
2
+ module Model
3
+
4
+ class Collection < ActiveRecord::Base
5
+ has_many :items, -> { order('pos') }
6
+ has_many :inscribes, :through => :items
7
+ end # class Collection
8
+
9
+ end # module Model
10
+ end # module OrdDb
@@ -0,0 +1,20 @@
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, -> { order('pos') } ## join table (use habtm - why? why not?)
11
+ has_many :layers, :through => :inscriberefs,
12
+ :source => :inscribe
13
+
14
+ has_many :generatives
15
+ has_many :inscribes, :through => :generatives
16
+ end # class Factory
17
+
18
+ end # module Model
19
+ end # module OrdDb
20
+
@@ -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
- end
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 # OrdDb
40
+ end # module OrdDb
22
41
 
23
42
 
@@ -0,0 +1,12 @@
1
+
2
+ module OrdDb
3
+ module Model
4
+
5
+ class Generative < ActiveRecord::Base
6
+ belongs_to :inscribe, foreign_key: 'id'
7
+ belongs_to :factory
8
+ end # class Generative
9
+
10
+ end # module Model
11
+ end # module OrdDb
12
+
@@ -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,216 @@ 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
+
70
+
71
+ def self.largest
72
+ order( 'bytes DESC' )
73
+ end
74
+
75
+ def self.block_counts
76
+ group( 'block' ).count
77
+ end
78
+
79
+ def self.block_with_timestamp_counts
80
+ group( Arel.sql( "block || ' @ ' || date" )).count
81
+ end
82
+
83
+ def self.content_type_counts
84
+ group( 'content_type' )
85
+ .order( Arel.sql( 'COUNT(*) DESC, content_type')).count
86
+ end
87
+
88
+
89
+ def self.date_counts
90
+ ## note: strftime is SQLite specific/only!!!
91
+ group( Arel.sql("strftime('%Y-%m-%d', date)"))
92
+ .order( Arel.sql("strftime('%Y-%m-%d', date)")).count
93
+ end
94
+
95
+ def self.month_counts
96
+ ## note: strftime is SQLite specific/only!!!
97
+ group( Arel.sql("strftime('%Y-%m', date)"))
98
+ .order( Arel.sql("strftime('%Y-%m', date)")).count
99
+ end
100
+
101
+ def self.hour_counts
102
+ ## note: strftime is SQLite specific/only!!!
103
+ group( Arel.sql("strftime('%Y-%m-%d %Hh', date)"))
104
+ .order( Arel.sql("strftime('%Y-%m-%d %Hh', date)")).count
105
+ end
106
+
107
+
108
+ class << self
109
+ alias_method :biggest, :largest
110
+ alias_method :counts_by_content_type, :content_type_counts
111
+ alias_method :counts_by_date, :date_counts
112
+ alias_method :counts_by_day, :date_counts
113
+ alias_method :counts_by_month, :month_counts
114
+ alias_method :counts_by_hour, :hour_counts
115
+ alias_method :counts_by_block, :block_counts
116
+ alias_method :counts_by_block_with_timestamp, :block_with_timestamp_counts
117
+ end
118
+
119
+
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
134
+
135
+ ###
136
+ ## add support for ordinals.com api txt (headers format)
137
+
138
+
139
+ def self.create_from_api( data ) create( _parse_api( data )); end
140
+ class << self
141
+ alias_method :create_from_cache, :create_from_api ## add alias - why? why not?
142
+ end
143
+
144
+
145
+ def self._parse_api( data ) ## parse api json data
146
+ ## num via title
147
+ attributes = {
148
+ id: data['id'],
149
+ num: _title_to_num( data['title'] ),
150
+ bytes: _content_length_to_bytes( data['content-length'] ),
151
+ sat: data['sat'].to_i(10),
152
+ content_type: data['content-type'],
153
+ block: data['genesis-height'].to_i(10),
154
+ fee: data['genesis-fee'].to_i(10),
155
+ tx: data['genesis-transaction'],
156
+ address: data['address'],
157
+ output: data['output'],
158
+ value: data['output-value'].to_i(10),
159
+ offset: data['offset'].to_i(10),
160
+ # "2023-06-01 05:00:57 UTC"
161
+ date: DateTime.strptime( data['timestamp'],
162
+ '%Y-%m-%d %H:%M:%S %z')
163
+ }
164
+
165
+ attributes
166
+ end
167
+
168
+
169
+ ## "title": "Inscription 9992615",
170
+ TITLE_RX = /^Inscription (?<num>[0-9]+)$/i
171
+
172
+ def self._title_to_num( str )
173
+ if m=TITLE_RX.match( str )
174
+ m[:num].to_i(10) ## use base 10
175
+ else
176
+ puts "!! ERROR - no inscribe num found in title >#{str}<"
177
+ exit 1 ## not found - raise exception - why? why not?
178
+ end
179
+ end
180
+
181
+ CONTENT_LENGTH_RX = /^(?<num>[0-9]+) bytes$/i
182
+
183
+ def self._content_length_to_bytes( str )
184
+ if m=CONTENT_LENGTH_RX.match( str )
185
+ m[:num].to_i(10) ## use base 10
186
+ else
187
+ puts "!! ERROR - bytes found in content lenght >#{str}<"
188
+ exit 1 ## not found - raise exception - why? why not?
189
+ end
190
+ end
191
+
192
+
193
+
194
+ ###
195
+ # instance methods
196
+ def extname
197
+ ## map mime type to file extname
198
+ ## see https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types
199
+ ## for real-world usage, see https://dune.com/dgtl_assets/bitcoin-ordinals-analysis
200
+ ## https://github.com/casey/ord/blob/master/src/media.rs
201
+
202
+ if content_type.start_with?( 'text/plain' )
203
+ '.txt'
204
+ elsif content_type.start_with?( 'text/markdown' )
205
+ '.md'
206
+ elsif content_type.start_with?( 'text/html' )
207
+ '.html'
208
+ elsif content_type.start_with?( 'text/javascript' ) ||
209
+ content_type.start_with?( 'application/javascript' )
210
+ ## note: application/javascript is considered bad practice/legacy
211
+ '.js'
212
+ elsif content_type.start_with?( 'image/png' )
213
+ ## Portable Network Graphics (PNG)
214
+ '.png'
215
+ elsif content_type.start_with?( 'image/jpeg' )
216
+ ## Joint Photographic Expert Group image (JPEG)
217
+ '.jpg' ## use jpeg - why? why not?
218
+ elsif content_type.start_with?( 'image/webp' )
219
+ ## Web Picture format (WEBP)
220
+ '.webp' ## note: no three-letter extension available
221
+ elsif content_type.start_with?( 'image/svg' )
222
+ ## Scalable Vector Graphics (SVG)
223
+ '.svg'
224
+ elsif content_type.start_with?( 'image/gif' )
225
+ ## Graphics Interchange Format (GIF)
226
+ '.gif'
227
+ elsif content_type.start_with?( 'image/avif' )
228
+ ## AV1 Image File Format (AVIF)
229
+ '.avif'
230
+ elsif content_type.start_with?( 'application/epub' )
231
+ '.epub'
232
+ elsif content_type.start_with?( 'application/pdf' )
233
+ '.pdf'
234
+ elsif content_type.start_with?( 'application/json' )
235
+ '.json'
236
+ elsif content_type.start_with?( 'application/pgp-signature' )
237
+ '.sig'
238
+ elsif content_type.start_with?( 'audio/mpeg' )
239
+ '.mp3'
240
+ elsif content_type.start_with?( 'audio/midi' )
241
+ '.midi'
242
+ elsif content_type.start_with?( 'video/mp4' )
243
+ '.mp4'
244
+ elsif content_type.start_with?( 'video/webm' )
245
+ '.wepm'
246
+ elsif content_type.start_with?( 'audio/mod' )
247
+ ## is typo? possible? only one inscription in 20m?
248
+ '.mod' ## check/todo/fix if is .wav??
249
+ else
250
+ puts "!! ERROR - no file extension configured for content type >#{content_type}<; sorry:"
251
+ pp self
252
+ exit 1
253
+ end
254
+ end
255
+
256
+ def export_path ## default export path
257
+ numstr = "%08d" % num ### e.g. 00000001
258
+ "./tmp/#{numstr}#{extname}"
259
+ end
260
+ def export( path=export_path )
261
+ if blob
262
+ write_blob( path, blob.content )
263
+ else
264
+ ## todo/fix: raise exception - no content
265
+ puts "!! ERROR - inscribe has no content (blob); sorry:"
266
+ pp self
267
+ exit 1
268
+ end
269
+ end
270
+
271
+
57
272
  end # class Inscribe
58
273
 
59
274
  end # module Model
@@ -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 at last
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 at last
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
@@ -3,8 +3,8 @@ 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 = 1
7
- PATCH = 2
6
+ MINOR = 2
7
+ PATCH = 0
8
8
  VERSION = [MAJOR,MINOR,PATCH].join('.')
9
9
 
10
10
  def self.version
data/lib/ordlite.rb CHANGED
@@ -2,11 +2,21 @@
2
2
  require_relative 'ordlite/base'
3
3
 
4
4
 
5
+
6
+
7
+
5
8
  ## add convenience helpers
6
9
  ## use require 'ordlite/base' if you do NOT want automagic aliases
7
10
 
8
11
 
9
- Inscribe = OrdDb::Model::Inscribe
10
- Blob = OrdDb::Model::Blob
12
+ Inscribe = OrdDb::Model::Inscribe
13
+ Blob = OrdDb::Model::Blob
14
+ Collection = OrdDb::Model::Collection
15
+ Factory = OrdDb::Model::Factory
16
+ Generative = OrdDb::Model::Generative
17
+
18
+
19
+ require 'active_support/number_helper'
20
+ include ActiveSupport::NumberHelper ## e.g. number_to_human_size
11
21
 
12
22
 
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ordlite
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.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-02 00:00:00.000000000 Z
11
+ date: 2023-07-28 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: ordinals
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: activerecord
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -159,12 +173,16 @@ files:
159
173
  - lib/ordlite.rb
160
174
  - lib/ordlite/base.rb
161
175
  - lib/ordlite/cache.rb
176
+ - lib/ordlite/importer.rb
162
177
  - lib/ordlite/models/blob.rb
178
+ - lib/ordlite/models/collection.rb
179
+ - lib/ordlite/models/factory.rb
163
180
  - lib/ordlite/models/forward.rb
181
+ - lib/ordlite/models/generative.rb
164
182
  - lib/ordlite/models/inscribe.rb
165
183
  - lib/ordlite/schema.rb
166
184
  - lib/ordlite/version.rb
167
- homepage: https://github.com/ordbase/generative-orc-721
185
+ homepage: https://github.com/ordbase/ordbase
168
186
  licenses:
169
187
  - Public Domain
170
188
  metadata: {}