ordlite 0.1.2 → 0.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: 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: {}