ordlite 0.1.2 → 0.2.1

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: 6f1f6f768a785a0badc768db45c1c8ca831958f983b673fe48990846f3d4260e
4
+ data.tar.gz: 234d31cabdc7ed109144487792da9f77c39821f935e3d7d91a151b91e817d511
5
5
  SHA512:
6
- metadata.gz: 1545c336ad9d22d92235bbe7160b3224a311cf5dd06b8a944d18d5d2e80c62a24f342be5ed3b295dd9b99cddd7654f931893f811a9571ef80688956cdff27178
7
- data.tar.gz: 7300eb7a35ddcf56a41e10cdbc8844d81f8340147b18125d62a528d1eb27e4f8854e72ff70ec3858723ce2da99a3105b80f4414ec1706a593e81915b1aec9fb5
6
+ metadata.gz: 403e1addf0b974af179de293a26c5f1f47da8d0f665da1ca1a44a89cbcfb025a642a04930f49f6df35681527b2cbcaae855f87de4b7ce09e6cd88b4cffab1472
7
+ data.tar.gz: ab359d39d1dcdf307e8b3239ed61e2f10f8fc6bfc4316c3f9f5554a9fbc2d88b0d1c24f120afcab9b035c675d82d22e0292c6d1202db7219d64f5e8f2c214b54
data/CHANGELOG.md CHANGED
@@ -1,4 +1,4 @@
1
- ### 0.1.2 / 2023-07-02
1
+ ### 0.2.1
2
2
  ### 0.0.1 / 2023-07-01
3
3
 
4
4
  * Everything is new. First release
data/Manifest.txt CHANGED
@@ -5,8 +5,13 @@ Rakefile
5
5
  lib/ordlite.rb
6
6
  lib/ordlite/base.rb
7
7
  lib/ordlite/cache.rb
8
+ lib/ordlite/factory.rb
9
+ lib/ordlite/importer.rb
8
10
  lib/ordlite/models/blob.rb
11
+ lib/ordlite/models/collection.rb
12
+ lib/ordlite/models/factory.rb
9
13
  lib/ordlite/models/forward.rb
14
+ lib/ordlite/models/generative.rb
10
15
  lib/ordlite/models/inscribe.rb
11
16
  lib/ordlite/schema.rb
12
17
  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 • Factories • Generatives
17
17
 
18
18
 
19
19
  Table Inscribes
@@ -62,97 +62,165 @@ 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 " #{Factory.count} factories"
67
+ puts " #{Generative.count} generative(s)"
65
68
 
66
69
  #=> 0 inscribe(s)
67
70
  #=> 0 blob(s)
71
+ #=> 0 collection(s)
72
+ #=> 0 generative(s)
68
73
  ```
69
74
 
70
75
 
71
76
 
72
77
 
73
- ### Example No 1 - Query Ordgen Deploy / Mint Inscriptions
78
+ ### Example No 1 - Auto-Add (Via Ordinals.com) First Thousand Inscriptions (Sub 1k)
74
79
 
75
80
  ``` ruby
76
81
  require 'ordlite'
77
82
 
78
83
 
79
- OrdDb.connect( adapter: 'sqlite3',
80
- database: './ord.db' )
84
+ OrdDb.open( './ord.db' )
81
85
 
82
86
 
83
- ####################
84
- ## query for deploy candidates
85
- ##
86
- ## e.g. sql where clause like
87
- ## content LIKE '%deploy%'
88
- ## AND ( content LIKE '%orc-721%'
89
- ## OR content LIKE '%og%')
90
- ##
87
+ 1000.times do |num| # auto-add inscription 0-999
88
+ OrdDb.import( num )
89
+ end
91
90
 
92
- deploys = Inscribe.deploys
93
- puts " #{deploys.size} deploy candidate(s)"
91
+ puts
92
+ puts " #{Inscribe.count} inscribe(s)"
93
+ puts " #{Blob.count} blob(s)"
94
+ #=> 1000 inscribe(s)
95
+ #=> 1000 blob(s)
96
+ ```
94
97
 
95
- deploys.each_with_index do |rec,i|
96
- puts "==> deploy #{i} - num #{rec.num} - #{rec.bytes} bytes - #{rec.date}"
97
- puts rec.content
98
+ Let's query for the ten biggest (by bytes) inscriptions
99
+ (and pretty print the result):
100
+
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"
98
107
  end
108
+ ```
99
109
 
110
+ resulting in:
100
111
 
101
- punks_deploys = Inscribe.deploys_by( slug: 'diypunks')
102
- 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
+ ```
103
124
 
104
125
 
126
+ Let's query for all inscriptions grouped by date (day) and dump the results:
105
127
 
106
- #######################
107
- ## query for mint candidates
108
- ##
109
- ## e.g. sql where clause like
110
- ## content LIKE '%mint%'
111
- ## AND ( content LIKE '%orc-721%'
112
- ## OR content LIKE '%og%')
128
+ ```ruby
129
+ pp Inscribe.counts_by_date ## or count_by_day
130
+ ```
113
131
 
114
- mints = Inscribe.mints
115
- puts " #{mints.size} mint candidate(s)"
132
+ resulting in:
116
133
 
117
- ## print last hundred mint candidates
118
- mints[-100,100].each_with_index do |rec,i|
119
- puts "==> mint #{i} - num #{rec.num} - #{rec.bytes} bytes - #{rec.date}"
120
- puts rec.content
121
- 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
+ ```
122
160
 
161
+ Let's query for all inscriptions grouped by month and dump the results:
123
162
 
124
- phunks_mints = Inscribe.mints_by( slug: 'diyphunks')
125
- puts " #{phunks_mints.size} mint candidate(s)"
163
+ ```ruby
164
+ pp Inscribe.counts_by_month
165
+ ```
126
166
 
167
+ resulting in:
168
+
169
+ ```
170
+ {"2022-12" => 3,
171
+ "2023-01" => 445,
172
+ "2023-02" => 552}
173
+ ```
174
+
175
+
176
+ Let's query for all content types and group by count (descending) and dump the results:
127
177
 
128
- puts " #{deploys.size} deploy candidate(s)"
129
- puts " #{mints.size} mint candidate(s)"
130
178
 
131
- #=> 123 deploy candidate(s)
132
- #=> 7453 mint candidate(s)
179
+ ```ruby
180
+ pp Inscribe.counts_by_content_type
181
+ ```
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}
133
202
  ```
134
203
 
204
+ and so on.
135
205
 
136
206
 
137
207
 
138
- ### Bonus: Import (Cached) Inscription Datafiles (& Content)
208
+
209
+ ### Bonus: Import (Cached) Inscription Meta Datafiles (& Content Blobs)
139
210
 
140
211
  Let's import all cached
141
- inscriptions metadata datafiles (& content)
142
- from [/ordinals.cache](https://github.com/ordbase/ordinals.cache)
212
+ inscriptions metadata datafiles (& content blobs)
213
+ from [/ordinals.cache](https://github.com/ordbase/ordinals.cache)
143
214
  into an (sql) database e.g. `ord.db`:
144
215
 
145
216
 
146
217
  ``` ruby
147
218
  require 'ordlite'
148
219
 
149
- OrdDb.connect( adapter: 'sqlite3',
150
- database: './ord.db' )
151
-
152
- OrdDb.create_all # build table schema
220
+ OrdDb.open( './ord.db' )
153
221
 
154
- cache_dir = './ordinals.cache/btc'
155
- cache = OrdDb::Cache.new( cache_dir )
222
+ cache_dir = './ordinals.cache/inscription'
223
+ cache = Ordinals::Cache.new( cache_dir )
156
224
  cache.import_all
157
225
 
158
226
 
@@ -171,9 +239,12 @@ The scripts are dedicated to the public domain.
171
239
  Use it as you please with no restrictions whatsoever.
172
240
 
173
241
 
242
+ ## Questions? Comments?
243
+
244
+ Join us in the [Ordgen / ORC-721 discord (chat server)](https://discord.gg/dDhvHKjm2t). Yes you can.
245
+ Your questions and commetary welcome.
246
+
247
+
248
+ Or post them over at the [Help & Support](https://github.com/geraldb/help) page. Thanks.
174
249
 
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
250
 
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'],
@@ -26,6 +27,7 @@ Hoe.spec 'ordlite' do
26
27
  ['props'],
27
28
  ['props-activerecord'],
28
29
  ['sqlite3'],
30
+ ['pixelart'], ## required for factory (ordgen/orc-721) support for now
29
31
  ]
30
32
 
31
33
  self.licenses = ['Public Domain']
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?
@@ -102,5 +134,13 @@ module OrdDb
102
134
  end # module OrdDb
103
135
 
104
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
+
105
145
  # say hello
106
146
  puts Ordlite.banner ## if defined?($RUBYCOCOS_DEBUG) && $RUBCOCOS_DEBUG
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,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