BEM layout in Ruby on Rails

Introduction


In this article, I would like to talk about the BEM layout technique in rail projects. I have not seen such manuals yet (except, perhaps, this one , but it does not suit much as a guide and I will talk about it later), so I decided to write this article. In addition, I created a gem that will simplify the integration of BEM and rail, I will write about it and how to use it later.

Training


First you need to install the actual rub and rails. I prefer rvm . Follow the instructions for installing rvm, and then install the rails through the command:

gem install rails


Well, or you can install the rails from here if you have Windows.

Then create a new project through the command:

rails new some_cool_project


Next, add the “bem” gem to the Gemfile file, which is located in the root of the newly created project. I also recommend that you add high_voltage and slim-rails gems . The first one allows creating standard pages for the layout designer without any special knowledge of rails and hacks, the second one is an excellent template engine that can significantly speed up layout and also minify the output html.

Now install the necessary files for the bem gem using the command:

rails g bem:install


The config / initializers / bem.rb config is installed, in which you can change the technologies (originally scss and js) and the templates for generating files for each technology lib / bem / templates / scss.tt and lib / bem / templates / js.tt . Suppose we want to use less in our project, then this technology needs to be written in the config / initializers / bem.rb config instead of scss:

BEM.configure do |config|
  config.technologies = [
    { :group => 'stylesheets', :extension => '.less', :name => 'less',
      :css_directive => '@import', :css_prefix => "'", :css_postfix => "';" },
    { :group => 'javascripts', :extension => '.js', :name => 'js' }
  ]
end


I also highly recommend installing the config for spring via the command:

rails g bem:spring


This library will allow you to load the environment in the background, which will quickly generate blocks.

If you installed the config for spring, then run the following command:

spring stop


This command is needed to reboot spring later.

Now you can begin to layout.

Layout


As an example for layout, I chose layout from this article.

Blocks, levels, and manifests are created using the bem create command. This command accepts the following flags as an input (the value of the flags must be specified through a space after them):

  • -b creates or uses a block.
  • -e creates or uses an element.
  • -m creates a modifier.
  • -v modifier value.
  • -l creates or uses a level.
  • -a creates or uses a manifest.
  • -js flag for creating javascripts technology files.
  • -css flag for creating stylesheets technology files.


You can also read information about flags and their use by running the command:

bem help create


The create command will create the same file structure (as here ).

Next, we will create our future level, in which our blocks and manifest will be connected via the command:

spring bem create -l shared -a application


It is possible without spring:

bem create -l shared -a application


But, as I said, spring will keep the environment in the background, which will speed up the execution of subsequent commands.

After executing this command, you will see messages in the terminal that levels and manifests have been created for each of the technologies used. In this case, shared is the level at which our blocks will be located, and application is the manifest.

If you look into the manifest file, you will see that the shared level is connected to it by a string
@import 'shared/shared';

A simple rule applies - levels are connected in manifests, blocks are connected in levels.

We delete the previous app / assets / application.css manifest, since we will now use the app / assets / application.less manifest.

Next, we will create the blocks | elements | modifiers we need through similar commands:

spring bem create -l shared -b clear --no-js
spring bem create -l shared -b page --no-js
spring bem create -l shared -b page -e head-line --no-js
spring bem create -l shared -b page -e line --no-js
spring bem create -l shared -b link -m menu -v active --no-js
...


As you can see, I indicate the shared level in which the blocks will be located and also the --no-js flag, since js files will not be used for these blocks.

After these commands are executed, templates will be created in which the css class we need is already registered and all you need to do is fill it with the appropriate properties.

If you made a mistake and wrote the wrong class or included the block in the wrong level / manifest, you can always roll back by performing the reverse command to delete the block | element | modifier | level through the bem destroy command. It accepts all the same flags except --js and --css.

Next, when we create the blocks | elements | modifiers we need, we go to the html layout. If you installed the high_voltage gem, which I already wrote about, then just creating a folder is enough:

mkdir app/views/pages


In which will be located the views we need. If you read the documentation for this gem, then you probably liked the ease of connecting and displaying the view. For example, you need layout for the welcome page. We create the app / views / pages / welcome.html.slim view and set the html we need there. After starting the web server through the command

rails s


You can see the result at http: // localhost: 3000 / pages / welcome. We

put all the images in the app / assets / images folder and replace all the tags with image_tag rail helpers. The addresses of pictures in less files are replaced with image-url (the helper that the less-rails gem provides).

Unfortunately, the above example does not use js. Nevertheless, the templates that are provided for it make it easy to write the desired function and run it:

function your_function_name_initializer() {
// some code
}
$(function() {
  your_function_name_initializer();
});
$(window).bind('page:load', function() {
  your_function_name_initializer();
})


Why is that? The fact is that starting with version 4 of the rail, the turbolinks gem is used, which significantly speeds up the display of pages, but adds some features to js initialization, so you have to design these initializations as functions and then call them with these two events.

The final version of the transferred layout and the rail project itself can be viewed on the github .

Alternatives


There is also a bem-on-rails gem , about an article about which I spoke at the beginning. I already wanted to use it, but it has many disadvantages at the moment that forced me to write my gem. Among the minuses are the inability to work normally with levels and manifests (I still could not create two manifests with my levels and scatter blocks on them), poorly readable code (somewhere 4 spaces as a tab, somewhere two) and generally poor quality code (there is not a single test, the architecture is ugly, it feels like the gem was done in a hurry and the code was just thrown in and I generally couldn’t run it on rails of version 4.1 - I had to send the patch ), poor documentation, and also, if you use the view helpers of this gem then ozhet receive such example (a simple footer):

= b 'footer', content: [{ elem: 'el', elemMods: ['left'], content: ['ОАО «фирма»'] },
   { elem: 'el', elemMods: ['center'], content: [{ elem: 'nav-link', tag: 'a', content: ['ссылка'] },
   { elem: 'nav-link', tag: 'a', content: ['ещё сслыка']},
   { elem: 'nav-link', tag: 'a', content: ['и ещё ссылка'] }] },
   { elem: 'el', elemMods: ['right'], 
   content: ['такие дела'.html_safe] }]


Which obviously does not benefit readability and can confuse the layout designer.

Nevertheless, I believe that in general this is a good attempt to adapt bem-tools to the rails.

There are some more exotic options for embedding nodes in rails to use native bem-tools through grunt, such as half-pipe . But I considered this option to be very cumbersome and difficult, so I did not consider it seriously.

And yet, you can, of course, use bem-tools themselves in some separate repository, but the process of layout and transfer will be very complicated in this case - you will need to write some (bash) script to automatically transfer and replace urls, for example, constantly chasing layout hither and thither. Well, that's okay. But the worst part is switching from branch to branch in these two repositories - before running the transfer script, you need to make sure that we use the correct branch in the repository with rails and in the repository with layout, and then also watch when which branch is merged and merged her in another repository too. Therefore, the solution is not the best.

Conclusion


So, it will be enough for the layout designer to simply rivet the views and blocks with css | js without necessarily knowing what's going on inside. And the backend developer will insert the necessary logic. That is, the layout process is greatly simplified.

The bem library (gem) I wrote is completely open and uses the most affordable MIT license. I wrote the library just recently and I will be glad to receive your feedback and suggestions, as well as pull-quests for github.

I wrote quite briefly about everything, for each item you can write a lot of explanations and arguments, so if you have any questions, I will be happy to answer them in the comments.

References


https://github.com/gkopylov/bem
https://github.com/gkopylov/bem_with_rails_and_less_example_app

P.S.

At the time of writing, the bem gem version 1.0.0 was used. At the moment, the version of this gem is 1.1.0 - it adds the ability to connect blocks | elements | modifiers directly to the manifest. This is done so that the layout designer can change the order of connecting styles directly in the manifest, instead of connecting levels there.

To use this option, just add the -i flag to the generation command. Thus, the created styles will be connected immediately to the manifest without creating styles for the level.

I wanted to write a little more about the use of import and require directives. Both directives have their pros and cons. Among the benefits of import:
- you can connect mixins and variables
- everything merges into one file and is quickly given to the browser, instead of the many files that require generates (but this is also a minus, see below)
among the minuses:
- it is difficult to debug css - if a preprocessor error occurs, it will be difficult to find the place of this error
is that the styles are not updated when changing the attached css files (in order to update the styles, run the command rm -rf tmp / assets / * and restart the rails)
Among the pluses of require:
is easier to debug, as it compiles a separate one for each require file (in the development environment)
- in possibility livereload injection (theoretically make it easier than imports)
among the minuses:
- the inability to use the hagfish or variables
- compiled style files may be given to the browser for a long time if there are a lot of 'require' directives.

So the decision to use this or that directive needs to be taken deliberately and depending on the project.

I wanted to write a little more why, by default, I decided to use scss. The fact is that in the sass rail community the preprocessor is more popular and even by default when creating a new application a gem with this preprocessor is turned on. And there (as a matter of fact, faost correctly noticed and in less http://lesscss.org/features/#parent-selectors-feature ) there is an excellent feature that is just suitable for BEM - this is the use of ampersand. Already many in the BEM community are moving away from writing a separate file for the modifier (if it is small) and writing the modifier directly in the file with the block, and with this sass ampersand you can just do such things directly in the styles for the block.

Also popular now: