respanol 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +50 -0
  3. data/.rspec +2 -0
  4. data/Gemfile +15 -0
  5. data/Gemfile.lock +95 -0
  6. data/Guardfile +122 -0
  7. data/README.md +21 -0
  8. data/lib/respanol/ano.rb +8 -0
  9. data/lib/respanol/examenes/ano_examen.rb +20 -0
  10. data/lib/respanol/examenes/conjugacion_examen.rb +20 -0
  11. data/lib/respanol/examenes/examen_base.rb +53 -0
  12. data/lib/respanol/examenes/fecha_examen.rb +22 -0
  13. data/lib/respanol/examenes/hora_examen.rb +25 -0
  14. data/lib/respanol/examenes/numero_examen.rb +38 -0
  15. data/lib/respanol/examenes/semana_examen.rb +20 -0
  16. data/lib/respanol/fecha.rb +12 -0
  17. data/lib/respanol/hora.rb +43 -0
  18. data/lib/respanol/impresion.rb +23 -0
  19. data/lib/respanol/numero.rb +102 -0
  20. data/lib/respanol/pronombre.rb +81 -0
  21. data/lib/respanol/semana.rb +14 -0
  22. data/lib/respanol/traducir.rb +32 -0
  23. data/lib/respanol/traductor.rb +15 -0
  24. data/lib/respanol/verbos/aprender.rb +7 -0
  25. data/lib/respanol/verbos/caminar.rb +7 -0
  26. data/lib/respanol/verbos/dormir.rb +7 -0
  27. data/lib/respanol/verbos/entender.rb +7 -0
  28. data/lib/respanol/verbos/hablar.rb +7 -0
  29. data/lib/respanol/verbos/hacer.rb +7 -0
  30. data/lib/respanol/verbos/jugar.rb +7 -0
  31. data/lib/respanol/verbos/leer.rb +7 -0
  32. data/lib/respanol/verbos/poder.rb +7 -0
  33. data/lib/respanol/verbos/ser.rb +7 -0
  34. data/lib/respanol/verbos/subir.rb +7 -0
  35. data/lib/respanol/verbos/tener.rb +7 -0
  36. data/lib/respanol/verbos/tocar.rb +7 -0
  37. data/lib/respanol/verbos/verbo_base.rb +7 -0
  38. data/lib/respanol/verbos/viajar.rb +7 -0
  39. data/lib/respanol/verbos/vivir.rb +7 -0
  40. data/lib/respanol/version.rb +3 -0
  41. data/lib/respanol.rb +11 -0
  42. data/respanol.gemspec +30 -0
  43. data/spec/respanol/fecha_spec.rb +7 -0
  44. data/spec/respanol/hora_spec.rb +28 -0
  45. data/spec/respanol/impresion_spec.rb +15 -0
  46. data/spec/respanol/numero_spec.rb +99 -0
  47. data/spec/respanol_spec.rb +0 -0
  48. data/spec/spec_helper.rb +110 -0
  49. metadata +181 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 3deb9c078f27c7b848676d994ed1d8f65b1fba9e
4
+ data.tar.gz: c6712d1856f68e8bc8f65213d8478600f5e42524
5
+ SHA512:
6
+ metadata.gz: 817314e47c56d2600b852571f5eb5a9d013ca2b12196b9df5de786a9632d23351cce6ce79ee335ff8b28c47f5f44e0b48618ac7362c99056f19636fe5d3ccb6a
7
+ data.tar.gz: 76d104905279b24c37b06256b7c1b957588f0816210fea0171d54230145d6756717afeafa508b0d3b2fa67eaeed321f48fbfb9df216582925a6a6ebed4bc034b
data/.gitignore ADDED
@@ -0,0 +1,50 @@
1
+ *.gem
2
+ *.rbc
3
+ /.config
4
+ /coverage/
5
+ /InstalledFiles
6
+ /pkg/
7
+ /spec/reports/
8
+ /spec/examples.txt
9
+ /test/tmp/
10
+ /test/version_tmp/
11
+ /tmp/
12
+
13
+ # Used by dotenv library to load environment variables.
14
+ # .env
15
+
16
+ ## Specific to RubyMotion:
17
+ .dat*
18
+ .repl_history
19
+ build/
20
+ *.bridgesupport
21
+ build-iPhoneOS/
22
+ build-iPhoneSimulator/
23
+
24
+ ## Specific to RubyMotion (use of CocoaPods):
25
+ #
26
+ # We recommend against adding the Pods directory to your .gitignore. However
27
+ # you should judge for yourself, the pros and cons are mentioned at:
28
+ # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
29
+ #
30
+ # vendor/Pods/
31
+
32
+ ## Documentation cache and generated files:
33
+ /.yardoc/
34
+ /_yardoc/
35
+ /doc/
36
+ /rdoc/
37
+
38
+ ## Environment normalization:
39
+ /.bundle/
40
+ /vendor/bundle
41
+ /lib/bundler/man/
42
+
43
+ # for a library or gem, you might want to ignore these files since the code is
44
+ # intended to run in multiple environments; otherwise, check them in:
45
+ # Gemfile.lock
46
+ # .ruby-version
47
+ # .ruby-gemset
48
+
49
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
50
+ .rvmrc
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/Gemfile ADDED
@@ -0,0 +1,15 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
3
+
4
+ gem 'glosbe'
5
+
6
+ group :development do
7
+ gem 'guard'
8
+ gem 'guard-bundler'
9
+ gem 'guard-rspec'
10
+ end
11
+
12
+ group :test do
13
+ gem 'rspec'
14
+ gem 'simplecov', require: false
15
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,95 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ respanol (0.0.1)
5
+ glosbe (>= 0.0.10)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ coderay (1.1.1)
11
+ diff-lcs (1.3)
12
+ docile (1.1.5)
13
+ ffi (1.9.18)
14
+ formatador (0.2.5)
15
+ glosbe (0.0.10)
16
+ htmlentities (= 4.3.2)
17
+ httparty (= 0.13.1)
18
+ guard (2.14.1)
19
+ formatador (>= 0.2.4)
20
+ listen (>= 2.7, < 4.0)
21
+ lumberjack (~> 1.0)
22
+ nenv (~> 0.1)
23
+ notiffany (~> 0.0)
24
+ pry (>= 0.9.12)
25
+ shellany (~> 0.0)
26
+ thor (>= 0.18.1)
27
+ guard-bundler (2.1.0)
28
+ bundler (~> 1.0)
29
+ guard (~> 2.2)
30
+ guard-compat (~> 1.1)
31
+ guard-compat (1.2.1)
32
+ guard-rspec (4.7.3)
33
+ guard (~> 2.1)
34
+ guard-compat (~> 1.1)
35
+ rspec (>= 2.99.0, < 4.0)
36
+ htmlentities (4.3.2)
37
+ httparty (0.13.1)
38
+ json (~> 1.8)
39
+ multi_xml (>= 0.5.2)
40
+ json (1.8.6)
41
+ listen (3.1.5)
42
+ rb-fsevent (~> 0.9, >= 0.9.4)
43
+ rb-inotify (~> 0.9, >= 0.9.7)
44
+ ruby_dep (~> 1.2)
45
+ lumberjack (1.0.11)
46
+ method_source (0.8.2)
47
+ multi_xml (0.6.0)
48
+ nenv (0.3.0)
49
+ notiffany (0.1.1)
50
+ nenv (~> 0.1)
51
+ shellany (~> 0.0)
52
+ pry (0.10.4)
53
+ coderay (~> 1.1.0)
54
+ method_source (~> 0.8.1)
55
+ slop (~> 3.4)
56
+ rb-fsevent (0.9.8)
57
+ rb-inotify (0.9.8)
58
+ ffi (>= 0.5.0)
59
+ rspec (3.5.0)
60
+ rspec-core (~> 3.5.0)
61
+ rspec-expectations (~> 3.5.0)
62
+ rspec-mocks (~> 3.5.0)
63
+ rspec-core (3.5.4)
64
+ rspec-support (~> 3.5.0)
65
+ rspec-expectations (3.5.0)
66
+ diff-lcs (>= 1.2.0, < 2.0)
67
+ rspec-support (~> 3.5.0)
68
+ rspec-mocks (3.5.0)
69
+ diff-lcs (>= 1.2.0, < 2.0)
70
+ rspec-support (~> 3.5.0)
71
+ rspec-support (3.5.0)
72
+ ruby_dep (1.5.0)
73
+ shellany (0.0.1)
74
+ simplecov (0.14.1)
75
+ docile (~> 1.1.0)
76
+ json (>= 1.8, < 3)
77
+ simplecov-html (~> 0.10.0)
78
+ simplecov-html (0.10.0)
79
+ slop (3.6.0)
80
+ thor (0.19.4)
81
+
82
+ PLATFORMS
83
+ ruby
84
+
85
+ DEPENDENCIES
86
+ glosbe
87
+ guard
88
+ guard-bundler
89
+ guard-rspec
90
+ respanol!
91
+ rspec
92
+ simplecov
93
+
94
+ BUNDLED WITH
95
+ 1.14.6
data/Guardfile ADDED
@@ -0,0 +1,122 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ ## Uncomment and set this to only include directories you want to watch
5
+ # directories %w(app lib config test spec features) \
6
+ # .select{|d| Dir.exists?(d) ? d : UI.warning("Directory #{d} does not exist")}
7
+
8
+ ## Note: if you are using the `directories` clause above and you are not
9
+ ## watching the project directory ('.'), then you will want to move
10
+ ## the Guardfile to a watched dir and symlink it back, e.g.
11
+ #
12
+ # $ mkdir config
13
+ # $ mv Guardfile config/
14
+ # $ ln -s config/Guardfile .
15
+ #
16
+ # and, you'll have to watch "config/Guardfile" instead of "Guardfile"
17
+
18
+ module ::Guard
19
+ class Espanol < Plugin
20
+ def initialize(options = {})
21
+ super
22
+ end
23
+
24
+ def start
25
+ require 'respanol'
26
+ end
27
+
28
+ def reload
29
+ #Object.send(:remove_const, :EstudiarEspanol)
30
+ load './lib/respanol.rb'
31
+ Guard::UI.info 'REspanol Reloaded'
32
+ end
33
+
34
+ def run_on_additions(paths)
35
+ paths.each do |p|
36
+ load p
37
+ Guard::UI.info "Loaded #{p}"
38
+ end
39
+ end
40
+
41
+ def run_on_modifications(paths)
42
+ paths.each do |p|
43
+ load p
44
+ Guard::UI.info "Loaded #{p}"
45
+ end
46
+ end
47
+
48
+ def run_on_removals(paths)
49
+ reload
50
+ end
51
+ end
52
+ end
53
+
54
+ guard :espanol do
55
+ watch(%r{lib/(.*)\.rb}) { |m| "#{m[1]}.rb" }
56
+ end
57
+
58
+ guard :bundler do
59
+ require 'guard/bundler'
60
+ require 'guard/bundler/verify'
61
+ helper = Guard::Bundler::Verify.new
62
+
63
+ files = ['Gemfile']
64
+ files += Dir['*.gemspec'] if files.any? { |f| helper.uses_gemspec?(f) }
65
+
66
+ # Assume files are symlinked from somewhere
67
+ files.each { |file| watch(helper.real_path(file)) }
68
+ end
69
+
70
+ # Note: The cmd option is now required due to the increasing number of ways
71
+ # rspec may be run, below are examples of the most common uses.
72
+ # * bundler: 'bundle exec rspec'
73
+ # * bundler binstubs: 'bin/rspec'
74
+ # * spring: 'bin/rspec' (This will use spring if running and you have
75
+ # installed the spring binstubs per the docs)
76
+ # * zeus: 'zeus rspec' (requires the server to be started separately)
77
+ # * 'just' rspec: 'rspec'
78
+
79
+ guard :rspec, cmd: "bundle exec rspec" do
80
+ require "guard/rspec/dsl"
81
+ dsl = Guard::RSpec::Dsl.new(self)
82
+
83
+ # Feel free to open issues for suggestions and improvements
84
+
85
+ # RSpec files
86
+ rspec = dsl.rspec
87
+ watch(rspec.spec_helper) { rspec.spec_dir }
88
+ watch(rspec.spec_support) { rspec.spec_dir }
89
+ watch(rspec.spec_files)
90
+
91
+ # Ruby files
92
+ ruby = dsl.ruby
93
+ dsl.watch_spec_files_for(ruby.lib_files)
94
+
95
+ # Rails files
96
+ #rails = dsl.rails(view_extensions: %w(erb haml slim))
97
+ #dsl.watch_spec_files_for(rails.app_files)
98
+ #dsl.watch_spec_files_for(rails.views)
99
+
100
+ #watch(rails.controllers) do |m|
101
+ # [
102
+ # rspec.spec.call("routing/#{m[1]}_routing"),
103
+ # rspec.spec.call("controllers/#{m[1]}_controller"),
104
+ # rspec.spec.call("acceptance/#{m[1]}")
105
+ # ]
106
+ #end
107
+
108
+ # Rails config changes
109
+ #watch(rails.spec_helper) { rspec.spec_dir }
110
+ #watch(rails.routes) { "#{rspec.spec_dir}/routing" }
111
+ #watch(rails.app_controller) { "#{rspec.spec_dir}/controllers" }
112
+
113
+ # Capybara features specs
114
+ #watch(rails.view_dirs) { |m| rspec.spec.call("features/#{m[1]}") }
115
+ #watch(rails.layouts) { |m| rspec.spec.call("features/#{m[1]}") }
116
+
117
+ # Turnip features and steps
118
+ #watch(%r{^spec/acceptance/(.+)\.feature$})
119
+ #watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) do |m|
120
+ # Dir[File.join("**/#{m[1]}.feature")][0] || "spec/acceptance"
121
+ #end
122
+ end
data/README.md ADDED
@@ -0,0 +1,21 @@
1
+ # Respanol
2
+ Some ruby scripts to help learning Espanol.
3
+ These scripts are developed while I started studying Spanish.
4
+
5
+ **IN PROGRESS**: some might not be correct for whole situations but so far it's coped with what I've learnt
6
+
7
+ # What's included?
8
+ These scripts test your Espanol knownledge in many aspects:
9
+
10
+ * Get word definition
11
+ * Number
12
+ * Days of the week
13
+ * Months of the year
14
+ * Verb Conjugations
15
+
16
+ # How to run
17
+ You should have installed Ruby. Then bundle to install gems `bundle install`.
18
+
19
+ Then include library in irb: `irb -I lib -r espanol`. Enjoy studying!!!
20
+
21
+ I will published this as a gem when I feel it's ready.
@@ -0,0 +1,8 @@
1
+ module Respanol
2
+ class Ano
3
+ MESES = %W{ #{''}
4
+ Enero Febrero Marzo Abril Mayo Junio
5
+ Julio Agosto Septiembre Octubre Noviembre Diciembre
6
+ }
7
+ end
8
+ end
@@ -0,0 +1,20 @@
1
+ module Respanol
2
+ module Examen
3
+ class AnoExamen < ExamenBase
4
+ def leer
5
+ mes = Ano::MESES.sample
6
+ ensayar("#{mes} indice in Vietnamita es ") do |v|
7
+ v.to_i == (Ano::MESES.find_index(mes))
8
+ end
9
+ end
10
+
11
+ def escribir
12
+ indices = (1..(Ano::MESES.size - 1)).to_a
13
+ indice = indices.sample
14
+ ensayar("Mes indice #{indice} es ") do |v|
15
+ Ano::MESES[indice].downcase == v.downcase
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ module Respanol
2
+ module Examen
3
+ class ConjugacionExamen < ExamenBase
4
+ def initialize(verbos = nil)
5
+ @verbos = Array(verbos)
6
+ end
7
+
8
+ def conjugado(klase = nil)
9
+ klases = @verbos
10
+ klases = klases | Array(klase) unless klase.nil?
11
+ kl = klases.sample
12
+ pro = Pronombre.todos.sample
13
+ kl.prefijo
14
+ ensayar("#{pro} ") do |v|
15
+ kl::CONJUGACION.find_index(v) == Pronombre.verbo_indice(pro)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,53 @@
1
+ module Respanol
2
+ module Examen
3
+ class ExamenBase
4
+ include ::Respanol::Impresion
5
+
6
+ GAZA_CONDE = 50
7
+
8
+ def self.gaza(conde = GAZA_CONDE, instancia = nil)
9
+ comienzo = Time.now
10
+ res = conde.times.map do |c|
11
+ print ":#{c} "
12
+ yield(instancia || self.new)
13
+ end.select { |r| r != false }.count
14
+ puts "Exito: #{res} / #{conde}. En #{(Time.now - comienzo).round} segundos!"
15
+
16
+ res
17
+ end
18
+
19
+ def gaza(conde = GAZA_CONDE)
20
+ self.class.gaza(conde, self, &Proc.new)
21
+ end
22
+
23
+ def ensayar(*args)
24
+ print "> "
25
+ exito = true
26
+ loop do
27
+ break if yield(pedir(*args))
28
+ decir_que_no
29
+ exito = false
30
+ end
31
+ felicitar
32
+
33
+ exito
34
+ end
35
+
36
+ def pedir(*args)
37
+ Readline.readline(*args)
38
+ end
39
+
40
+ def optener
41
+ $stdin.gets.chomp
42
+ end
43
+
44
+ def decir_que_no
45
+ puts " => No!"
46
+ end
47
+
48
+ def felicitar
49
+ puts " => Muy bien!"
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,22 @@
1
+ module Respanol
2
+ module Examen
3
+ class FechaExamen < ExamenBase
4
+ def leer(fecha_rango = (Date.civil(1900,1,1)..Date.today))
5
+ fe = rand(fecha_rango)
6
+ ensayar("#{Fecha.fecha_en_palabras(fe)} : ") do |v|
7
+ m = /(\d+)[^\d]+(\d+)[^\d]+(\d+)/.match(v)
8
+ if m
9
+ m[1] == fe.year.to_s && m[2] == fe.month.to_s && m[3] == fe.day.to_s
10
+ end
11
+ end
12
+ end
13
+
14
+ def escribir(fecha_rango = (Date.civil(1900,1,1)..Date.today))
15
+ fe = rand(fecha_rango)
16
+ ensayar("Fecha #{fe.strftime('%A %Y-%m-%d')}: ") do |v|
17
+ v.gsub(/\s+/, ' ').strip.downcase == Fecha.fecha_en_palabras(fe).downcase
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,25 @@
1
+ module Respanol
2
+ module Examen
3
+ class HoraExamen < ExamenBase
4
+ def leer(from = 0, to = 24)
5
+ fe = Date.today
6
+ ho = Time.local(fe.year, fe.month, fe.day, rand(from..to), rand(0..60), rand(0..60))
7
+ ensayar("#{Hora.responder_hora_en_palabras(ho)} = ") do |v|
8
+ m = /(\d+)[^\d]+(\d+)/.match(v)
9
+ if m
10
+ m[1].to_i == (ho.hour % 12) && m[2].to_i == ho.min
11
+ end
12
+ end
13
+ end
14
+
15
+ def escribir(from = 0, to = 24)
16
+ fe = Date.today
17
+ ho = Time.local(fe.year, fe.month, fe.day, rand(from..to), rand(0..60), rand(0..60))
18
+ ensayar("#{ho.strftime('%I:%M:%S %p')} = ") do |v|
19
+ v.gsub(/\s+/, ' ').strip.downcase ==
20
+ Hora.responder_hora_en_palabras(ho).downcase
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,38 @@
1
+ module Respanol
2
+ module Examen
3
+ class NumeroExamen < ExamenBase
4
+ attr_accessor :klase
5
+
6
+ MAXIMO = 100
7
+
8
+ def initialize(feminino = false)
9
+ @klase = feminino ? FemininoNumero : Numero
10
+ end
11
+
12
+ def feminino
13
+ dup = self.dup
14
+ dup.klase = FemininoNumero
15
+ dup
16
+ end
17
+
18
+ def feminino!
19
+ self.klase = FemininoNumero
20
+ self
21
+ end
22
+
23
+ def leer(rango = (1...MAXIMO))
24
+ num = rand(rango)
25
+ ensayar("#{@klase.numero_en_palabras(num)} = ") do |v|
26
+ v.to_i == num
27
+ end
28
+ end
29
+
30
+ def escribir(rango = (1...MAXIMO))
31
+ num = rand(rango)
32
+ ensayar("#{num} = ") do |s|
33
+ s.gsub(/\s+/, ' ').strip == @klase.numero_en_palabras(num)
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,20 @@
1
+ module Respanol
2
+ module Examen
3
+ class SemanaExamen < ExamenBase
4
+ def leer
5
+ dia = Semana::DIAS.sample
6
+ ensayar("#{dia} indice in Vietnamita es ") do |v|
7
+ v.to_i == (Semana::DIAS.find_index(dia) + 1)
8
+ end
9
+ end
10
+
11
+ def escribir
12
+ indices = (0..(Semana::DIAS.size - 2)).to_a
13
+ indice = indices.sample
14
+ ensayar("dia indice #{indice + 1} es ") do |v|
15
+ Semana::DIAS[indice].downcase == v.downcase
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,12 @@
1
+ module Respanol
2
+ class Fecha
3
+ def self.fecha_en_palabras(fecha)
4
+ dia = Semana::DIAS[fecha.wday]
5
+ fecha_numero = Numero.numero_en_palabras(fecha.mday)
6
+ mes = Ano::MESES[fecha.month]
7
+ ano = Numero.numero_en_palabras(fecha.year)
8
+
9
+ [dia, fecha_numero, 'de', mes, 'de', ano].join(' ')
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,43 @@
1
+ module Respanol
2
+ class Hora
3
+ def self.responder_hora_en_palabras(hora)
4
+ "#{comienzo(hora)} #{hora_en_palabras(hora)}"
5
+ end
6
+
7
+ def self.comienzo(hora)
8
+ h = hora.hour % 12
9
+ m = hora.min
10
+ if h == 1 && m <= 30
11
+ "Es la"
12
+ else
13
+ "Son las"
14
+ end
15
+ end
16
+
17
+ def self.hora_en_palabras(hora)
18
+ h = hora.hour % 12
19
+ m = hora.min
20
+ s_h = if m <= 30
21
+ FemininoNumero.numero_en_palabras(h)
22
+ else
23
+ FemininoNumero.numero_en_palabras(h + 1)
24
+ end
25
+ s_m = case m
26
+ when 0
27
+ 'en punto'
28
+ when 15
29
+ 'y cuarto'
30
+ when 30
31
+ 'y media'
32
+ else
33
+ if m < 30
34
+ "y #{FemininoNumero.numero_en_palabras(m)}"
35
+ else
36
+ "menos #{FemininoNumero.numero_en_palabras(60 - m)}"
37
+ end
38
+ end
39
+
40
+ "#{s_h} #{s_m}"
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,23 @@
1
+ module Respanol
2
+ module Impresion
3
+ module ClassMethods
4
+ def prefijo
5
+ print "#{self.name.split('::').last}:: "
6
+ end
7
+
8
+ def impresion(*args)
9
+ prefijo
10
+ print(*args)
11
+ end
12
+ end
13
+
14
+ module InstanceMethods
15
+
16
+ end
17
+
18
+ def self.included(receiver)
19
+ receiver.extend ClassMethods
20
+ receiver.send :include, InstanceMethods
21
+ end
22
+ end
23
+ end