So, I've read it all:
Best way to load module/class from lib folder in Rails 3?
Rails doesn't load my module from lib
Rails /lib modules and
How to create and use a module using Ruby on Rails 3?
But I can't make it work. Here's my situation:
I have a calc_distance(place1, place2) method and an attribute places to my User model and I want to define a method calc_total_distance in the User model.
I want to access the calc_distance method through a Utils lib and not to load the whole utils when using it.
In /lib/utils.rb
module Utils
def calc_distance a, b
# Blah blah blah
end
end
In /config/application.rb I have:
config.autoload_paths += %W(#{config.root}/lib)
In the console, I can do
include Utils then calc_distance(place1, place2) and it works. But Utils::calc_distance(place1 place2) doesn't work ...
Extra-question is can I do this ?
Then in my User.rb model:
def get_total_distance
# Blah blah blah
dist += calc_distance(place1, place2)
# Blah blah blah
end
returns me undefined method 'include' for #<User:0x00000006368278>
and
def get_total_distance
# Blah blah blah
dist += Utils::calc_distance(place1, place2)
# Blah blah blah
end
returns me undefined method 'calc_distance' for Utils:Module
How can I achieve this, knowing that I really prefer the second method (which as I reckon, doesn't load the whole Utils module ...
If you don't wan't to do mixin, but just to define some Utils' methods, then you can define the methods on module level (using self. prefix):
module Utils
def self.calc_distance a, b
# Blah blah blah
end
end
and then call them this way:
Utils.calc_distance(place1, place2)
instead of
Utils::calc_distance(place1, place2)
class Athlete < ActiveRecord::Base
include Utils
def get_total_distance
# Blah blah blah
dist += calc_distance(place1, place2)
# Blah blah blah
end
end
Related
I'm using https://github.com/kenn/active_flag and https://github.com/chanzuckerberg/sorbet-rails
This is what its rbi looks like:
module ActiveFlag
extend ActiveSupport::Concern
end
class ActiveFlag::Definition
def column; end
def human(key, options = nil); end
def humans; end
def initialize(column, keys, klass); end
def keys; end
def maps; end
def pairs; end
def set_all!(key); end
def to_array(integer); end
def to_i(arg); end
def to_value(instance, integer); end
def unset_all!(key); end
end
class ActiveFlag::Railtie < Rails::Railtie
end
class ActiveFlag::Value < Set
def method_missing(symbol, *args, &block); end
def raw; end
def set!(key, options = nil); end
def set(key); end
def set?(key); end
def to_human; end
def to_s; end
def unset!(key, options = nil); end
def unset(key); end
def unset?(key); end
def with(instance, definition); end
end
module ActiveFlag::ClassMethods
def flag(column, keys); end
end
class ActiveRecord::Base
extend ActiveFlag::ClassMethods
end
The last bit (extending AR::Base) I added, the rest srb rbi gems generated automatically.
To actually use Active Flag, I then do this in my model:
flag :visible_to, [:employee, :manager, :admin]
visible_to is an integer column. Sorbet Rails has already typed it as such:
sig { returns(Integer) }
def visible_to; end
sig { params(value: Integer).void }
def visible_to=(value); end
sig { returns(T::Boolean) }
def visible_to?; end
I could change this definition, but it's an autogenerated file and I assume the changes will get lost. So the next thing I tried was adding a sig directly above where the method gets used:
sig { returns(ActiveFlag::Value) }
def visible_to; end
flag :visible_to, [:employee, :manager, :admin]
The problem here is that Sorbet fails because the method I've "defined" doesn't return anything. Which I know is fine, because it'll get overriden when flag is called (it uses define_method internally), but I don't know how to communicate this to Sorbet.
Returning value that does not conform to method result type https://srb.help/7005
54 | def visible_to; end
^^^^^^^^^^^^^^^^^^^
Expected ActiveFlag::Value
Method visible_to has return type ActiveFlag::Value
54 | def visible_to; end
^^^^^^^^^^^^^^
Got NilClass originating from:
54 | def visible_to; e
So my question is. What's the best way to tell Sorbet that this method will return an ActiveFlag::Value once it gets defined, ideally without making a manual change to an autogenerated file?
btw. I tried to see how types for enum in Rails are handled... it doesn't look like that's been done on sorbet-typed yet. Possibly for the same reason.
When you want to override a generated rbi file you can create a second rbi file for the same class in your workspace and move the methods in there. Sorbet will handle merging multiple definitions files for you. We keep these files in a separate sorbet/custom directory next to the generated files; others keep the rbi files next to the rb files in their application directory.
As to enums, Sorbet does not natively support enum literal types so that's likely why.
You could implement a custom plugin for sorbet-rails to generate methods added by active_flag and remove the sigs generated wrongly. Here is the documentation for it:
https://github.com/chanzuckerberg/sorbet-rails/blob/master/README.md#extending-model-generation-task-with-custom-plugins
I'm trying to make a login in elixir, but when i put this code:
def changeset(model, params \\ :empty) do
model
|> cast(params, ~w(email), [])
|> validate_format(:email, ~r/#/)
end
I keep getting this error:
== Compilation error on file web/models/user.ex ==
** (CompileError) web/models/user.ex:25: definitions with multiple clauses and default values require a header. Instead of:
def foo(:first_clause, b \\ :default) do ... end
def foo(:second_clause, b) do ... end
one should write:
def foo(a, b \\ :default)
def foo(:first_clause, b) do ... end
def foo(:second_clause, b) do ... end
def changeset/2 has multiple clauses and defines defaults in one or more clauses
web/models/user.ex:25: (module)
(stdlib) erl_eval.erl:670: :erl_eval.do_apply/6
(elixir) lib/kernel/parallel_compiler.ex:117: anonymous fn/4 in Kernel.ParallelCompiler.spawn_compilers/1
I understand that i have to put a header, but i don´t know exactly how to fix it, anyone could explain me please?
Your code likely looks something like this:
def changeset(model, params \\ :empty) do
# ...
end
def changeset(model, %{"some" => value}) do
# ...
end
Elixir complains about this, because having multiple clauses with default values might result in ambiguities. To prevent such situations on a syntactic level, Elixir enforces using a separate function header for default values.
As the error message suggests, you need to add a separate function header defining the default value, then write your other clauses without default values:
# This is a function header, whose only purpose is to set
# default values that apply to all subsequent clauses
def changeset(model, params \\ :empty)
def changeset(model, params) do
# ...
end
def changeset(model, %{"some" => value}) do
# ...
end
I've implemented a custom error message processor for Korean language. In Korean, postpositions take different forms depending on the sound of the preceding noun or pronoun.
For example, when marking a subject, ka (가) is used following a vowel and i (이) is used following a consonant.
Examples (hyphens are to denote morpheme boundaries):
Romanization: sakwa-ka ppalkah-ta.
Gloss: Apple-SUBJECT red-PRESENT.
Translation: Apple is red.
Romanization: phainayphul-i tal-ta.
Gloss: Pineapple-SUBJECT sweet-PRESENT.
Translation: Pineapple is sweet.
Therefore, the standard error message system implemented in ActiveModel::Errors is not adequate for Korean. You should either include the attribute in the message making a lot of duplicates ("A is blank", "B is blank", "C is blank", ...), or avoid postpositions after the attribute which is often difficult or makes awkward sentences.
I monkey patched ActiveModel::Errors and altered generate_message to solve this problem. Following is the code (Gist) which is currently in config/initializers in my Rails app.
# encoding: UTF-8
# Original article: http://dailyupgrade.me/post/6806676778/rubyonrails-full-messages-for-korean
# Modified to support more postpositions and client_side_validations gem.
#
# Add Korean translations like this:
# ko:
# errors:
# format: "%{message}"
# messages:
# blank: "%{attribute}((이)) 입력되지 않았습니다"
# taken: "이미 사용 중인 %{attribute}입니다"
# invalid: "잘못된 %{attribute}입니다"
# too_short: "%{attribute}((이)) 너무 짧습니다"
#
class Korean
POSTPOSITIONS = {"은" => "는", "이" => "가", "을" => "를", "과" => "와", "으로" => "로"}
def self.select_postposition(last_letter, postposition)
return postposition unless last_letter >= "가" && last_letter <= "힣"
final = last_letter.mb_chars.last.decompose[2]
if final.nil?
# 받침 없음
POSTPOSITIONS[postposition] || postposition
elsif final == "ㄹ" && (postposition == "으로" || postposition == "로")
# 'ㄹ 받침 + (으)로'를 위한 특별 규칙
"로"
else
# 받침 있음
POSTPOSITIONS.invert[postposition] || postposition
end
end
end
module ActiveModel
class Errors
old_generate_message = instance_method("generate_message")
define_method("generate_message") do |attribute, type = :invalid, options = {}|
msg = old_generate_message.bind(self).(attribute, type, options)
return msg unless I18n.locale == :ko
msg.gsub(/(?<=(.{1}))\(\((은|는|이|가|을|를|과|와|으로|로)\)\)/) do
Korean.select_postposition $1, $2
end
end
end
end
My first question is whether it is possible to achieve the same goal without monkey patching. I'm new to Rails (and Ruby too) so couldn't come up with a better solution.
The second question is about extracting this code from my Rails app and making it into a separate gem. I'm cutting my teeth on developing gems and recently made my first gem. In what place should I put this code in a gem? config/initializers doesn't seem right.
I'm not good at ruby but the following javascript code do the same thing. I hope it may help.
var hasJongsung = function(word) {
return !!(word && word[word.length -1].charCodeAt()>=0xAC00 && word[word.length-1].charCodeAt()<=0xD7A3 && (word[word.length -1].charCodeAt()-0xAC00)%28);
};
source:http://spectrumdig.blogspot.kr/2012/11/unicode-20.html
I have had some difficulties in using extended classes in rails, in particular extending the Matrix class - and I have also asked some questions related to this:
Am I extending this inbuilt ruby class correctly
Using custom functions in rails app
Where do I put this matrix class in my rails app
In general the answers have been around autoloading in rails 3. However, when I extend 'Math' new functions work, when I extend 'Matrix' new functions don't work - even if I treat them in the same way
I've tried many iterations and change module names, requires, autoloads but here are my latest key files:
application.rb:
require File.expand_path('../boot', __FILE__)
require 'rails/all'
require 'matrix'
# If you have a Gemfile, require the gems listed there, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(:default, Rails.env) if defined?(Bundler)
module SimpleFixed
class Application < Rails::Application
# Settings in config/environments/* take precedence over those specified here.
# Application configuration should go into files in config/initializers
# -- all .rb files in that directory are automatically loaded.
# Custom directories with classes and modules you want to be autoloadable.
# **I have tried all these autoload variants**
# config.autoload_paths += %W(#{config.root}/extras)
# config.autoload_paths += %W(#{config.root}/lib)
# config.autoload_paths += Dir["#{config.root}/lib/**/"]
# config.autoload_paths << "#{Rails.root}/lib"
config.autoload_paths << "#{::Rails.root.to_s}/lib" # <-- set path
require "extend_matrix" # <-- forcibly load your matrix extension
*other standard code here*
lib/extend_math.rb
module Math
class << self
def cube_it(num)
num**3
end
def add_five(num)
num+5
end
end
end
lib/extend_matrix.rb
module Extend_matrix **An error is raised if I call this Matrix**
class Matrix
def symmetric?
return false if not square?
(0 ... row_size).each do |i|
(0 .. i).each do |j|
return false if self[i,j] != self[j,i]
end
end
true
end
def cholesky_factor
raise ArgumentError, "must provide symmetric matrix" unless symmetric?
l = Array.new(row_size) {Array.new(row_size, 0)}
(0 ... row_size).each do |k|
(0 ... row_size).each do |i|
if i == k
sum = (0 .. k-1).inject(0.0) {|sum, j| sum + l[k][j] ** 2}
val = Math.sqrt(self[k,k] - sum)
l[k][k] = val
elsif i > k
sum = (0 .. k-1).inject(0.0) {|sum, j| sum + l[i][j] * l[k][j]}
val = (self[k,i] - sum) / l[k][k]
l[i][k] = val
end
end
end
Matrix[*l]
end
end
end
my view page:
<%= Math.add_five(6) %> **works*
<%= Matrix[[25,15,-5],[15,18,0],[-5,0,11]].cholesky_factor %> **doesn't work**
Could it be because Math is a Module in ruby but Matrix is a class? If so, how do I correct for this?
If you have a look at the implementation of Matrix, you will see the reason. For me, it is located in .../ruby192/lib/ruby/1.9.1/matrix.rb. The definition is (omitted all methods):
class Matrix
include Enumerable
include ExceptionForMatrix
...
end
That means that Matrix is not contained in a module. Your source for your additions should begin:
class Matrix
def symmetric?
...
end
def cholesky_factor
...
end
end
So your addition to a class or module has to match the current definition. Matrix is known as Matrix in the Ruby constants, and not as Extend_matrix::Matrix which is what you have defined.
How can you tell if a generate or destroy command has been used to invoke a custom generator?
In Rails 2 you could do this:
if options[:command] == :destroy
...
end
I want to print out some helpful information, but only when rails generate has been called, not when rails destroy is called:
if is_generating
puts "You're generated something!"
end
Thanks.
check the generator class's behavior. It seems you should get either :invoke for generate or :revoke for destroy. For example, I added this:
class PatternGenerator < Rails::Generators::NamedBase
def echo_behavior
p "generate? #{generating?}"
p "destroying? #{destroying?}"
end
protected
def generating?
:invoke == behavior
end
def destroying?
:revoke == behavior
end
end
Running this, I get:
younker % rails g pattern foo
"generate? true"
"destroying? false"
younker % rails destroy pattern foo
"generate? false"
"destroying? true"
Seems to work and makes sense, so that's my final answer.