ordlite 0.1.2 → 0.2.1

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: 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