📜 ⬆️ ⬇️

RailsStuff - development kit on rails

image

We recently published the RailsStuff gem. This is a collection of small modules and utilities for performing a variety of common tasks: from organizing controllers and generating unique random values ​​to the parameter parser and translation helpers. In this post I will tell about some of them:


Installation


Most of the modules are too small to be a separate gem, and for convenience it was decided to combine them into one gem. It uses lazy loading: if you don’t use a module, it will not be loaded. Some modules are used directly through include / extend, others add class methods for activation and configuration. For the second, the gem has Rails :: Engine, which automatically loads them into the base classes of models and controllers (ActiveRecord :: Base and ActionController :: Base). You can select individual modules or disable automatic loading completely by adding initializer:
')
#    : RailsStuff.load_modules = [] #    : RailsStuff.load_modules = %i(sort_scope statusable) 

Heme has no dependencies, but for some modules (or even individual methods) you will need to install something else. For such modules, dependencies are listed in the documentation.

ResourcesController


This is one of the core modules in RailsStuff. In fact, it is a simplified and updated version of InheritedResources.

InheritedResources showed me a great approach to writing controllers. He implemented many of the functions that I was looking for or did myself, for example:


InheritedResources allows you to avoid these problems and focus on the business logic of the application. Ho the library is too overloaded, and quite often you have to look for suitable combinations of settings or go into the source code for the name of the method that needs to be overridden, or to look at exactly how something is implemented. In addition, the library has been deprecated for about a year. Therefore, it was decided to start new projects without using InheritedResources.

So, after using our modules in several new applications, we selected them in ResourcesController, which is the main part of RailsStuff. This is a lightweight version of InheritedResources, providing basic capabilities, and lacks some complex features. But this allows us to make the code simpler and clearer both in the library and in the applications. All methods in the library as simply as possible perform individual functions. The basic principle is not to provide a lot of settings for all possible cases, but to provide a simple, extensible basic functionality. If special behavior is required, simply write the method as you would have done without the ResourcesController.

Here are the main differences:


Examples of controllers from specs RailsStuff
 module Site class UsersController < SiteController resources_controller permit_attrs :name, :email end class ProjectsController < SiteController resources_controller sti: true resource_helper :user permit_attrs :name permit_attrs_for Project::External, :company permit_attrs_for Project::Internal, :department def create super(action: :index) end protected def after_save_url url_for action: :index, user_id: resource.user_id end def source_relation params.key?(:user_id) ? user.projects : self.class.resource_class end end end 


Type tracker


Often, when using STI models in the interface, it is necessary to list all possible types. In such situations, lazy loading gets in the way, and you need to explicitly call require_dependency/eager_load for all inheriting models. Yes, and DescendantsTracker for getting a list of all inherited classes in this case is not the best solution. For such tasks we use TypesTracker. It contains a helper for loading all types for the model and stores a separate array with all the inherited classes: the list is ready and available at any time. In this case, you can selectively remove some classes from this list.

 class Project extend RailsStuff::TypesTracker # ... eager_load_types! #   .rb  app/models/project #    : eager_load_types! 'lib/path/to/projects' end class Project::Big < Project unregister_type #      end class Project::Internal < Project::Big; end class Project::External < Project::Big; end class Project::Small < Project; end Project.types_list # [Internal, External, Small] 

If the array is replaced with a custom storage, then you can set flags for each class and filter / sort them by output:

 class Project extend RailsStuff::TypesTracker # MyTaggedArray   #add(klass, *args). #   *args -  *tags. self.types_list_class = MyTaggedArray end class Project::Internal < Project::Big #  types_list.add Project::Internal, :tag_1 register_type :tag_1 end class Project::External < Project::Big register_type :tag_2 end Project.types_list.with(:tag_1) 

Random Unique Value Generator


Sometimes it is required for the created record to generate a unique value. Theoretically, the UUID provides such an opportunity, but sometimes the value must be generated in accordance with the pattern, and collisions are possible. RandomUniqAttr uses database restrictions and automatically regenerates the value if the entry fails. This allows the use of such a generator even when collisions are quite likely. Here is how it works:

  1. The recording is saved as usual.
  2. If the field already matters, then nothing happens.
  3. Generates a value and tries to save a record.
  4. If a RecordNotUnique error RecordNotUnique , repeat the previous step.

This approach has a condition: the field must be declared nullable. Despite this, the field will not have NULL after the completion of the transaction.

 # -  SecureRandom.hex(32) random_uniq_attr :token #   : random_uniq_attr(:code) { |instance| my_random(instance) } 

Helper


For multilingual interfaces, RailsStuff has helpers for translating action names and confirmations. These methods cache translations for the duration of the query, and for large lists and tables, translation searches will be performed only once. Before use, add helpers.actions and / or helpers.confirmations translation files:

 ru: helpers: actions: edit:  delete:  confirm: ? confirmations: delete:  ? 

Now in all templates, you can start translating actions in the same way:

 #    : # include RailsStuff::Helpers::Translation = translate_action(:edit) or translate_action(:delete) - collection.each do |resource| tr td= resource.name td= link_to 'x', url_for(resource), method: :delete, data: {confirm: translate_confirmation(:delete)} = translate_confirmation(:purge_all) # : '?' 

In order for the application to contain all references to the main actions in the same way, there are helpers for RailsStuff for them:

 #   . #      rails_stuff/helpers/links include RailsStuff::Helpers::Links ICONS = { destroy: '<span class="glyphicon glyphicon-trash"></span>'.html_safe, edit: '', new: -> { translate_action(:new) }, } def basic_link_icons ICONS end # : link_to_edit([:edit, :scope, resource]) or link_to_edit(edit_path) link_to_edit('url', class: 'text-info') #  url  ,  url_for(action: edit). #    /users/1 link_to_edit #    '/users/1/edit' #     : link_to_destroy or link_to_new 

By default, action text is used as the text for references. For users of Bootstrap or Glyphicon, there is a RailsStuff :: Helpers :: Bootstrap helper that uses icons for these links.

What else can you find?



You can try gem 'rails_stuff', '~> 0.4.0' adding gem 'rails_stuff', '~> 0.4.0' in the Gemfile. More details, documentation in English and source codes are available in the githaba repository .

Source: https://habr.com/ru/post/268359/


All Articles