schema_comments 0.2.0.alpha5 → 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.
@@ -34,6 +34,13 @@ describe AnnotateModels do
34
34
  class Book < ActiveRecord::Base
35
35
  end
36
36
 
37
+ puts "ActiveRecord::Base.connection.adapter_name: #{ActiveRecord::Base.connection.adapter_name.inspect}"
38
+
39
+ int_expr =
40
+ case ActiveRecord::Base.connection.adapter_name
41
+ when /mysql/i then ":integer(4)"
42
+ else ":integer "
43
+ end
37
44
 
38
45
  AnnotateModels.get_schema_info(Book).should == %{# == Schema Info ==
39
46
  #
@@ -41,9 +48,9 @@ describe AnnotateModels do
41
48
  #
42
49
  # Table name: books # 書籍
43
50
  #
44
- # id :integer not null, primary key
51
+ # id #{int_expr} not null, primary key
45
52
  # title :string(100) not null # タイトル
46
- # size :integer not null, default(1) # 判型
53
+ # size #{int_expr} not null, default(1) # 判型
47
54
  # price :decimal(17, 14) not null, default(0.0) # 価格
48
55
  # created_at :datetime # 登録日時
49
56
  # updated_at :datetime # 更新日時
data/spec/database.yml CHANGED
@@ -1,13 +1,15 @@
1
- sqlite:
2
- :adapter: sqlite
3
- :dbfile: schema_comments_test.sqlite.db
4
1
  sqlite3:
5
2
  :adapter: sqlite3
6
- :database: schema_comments_test.sqlite3.db
3
+ :database: ":memory:"
7
4
  mysql:
8
5
  adapter: mysql
9
6
  encoding: utf8
10
7
  username: root
11
8
  password:
12
- socket: /opt/local/var/run/mysql5/mysqld.sock
13
- database: schema_comments_test
9
+ database: schema_comments_mysql_test
10
+ mysql2:
11
+ adapter: mysql2
12
+ encoding: utf8
13
+ username: root
14
+ password:
15
+ database: schema_comments_mysql2_test
data/spec/fake_app.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # -*- coding: utf-8 -*-
1
2
 
2
3
  # see https://github.com/amatsuda/kaminari/blob/master/spec/fake_app.rb
3
4
 
@@ -6,8 +7,28 @@ require 'action_controller/railtie'
6
7
  require 'action_view/railtie'
7
8
 
8
9
  # database
9
- ActiveRecord::Base.configurations = {'test' => {:adapter => 'sqlite3', :database => ':memory:'}}
10
- ActiveRecord::Base.establish_connection('test')
10
+ db_name = ENV['DB'] || 'sqlite3'
11
+ configs = YAML.load_file(File.expand_path("../database.yml", __FILE__))
12
+ config = configs[db_name]
13
+
14
+ def mysql_creation_options(config)
15
+ @charset = ENV['CHARSET'] || 'utf8'
16
+ @collation = ENV['COLLATION'] || 'utf8_unicode_ci'
17
+ {:charset => (config['charset'] || @charset), :collation => (config['collation'] || @collation)}
18
+ end
19
+
20
+ case db_name
21
+ when /mysql/ then
22
+ ActiveRecord::Base.establish_connection(config.merge('database' => nil))
23
+ begin
24
+ ActiveRecord::Base.connection.create_database(config['database'], mysql_creation_options(config))
25
+ rescue ActiveRecord::StatementInvalid => e
26
+ raise e unless e.message =~ /^Mysql2?::Error: Can't create database|^ActiveRecord::JDBCError: Can't create database/
27
+ end
28
+ end
29
+
30
+ ActiveRecord::Base.configurations = configs
31
+ ActiveRecord::Base.establish_connection( db_name )
11
32
 
12
33
  # config
13
34
  app = Class.new(Rails::Application)
@@ -1,14 +1,14 @@
1
- ---
2
- table_comments:
3
- addresses: "住所"
4
- emails: "メール"
5
- person: ""
6
- column_comments:
7
- addresses:
8
- person_id: ""
9
- descriptions: "記述"
10
- emails:
11
- person_id: ""
12
- address: "アドレス"
13
- person:
14
- name: "名前"
1
+ ---
2
+ table_comments:
3
+ addresses: 住所
4
+ emails: メール
5
+ person: 人
6
+ column_comments:
7
+ addresses:
8
+ person_id: 人
9
+ descriptions: 記述
10
+ emails:
11
+ person_id: 人
12
+ address: アドレス
13
+ person:
14
+ name: 名前
@@ -1,9 +1,9 @@
1
- ---
2
- table_comments:
3
- products: "商品"
4
- column_comments:
5
- products:
6
- product_type_cd: "カテゴリコード"
7
- name: "商品名称"
8
- created_at: "登録日時"
9
- updated_at: "更新日時"
1
+ ---
2
+ table_comments:
3
+ products: 商品
4
+ column_comments:
5
+ products:
6
+ product_type_cd: カテゴリコード
7
+ name: 商品名称
8
+ created_at: 登録日時
9
+ updated_at: 更新日時
@@ -3,8 +3,6 @@ require File.join(File.dirname(__FILE__), '../spec_helper')
3
3
 
4
4
  describe SchemaComments::ConnectionAdapters do
5
5
 
6
- IGNORED_TABLES = %w(schema_migrations)
7
-
8
6
  before(:each) do
9
7
  SchemaComments.yaml_path = File.expand_path(File.join(File.dirname(__FILE__), 'schema_comments.yml'))
10
8
  FileUtils.rm(SchemaComments.yaml_path, :verbose => true) if File.exist?(SchemaComments.yaml_path)
@@ -3,8 +3,6 @@ require File.join(File.dirname(__FILE__), '../spec_helper')
3
3
 
4
4
  describe SchemaComments::SchemaComment do
5
5
 
6
- IGNORED_TABLES = %w(schema_migrations)
7
-
8
6
  before(:each) do
9
7
  SchemaComments.yaml_path = File.expand_path(File.join(File.dirname(__FILE__), 'schema_comments.yml'))
10
8
  FileUtils.rm(SchemaComments.yaml_path, :verbose => true) if File.exist?(SchemaComments.yaml_path)
@@ -1,10 +1,10 @@
1
- ---
2
- table_comments:
3
- products: "商品"
4
- column_comments:
5
- products:
6
- product_type_cd: "種別コード"
7
- price: "価格"
8
- name: "商品名"
9
- created_at: "登録日時"
10
- updated_at: "更新日時"
1
+ ---
2
+ table_comments:
3
+ products: 商品
4
+ column_comments:
5
+ products:
6
+ product_type_cd: 種別コード
7
+ price: 価格
8
+ name: 商品名
9
+ created_at: 登録日時
10
+ updated_at: 更新日時
@@ -1,10 +1,10 @@
1
- ---
2
- table_comments:
3
- products: "商品"
4
- column_comments:
5
- products:
6
- product_type_cd: "カテゴリコード"
7
- name: "商品名"
8
- created_at: "登録日時"
9
- updated_at: "更新日時"
1
+ ---
2
+ table_comments:
3
+ products: 商品
4
+ column_comments:
5
+ products:
6
+ product_type_cd: カテゴリコード
7
+ name: 商品名
8
+ created_at: 登録日時
9
+ updated_at: 更新日時
10
10
  users:
@@ -3,8 +3,6 @@ require File.join(File.dirname(__FILE__), '../spec_helper')
3
3
 
4
4
  describe ActiveRecord::SchemaDumper do
5
5
 
6
- IGNORED_TABLES = %w(schema_migrations)
7
-
8
6
  before(:each) do
9
7
  SchemaComments.yaml_path = File.expand_path(File.join(File.dirname(__FILE__), 'schema_comments.yml'))
10
8
  FileUtils.rm(SchemaComments.yaml_path, :verbose => true) if File.exist?(SchemaComments.yaml_path)
@@ -42,9 +40,10 @@ describe ActiveRecord::SchemaDumper do
42
40
  }
43
41
 
44
42
  dest = StringIO.new
45
- ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, dest)
43
+ # ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, dest)
44
+ SchemaComments::SchemaDumper.dump(ActiveRecord::Base.connection, dest)
46
45
  dest.rewind
47
- dest.read.should == <<EOS
46
+ s = <<EOS
48
47
  # encoding: UTF-8
49
48
  # This file is auto-generated from the current state of the database. Instead
50
49
  # of editing this file, please use the migrations feature of Active Record to
@@ -61,15 +60,32 @@ describe ActiveRecord::SchemaDumper do
61
60
  ActiveRecord::Schema.define(:version => 1) do
62
61
 
63
62
  create_table "products", :force => true, :comment => '商品' do |t|
63
+ EOS
64
+
65
+ if ENV['DB'] =~ /mysql/i
66
+ s << <<EOS
67
+ #t.column "id", "int(11)", :null => false, :comment => "AUTO_INCREMENT PRIMARY KEY by rails"
68
+ t.column "product_type_cd", "varchar(255)", :comment => "種別コード"
69
+ t.column "price", "int(11)", :comment => "価格"
70
+ t.column "name", "varchar(255)", :comment => "商品名"
71
+ t.column "created_at", "datetime", :comment => "登録日時"
72
+ t.column "updated_at", "datetime", :comment => "更新日時"
73
+ EOS
74
+ else
75
+ s << <<EOS
64
76
  t.string "product_type_cd", :comment => "種別コード"
65
77
  t.integer "price", :comment => "価格"
66
78
  t.string "name", :comment => "商品名"
67
79
  t.datetime "created_at", :comment => "登録日時"
68
80
  t.datetime "updated_at", :comment => "更新日時"
81
+ EOS
82
+ end
83
+ s << <<EOS
69
84
  end
70
85
 
71
86
  end
72
87
  EOS
88
+ dest.read.should == s
73
89
  end
74
90
 
75
91
  end
data/spec/spec_helper.rb CHANGED
@@ -1,19 +1,30 @@
1
+ if ENV["COVERAGE"] and not(ENV["COVERAGE"].empty?)
2
+ require "simplecov"
3
+ SimpleCov.start
4
+ end
5
+
6
+ ENV['DB'] ||= 'sqlite3'
7
+
1
8
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
9
  $LOAD_PATH.unshift(File.dirname(__FILE__))
3
10
 
4
11
  require 'rails'
5
12
  require 'schema_comments'
6
13
  # require 'database_cleaner'
14
+ SchemaComments.yaml_path = File.expand_path("schema_comments.yml", File.dirname(__FILE__))
7
15
 
8
16
  # Ensure we use 'syck' instead of 'psych' in 1.9.2
9
17
  # RubyGems >= 1.5.0 uses 'psych' on 1.9.2, but
10
18
  # Psych does not yet support YAML 1.1 merge keys.
11
19
  # Merge keys is often used in mongoid.yml
12
20
  # See: http://redmine.ruby-lang.org/issues/show/4300
13
- if RUBY_VERSION >= '1.9.2'
14
- YAML::ENGINE.yamler = 'syck'
15
- end
16
- require 'yaml_waml'
21
+ # if RUBY_VERSION >= '1.9.2'
22
+
23
+ # if defined?(YAML::ENGINE) && YAML::ENGINE.respond_to?(:yamler=)
24
+ # YAML::ENGINE.yamler = 'syck'
25
+ # end
26
+
27
+ # require 'yaml_waml'
17
28
 
18
29
  require 'fake_app'
19
30
 
@@ -23,6 +34,7 @@ require 'rspec/rails'
23
34
  # in ./support/ and its subdirectories.
24
35
  Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
25
36
 
37
+
26
38
  RSpec.configure do |config|
27
39
 
28
40
  %w(resources/models).each do |path|
@@ -38,3 +50,4 @@ end
38
50
 
39
51
  MIGRATIONS_ROOT = File.join(File.dirname(__FILE__), 'migrations')
40
52
 
53
+ IGNORED_TABLES = %w(schema_migrations)
@@ -34,18 +34,18 @@ describe SchemaComments::SchemaComment do
34
34
  File.read(SchemaComments.yaml_path).split(/$/).map(&:strip).should == %{
35
35
  ---
36
36
  table_comments:
37
- addresses: "住所"
38
- emails: "メール"
39
- person: ""
37
+ addresses: 住所
38
+ emails: メール
39
+ person: 人
40
40
  column_comments:
41
41
  addresses:
42
- person_id: ""
43
- descriptions: "記述"
42
+ person_id: 人
43
+ descriptions: 記述
44
44
  emails:
45
- person_id: ""
46
- address: "アドレス"
45
+ person_id: 人
46
+ address: アドレス
47
47
  person:
48
- name: "名前"
48
+ name: 名前
49
49
  }.split(/$/).map(&:strip)
50
50
  end
51
51
 
metadata CHANGED
@@ -1,19 +1,19 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: schema_comments
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0.alpha5
5
- prerelease: 6
4
+ version: 0.2.0
5
+ prerelease:
6
6
  platform: ruby
7
7
  authors:
8
8
  - akimatter
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-04-18 00:00:00.000000000 Z
12
+ date: 2013-01-28 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
16
- requirement: &2168170080 !ruby/object:Gem::Requirement
16
+ requirement: !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,15 @@ dependencies:
21
21
  version: 3.0.0
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *2168170080
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 3.0.0
25
30
  - !ruby/object:Gem::Dependency
26
31
  name: activerecord
27
- requirement: &2168169580 !ruby/object:Gem::Requirement
32
+ requirement: !ruby/object:Gem::Requirement
28
33
  none: false
29
34
  requirements:
30
35
  - - ! '>='
@@ -32,76 +37,95 @@ dependencies:
32
37
  version: 3.0.0
33
38
  type: :runtime
34
39
  prerelease: false
35
- version_requirements: *2168169580
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: 3.0.0
36
46
  - !ruby/object:Gem::Dependency
37
- name: rspec
38
- requirement: &2168185480 !ruby/object:Gem::Requirement
47
+ name: bundler
48
+ requirement: !ruby/object:Gem::Requirement
39
49
  none: false
40
50
  requirements:
41
- - - ~>
51
+ - - ! '>='
42
52
  - !ruby/object:Gem::Version
43
- version: 2.8.0
53
+ version: '0'
44
54
  type: :development
45
55
  prerelease: false
46
- version_requirements: *2168185480
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
47
62
  - !ruby/object:Gem::Dependency
48
- name: rspec-rails
49
- requirement: &2168184960 !ruby/object:Gem::Requirement
63
+ name: rake
64
+ requirement: !ruby/object:Gem::Requirement
50
65
  none: false
51
66
  requirements:
52
- - - ~>
67
+ - - ! '>='
53
68
  - !ruby/object:Gem::Version
54
- version: 2.8.1
69
+ version: '0'
55
70
  type: :development
56
71
  prerelease: false
57
- version_requirements: *2168184960
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
58
78
  - !ruby/object:Gem::Dependency
59
- name: yard
60
- requirement: &2168184480 !ruby/object:Gem::Requirement
79
+ name: rspec
80
+ requirement: !ruby/object:Gem::Requirement
61
81
  none: false
62
82
  requirements:
63
83
  - - ~>
64
84
  - !ruby/object:Gem::Version
65
- version: '0.7'
85
+ version: 2.10.0
66
86
  type: :development
67
87
  prerelease: false
68
- version_requirements: *2168184480
69
- - !ruby/object:Gem::Dependency
70
- name: rdoc
71
- requirement: &2168183960 !ruby/object:Gem::Requirement
88
+ version_requirements: !ruby/object:Gem::Requirement
72
89
  none: false
73
90
  requirements:
74
91
  - - ~>
75
92
  - !ruby/object:Gem::Version
76
- version: '3.12'
77
- type: :development
78
- prerelease: false
79
- version_requirements: *2168183960
93
+ version: 2.10.0
80
94
  - !ruby/object:Gem::Dependency
81
- name: bundler
82
- requirement: &2168183440 !ruby/object:Gem::Requirement
95
+ name: rspec-rails
96
+ requirement: !ruby/object:Gem::Requirement
83
97
  none: false
84
98
  requirements:
85
99
  - - ~>
86
100
  - !ruby/object:Gem::Version
87
- version: 1.0.0
101
+ version: 2.10.1
88
102
  type: :development
89
103
  prerelease: false
90
- version_requirements: *2168183440
91
- - !ruby/object:Gem::Dependency
92
- name: jeweler
93
- requirement: &2168182960 !ruby/object:Gem::Requirement
104
+ version_requirements: !ruby/object:Gem::Requirement
94
105
  none: false
95
106
  requirements:
96
107
  - - ~>
97
108
  - !ruby/object:Gem::Version
98
- version: 1.8.3
109
+ version: 2.10.1
110
+ - !ruby/object:Gem::Dependency
111
+ name: yard
112
+ requirement: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
99
118
  type: :development
100
119
  prerelease: false
101
- version_requirements: *2168182960
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ! '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
102
126
  - !ruby/object:Gem::Dependency
103
127
  name: simplecov
104
- requirement: &2168182480 !ruby/object:Gem::Requirement
128
+ requirement: !ruby/object:Gem::Requirement
105
129
  none: false
106
130
  requirements:
107
131
  - - ! '>='
@@ -109,10 +133,15 @@ dependencies:
109
133
  version: '0'
110
134
  type: :development
111
135
  prerelease: false
112
- version_requirements: *2168182480
136
+ version_requirements: !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - ! '>='
140
+ - !ruby/object:Gem::Version
141
+ version: '0'
113
142
  - !ruby/object:Gem::Dependency
114
- name: ZenTest
115
- requirement: &2168181980 !ruby/object:Gem::Requirement
143
+ name: autotest
144
+ requirement: !ruby/object:Gem::Requirement
116
145
  none: false
117
146
  requirements:
118
147
  - - ! '>='
@@ -120,7 +149,28 @@ dependencies:
120
149
  version: '0'
121
150
  type: :development
122
151
  prerelease: false
123
- version_requirements: *2168181980
152
+ version_requirements: !ruby/object:Gem::Requirement
153
+ none: false
154
+ requirements:
155
+ - - ! '>='
156
+ - !ruby/object:Gem::Version
157
+ version: '0'
158
+ - !ruby/object:Gem::Dependency
159
+ name: ZenTest
160
+ requirement: !ruby/object:Gem::Requirement
161
+ none: false
162
+ requirements:
163
+ - - '='
164
+ - !ruby/object:Gem::Version
165
+ version: 4.8.3
166
+ type: :development
167
+ prerelease: false
168
+ version_requirements: !ruby/object:Gem::Requirement
169
+ none: false
170
+ requirements:
171
+ - - '='
172
+ - !ruby/object:Gem::Version
173
+ version: 4.8.3
124
174
  description: schema_comments generates extra methods dynamically for attribute which
125
175
  has options
126
176
  email: akm2000@gmail.com
@@ -135,26 +185,24 @@ files:
135
185
  - Gemfile.lock
136
186
  - LICENSE.txt
137
187
  - README.rdoc
138
- - Rakefile
139
188
  - VERSION
140
189
  - init.rb
141
190
  - lib/annotate_models.rb
142
191
  - lib/hash_key_orderable.rb
143
- - lib/schema_comments.rb
144
192
  - lib/schema_comments/base.rb
145
193
  - lib/schema_comments/connection_adapters.rb
146
194
  - lib/schema_comments/migration.rb
147
195
  - lib/schema_comments/migrator.rb
148
196
  - lib/schema_comments/schema.rb
149
197
  - lib/schema_comments/schema_comment.rb
198
+ - lib/schema_comments/schema_dumper/mysql.rb
150
199
  - lib/schema_comments/schema_dumper.rb
151
200
  - lib/schema_comments/task.rb
152
- - schema_comments.gemspec
153
- - spec/.gitignore
201
+ - lib/schema_comments.rb
202
+ - tasks/annotate_models_tasks.rake
203
+ - tasks/schema_comments.rake
154
204
  - spec/annotate_models_spec.rb
155
- - spec/database.yml
156
205
  - spec/fake_app.rb
157
- - spec/fixtures/.gitignore
158
206
  - spec/hash_key_orderable_spec.rb
159
207
  - spec/i18n_export_spec.rb
160
208
  - spec/migration_spec.rb
@@ -166,25 +214,21 @@ files:
166
214
  - spec/migrations/valid/006_change_products_name_with_comment.rb
167
215
  - spec/migrations/valid/007_change_comments.rb
168
216
  - spec/migrations/valid/008_create_users_without_comment.rb
169
- - spec/rcov.opts
170
217
  - spec/resources/models/product.rb
171
218
  - spec/resources/models/product_name.rb
172
219
  - spec/schema.rb
173
- - spec/schema_comments/.gitignore
174
220
  - spec/schema_comments/connection_adapters_spec.rb
175
221
  - spec/schema_comments/schema_comment_spec.rb
176
- - spec/schema_comments/schema_comments_broken_column_comments.yml
177
- - spec/schema_comments/schema_comments_broken_column_hash.yml
178
- - spec/schema_comments/schema_comments_broken_table_comments.yml
179
- - spec/schema_comments/schema_comments_users_without_column_hash.yml
180
222
  - spec/schema_comments/schema_dumper_spec.rb
181
- - spec/spec.opts
182
223
  - spec/spec_helper.rb
183
224
  - spec/yaml_export_spec.rb
184
- - tasks/annotate_models_tasks.rake
185
- - tasks/schema_comments.rake
225
+ - spec/database.yml
186
226
  - spec/human_readable_schema_comments.yml
187
227
  - spec/schema_comments/schema_comments.yml
228
+ - spec/schema_comments/schema_comments_broken_column_comments.yml
229
+ - spec/schema_comments/schema_comments_broken_column_hash.yml
230
+ - spec/schema_comments/schema_comments_broken_table_comments.yml
231
+ - spec/schema_comments/schema_comments_users_without_column_hash.yml
188
232
  - spec/schema_comments.yml
189
233
  homepage: http://github.com/akm/schema_comments
190
234
  licenses:
@@ -201,7 +245,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
201
245
  version: '0'
202
246
  segments:
203
247
  - 0
204
- hash: -464680538847567695
248
+ hash: -2176134516294785649
205
249
  required_rubygems_version: !ruby/object:Gem::Requirement
206
250
  none: false
207
251
  requirements:
@@ -210,7 +254,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
210
254
  version: 1.3.1
211
255
  requirements: []
212
256
  rubyforge_project:
213
- rubygems_version: 1.8.15
257
+ rubygems_version: 1.8.23
214
258
  signing_key:
215
259
  specification_version: 3
216
260
  summary: schema_comments generates extra methods dynamically